Autoplay next recommended video (#2137)
* Start working on autoplay of next video * Ignore changes made by gitpod * Apply changes from PR#1370 * Correct the spelling of recommendations * Fix linting errors * Move boolean check to existing onEnded handler * Pick a random video until the recommendations are improved * Add simple tests for autoPlayNextVideo * Fix lint ...again
This commit is contained in:
parent
32d7f2b754
commit
6aa5414813
|
@ -37,6 +37,8 @@
|
|||
/scripts/i18n/generate-iso639-target.ts
|
||||
|
||||
# Other
|
||||
/dump.rdb
|
||||
/.theia/
|
||||
/profiling/
|
||||
/*.zip
|
||||
/*.tar.xz
|
||||
|
|
|
@ -47,6 +47,11 @@
|
|||
inputName="autoPlayVideo" formControlName="autoPlayVideo"
|
||||
i18n-labelText labelText="Automatically plays video"
|
||||
></my-peertube-checkbox>
|
||||
|
||||
<my-peertube-checkbox
|
||||
inputName="autoPlayNextVideo" formControlName="autoPlayNextVideo"
|
||||
i18n-labelText labelText="Automatically starts playing next video"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<input type="submit" i18n-value value="Save" [disabled]="!form.valid">
|
||||
|
|
|
@ -36,6 +36,7 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
|||
nsfwPolicy: null,
|
||||
webTorrentEnabled: null,
|
||||
autoPlayVideo: null,
|
||||
autoPlayNextVideo: null,
|
||||
videoLanguages: null
|
||||
})
|
||||
|
||||
|
@ -57,6 +58,7 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
|||
nsfwPolicy: this.user.nsfwPolicy,
|
||||
webTorrentEnabled: this.user.webTorrentEnabled,
|
||||
autoPlayVideo: this.user.autoPlayVideo === true,
|
||||
autoPlayNextVideo: this.user.autoPlayNextVideo,
|
||||
videoLanguages
|
||||
})
|
||||
})
|
||||
|
@ -66,6 +68,7 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
|||
const nsfwPolicy = this.form.value[ 'nsfwPolicy' ]
|
||||
const webTorrentEnabled = this.form.value['webTorrentEnabled']
|
||||
const autoPlayVideo = this.form.value['autoPlayVideo']
|
||||
const autoPlayNextVideo = this.form.value['autoPlayNextVideo']
|
||||
|
||||
let videoLanguages: string[] = this.form.value['videoLanguages']
|
||||
if (Array.isArray(videoLanguages)) {
|
||||
|
@ -84,6 +87,7 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
|||
nsfwPolicy,
|
||||
webTorrentEnabled,
|
||||
autoPlayVideo,
|
||||
autoPlayNextVideo,
|
||||
videoLanguages
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ export class User implements UserServerModel {
|
|||
adminFlags?: UserAdminFlag
|
||||
|
||||
autoPlayVideo: boolean
|
||||
autoPlayNextVideo: boolean
|
||||
webTorrentEnabled: boolean
|
||||
videosHistoryEnabled: boolean
|
||||
videoLanguages: string[]
|
||||
|
|
|
@ -199,7 +199,11 @@
|
|||
<my-video-comments [video]="video" [user]="user"></my-video-comments>
|
||||
</div>
|
||||
|
||||
<my-recommended-videos [inputRecommendation]="{ uuid: video.uuid, tags: video.tags }" [user]="user"></my-recommended-videos>
|
||||
<my-recommended-videos
|
||||
[inputRecommendation]="{ uuid: video.uuid, tags: video.tags }"
|
||||
[user]="user"
|
||||
(gotRecommendations)="onRecommendations($event)"
|
||||
></my-recommended-videos>
|
||||
</div>
|
||||
|
||||
<div class="row privacy-concerns" *ngIf="hasAlreadyAcceptedPrivacyConcern === false">
|
||||
|
|
|
@ -35,6 +35,7 @@ import { getStoredTheater } from '../../../assets/player/peertube-player-local-s
|
|||
import { PluginService } from '@app/core/plugins/plugin.service'
|
||||
import { HooksService } from '@app/core/plugins/hooks.service'
|
||||
import { PlatformLocation } from '@angular/common'
|
||||
import { randomInt } from '@shared/core-utils/miscs/miscs'
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-watch',
|
||||
|
@ -69,6 +70,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
|||
remoteServerDown = false
|
||||
hotkeys: Hotkey[]
|
||||
|
||||
private nextVideoUuid = ''
|
||||
private currentTime: number
|
||||
private paramsSub: Subscription
|
||||
private queryParamsSub: Subscription
|
||||
|
@ -217,6 +219,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
|||
return this.video.tags
|
||||
}
|
||||
|
||||
onRecommendations (videos: Video[]) {
|
||||
if (videos.length > 0) {
|
||||
// Pick a random video until the recommendations are improved
|
||||
this.nextVideoUuid = videos[randomInt(0,videos.length - 1)].uuid
|
||||
}
|
||||
}
|
||||
|
||||
onVideoRemoved () {
|
||||
this.redirectService.redirectToHomepage()
|
||||
}
|
||||
|
@ -477,6 +486,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
|||
this.player.one('ended', () => {
|
||||
if (this.playlist) {
|
||||
this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
|
||||
} else if (this.user && this.user.autoPlayNextVideo) {
|
||||
this.zone.run(() => this.autoplayNext())
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -500,6 +511,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
|||
this.hooks.runAction('action:video-watch.video.loaded', 'video-watch')
|
||||
}
|
||||
|
||||
private autoplayNext () {
|
||||
if (this.nextVideoUuid) {
|
||||
this.router.navigate([ '/videos/watch', this.nextVideoUuid ])
|
||||
}
|
||||
}
|
||||
|
||||
private setRating (nextRating: UserVideoRateType) {
|
||||
const ratingMethods: { [id in UserVideoRateType]: (id: number) => Observable<any> } = {
|
||||
like: this.videoService.setVideoLike,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, Input, OnChanges } from '@angular/core'
|
||||
import { Component, Input, Output, OnChanges, EventEmitter } from '@angular/core'
|
||||
import { Observable } from 'rxjs'
|
||||
import { Video } from '@app/shared/video/video.model'
|
||||
import { RecommendationInfo } from '@app/shared/video/recommendation-info.model'
|
||||
|
@ -12,6 +12,7 @@ import { User } from '@app/shared'
|
|||
export class RecommendedVideosComponent implements OnChanges {
|
||||
@Input() inputRecommendation: RecommendationInfo
|
||||
@Input() user: User
|
||||
@Output() gotRecommendations = new EventEmitter<Video[]>()
|
||||
|
||||
readonly hasVideos$: Observable<boolean>
|
||||
readonly videos$: Observable<Video[]>
|
||||
|
@ -21,6 +22,7 @@ export class RecommendedVideosComponent implements OnChanges {
|
|||
) {
|
||||
this.videos$ = this.store.recommendations$
|
||||
this.hasVideos$ = this.store.hasRecommendations$
|
||||
this.videos$.subscribe(videos => this.gotRecommendations.emit(videos))
|
||||
}
|
||||
|
||||
public ngOnChanges (): void {
|
||||
|
|
|
@ -175,6 +175,7 @@ async function updateMe (req: express.Request, res: express.Response) {
|
|||
if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy
|
||||
if (body.webTorrentEnabled !== undefined) user.webTorrentEnabled = body.webTorrentEnabled
|
||||
if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo
|
||||
if (body.autoPlayNextVideo !== undefined) user.autoPlayNextVideo = body.autoPlayNextVideo
|
||||
if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled
|
||||
if (body.videoLanguages !== undefined) user.videoLanguages = body.videoLanguages
|
||||
if (body.theme !== undefined) user.theme = body.theme
|
||||
|
|
|
@ -65,6 +65,10 @@ function isUserBlockedValid (value: any) {
|
|||
return isBooleanValid(value)
|
||||
}
|
||||
|
||||
function isUserAutoPlayNextVideoValid (value: any) {
|
||||
return isBooleanValid(value)
|
||||
}
|
||||
|
||||
function isNoInstanceConfigWarningModal (value: any) {
|
||||
return isBooleanValid(value)
|
||||
}
|
||||
|
@ -106,6 +110,7 @@ export {
|
|||
isUserNSFWPolicyValid,
|
||||
isUserWebTorrentEnabledValid,
|
||||
isUserAutoPlayVideoValid,
|
||||
isUserAutoPlayNextVideoValid,
|
||||
isUserDisplayNameValid,
|
||||
isUserDescriptionValid,
|
||||
isNoInstanceConfigWarningModal,
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
isNoInstanceConfigWarningModal,
|
||||
isUserAdminFlagsValid,
|
||||
isUserAutoPlayVideoValid,
|
||||
isUserAutoPlayNextVideoValid,
|
||||
isUserBlockedReasonValid,
|
||||
isUserBlockedValid,
|
||||
isUserEmailVerifiedValid,
|
||||
|
@ -160,6 +161,12 @@ export class UserModel extends Model<UserModel> {
|
|||
@Column
|
||||
autoPlayVideo: boolean
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(false)
|
||||
@Is('UserAutoPlayNextVideo', value => throwIfNotValid(value, isUserAutoPlayNextVideoValid, 'auto play next video boolean'))
|
||||
@Column
|
||||
autoPlayNextVideo: boolean
|
||||
|
||||
@AllowNull(true)
|
||||
@Default(null)
|
||||
@Is('UserVideoLanguages', value => throwIfNotValid(value, isUserVideoLanguages, 'video languages'))
|
||||
|
@ -597,6 +604,7 @@ export class UserModel extends Model<UserModel> {
|
|||
webTorrentEnabled: this.webTorrentEnabled,
|
||||
videosHistoryEnabled: this.videosHistoryEnabled,
|
||||
autoPlayVideo: this.autoPlayVideo,
|
||||
autoPlayNextVideo: this.autoPlayNextVideo,
|
||||
videoLanguages: this.videoLanguages,
|
||||
|
||||
role: this.role,
|
||||
|
|
|
@ -418,6 +418,14 @@ describe('Test users API validators', function () {
|
|||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail with an invalid autoPlayNextVideo attribute', async function () {
|
||||
const fields = {
|
||||
autoPlayNextVideo: -1
|
||||
}
|
||||
|
||||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail with an invalid videosHistoryEnabled attribute', async function () {
|
||||
const fields = {
|
||||
videosHistoryEnabled: -1
|
||||
|
|
|
@ -481,6 +481,19 @@ describe('Test users', function () {
|
|||
expect(user.autoPlayVideo).to.be.false
|
||||
})
|
||||
|
||||
it('Should be able to change the autoPlayNextVideo attribute', async function () {
|
||||
await updateMyUser({
|
||||
url: server.url,
|
||||
accessToken: accessTokenUser,
|
||||
autoPlayNextVideo: true
|
||||
})
|
||||
|
||||
const res = await getMyUserInformation(server.url, accessTokenUser)
|
||||
const user = res.body
|
||||
|
||||
expect(user.autoPlayNextVideo).to.be.true
|
||||
})
|
||||
|
||||
it('Should be able to change the email attribute', async function () {
|
||||
await updateMyUser({
|
||||
url: server.url,
|
||||
|
|
|
@ -7,6 +7,7 @@ export interface UserUpdateMe {
|
|||
|
||||
webTorrentEnabled?: boolean
|
||||
autoPlayVideo?: boolean
|
||||
autoPlayNextVideo?: boolean
|
||||
videosHistoryEnabled?: boolean
|
||||
videoLanguages?: string[]
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ export interface User {
|
|||
adminFlags?: UserAdminFlag
|
||||
|
||||
autoPlayVideo: boolean
|
||||
autoPlayNextVideo: boolean
|
||||
webTorrentEnabled: boolean
|
||||
videosHistoryEnabled: boolean
|
||||
videoLanguages: string[]
|
||||
|
|
Loading…
Reference in New Issue