autoplay next video support for playlists

pull/2157/head
Rigel Kent 2019-12-11 20:20:42 +01:00 committed by Chocobozzz
parent d816f3a063
commit bee29df8a9
18 changed files with 144 additions and 18 deletions

View File

@ -1,4 +1,6 @@
::ng-deep svg {
width: inherit;
height: inherit;
::ng-deep {
svg {
width: inherit;
height: inherit;
}
}

View File

@ -17,6 +17,7 @@ export class User implements UserServerModel {
autoPlayVideo: boolean
autoPlayNextVideo: boolean
autoPlayNextVideoPlaylist: boolean
webTorrentEnabled: boolean
videosHistoryEnabled: boolean
videoLanguages: string[]

View File

@ -72,10 +72,6 @@ my-video-thumbnail,
a {
width: auto;
&:hover {
text-decoration: underline !important;
}
}
.video-info-account, .video-info-timestamp {

View File

@ -14,6 +14,17 @@
<span>{{ currentPlaylistPosition }}</span><span>{{ playlistPagination.totalItems }}</span>
</div>
</div>
<div class="playlist-controls">
<my-global-icon
iconName="videos"
[class.active]="autoPlayNextVideoPlaylist"
(click)="switchAutoPlayNextVideoPlaylist()"
[ngbTooltip]="autoPlayNextVideoPlaylistSwitchText"
placement="bottom auto"
container="body"
></my-global-icon>
</div>
</div>
<div *ngFor="let playlistElement of playlistElements">

View File

@ -34,6 +34,21 @@
margin: 0 3px;
}
}
.playlist-controls {
display: flex;
margin: 10px 0;
my-global-icon {
&:not(.active) {
opacity: .5
}
::ng-deep {
cursor: pointer;
}
}
}
}
my-video-playlist-element-miniature {

View File

@ -3,9 +3,12 @@ import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models'
import { Router } from '@angular/router'
import { AuthService } from '@app/core'
import { User, UserService } from '@app/shared'
import { AuthService, Notifier } from '@app/core'
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-element.model'
import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({
selector: 'my-video-watch-playlist',
@ -13,6 +16,8 @@ import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-
styleUrls: [ './video-watch-playlist.component.scss' ]
})
export class VideoWatchPlaylistComponent {
static LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST = 'auto_play_video_playlist'
@Input() video: VideoDetails
@Input() playlist: VideoPlaylist
@ -23,14 +28,24 @@ export class VideoWatchPlaylistComponent {
totalItems: null
}
autoPlayNextVideoPlaylist: boolean
autoPlayNextVideoPlaylistSwitchText = ''
noPlaylistVideos = false
currentPlaylistPosition = 1
constructor (
private userService: UserService,
private auth: AuthService,
private notifier: Notifier,
private i18n: I18n,
private videoPlaylist: VideoPlaylistService,
private router: Router
) {}
) {
this.autoPlayNextVideoPlaylist = this.auth.isLoggedIn()
? this.auth.getUser().autoPlayNextVideoPlaylist
: peertubeLocalStorage.getItem(VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) !== 'false'
this.setAutoPlayNextVideoPlaylistSwitchText()
}
onPlaylistVideosNearOfBottom () {
// Last page
@ -121,4 +136,33 @@ export class VideoWatchPlaylistComponent {
this.router.navigate([],{ queryParams: { videoId: next.video.uuid, start, stop } })
}
}
switchAutoPlayNextVideoPlaylist () {
this.autoPlayNextVideoPlaylist = !this.autoPlayNextVideoPlaylist
this.setAutoPlayNextVideoPlaylistSwitchText()
peertubeLocalStorage.setItem(
VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST,
this.autoPlayNextVideoPlaylist.toString()
)
if (this.auth.isLoggedIn()) {
const details = {
autoPlayNextVideoPlaylist: this.autoPlayNextVideoPlaylist
}
this.userService.updateMyProfile(details).subscribe(
() => {
this.auth.refreshUserInformation()
},
err => this.notifier.error(err.message)
)
}
}
private setAutoPlayNextVideoPlaylistSwitchText () {
this.autoPlayNextVideoPlaylistSwitchText = this.i18n('{{verb}} autoplay for playlists', {
verb: this.autoPlayNextVideoPlaylist ? this.i18n('Disable') : this.i18n('Enable')
})
}
}

View File

@ -217,6 +217,7 @@
<my-recommended-videos
[inputRecommendation]="{ uuid: video.uuid, tags: video.tags }"
[user]="user"
[playlist]="playlist"
(gotRecommendations)="onRecommendations($event)"
></my-recommended-videos>
</div>

View File

@ -37,6 +37,7 @@ 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'
import { RecommendedVideosComponent } from '../recommendations/recommended-videos.component'
@Component({
selector: 'my-video-watch',
@ -436,10 +437,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.player.one('ended', () => {
if (this.playlist) {
this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
if (
this.user && this.user.autoPlayNextVideoPlaylist ||
peertubeLocalStorage.getItem(VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true'
) this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
} else if (
this.user && this.user.autoPlayNextVideo ||
peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
peertubeLocalStorage.getItem(RecommendedVideosComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
) {
this.zone.run(() => this.autoplayNext())
}
@ -447,7 +451,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.player.one('stopped', () => {
if (this.playlist) {
this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
if (
this.user && this.user.autoPlayNextVideoPlaylist ||
peertubeLocalStorage.getItem(VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true'
) this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
}
})

View File

@ -12,6 +12,7 @@ import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
import { RecommendationsModule } from '@app/videos/recommendations/recommendations.module'
import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component'
import { QRCodeModule } from 'angularx-qrcode'
import { InputSwitchModule } from 'primeng/inputswitch'
@NgModule({
imports: [
@ -19,7 +20,8 @@ import { QRCodeModule } from 'angularx-qrcode'
SharedModule,
NgbTooltipModule,
QRCodeModule,
RecommendationsModule
RecommendationsModule,
InputSwitchModule
],
declarations: [

View File

@ -4,15 +4,17 @@
<div i18n class="title-page title-page-single">
Other videos
</div>
<div class="d-flex title-page-autoplay">
<span>Autoplay</span>
<div *ngIf="!playlist" class="d-flex title-page-autoplay">
<span i18n>Autoplay</span>
<p-inputSwitch [(ngModel)]="autoPlayNextVideo" (ngModelChange)="switchAutoPlayNextVideo()"></p-inputSwitch>
</div>
</div>
<div *ngFor="let video of (videos$ | async)">
<div *ngFor="let video of (videos$ | async); let i = index; let length = count">
<my-video-miniature [video]="video" [user]="user" (videoBlacklisted)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()">
</my-video-miniature>
<hr *ngIf="!playlist && i == 0 && length > 1" />
</div>
</ng-container>
</div>

View File

@ -1,6 +1,7 @@
import { Component, Input, Output, OnChanges, EventEmitter } from '@angular/core'
import { Observable } from 'rxjs'
import { Video } from '@app/shared/video/video.model'
import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
import { RecommendationInfo } from '@app/shared/video/recommendation-info.model'
import { RecommendedVideosStore } from '@app/videos/recommendations/recommended-videos.store'
import { User } from '@app/shared'
@ -14,10 +15,11 @@ import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
styleUrls: [ './recommended-videos.component.scss' ]
})
export class RecommendedVideosComponent implements OnChanges {
private static LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO = 'auto_play_next_video'
static LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO = 'auto_play_next_video'
@Input() inputRecommendation: RecommendationInfo
@Input() user: User
@Input() playlist: VideoPlaylist
@Output() gotRecommendations = new EventEmitter<Video[]>()
readonly hasVideos$: Observable<boolean>

View File

@ -176,6 +176,7 @@ async function updateMe (req: express.Request, res: express.Response) {
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.autoPlayNextVideoPlaylist !== undefined) user.autoPlayNextVideoPlaylist = body.autoPlayNextVideoPlaylist
if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled
if (body.videoLanguages !== undefined) user.videoLanguages = body.videoLanguages
if (body.theme !== undefined) user.theme = body.theme

View File

@ -69,6 +69,10 @@ function isUserAutoPlayNextVideoValid (value: any) {
return isBooleanValid(value)
}
function isUserAutoPlayNextVideoPlaylistValid (value: any) {
return isBooleanValid(value)
}
function isNoInstanceConfigWarningModal (value: any) {
return isBooleanValid(value)
}
@ -111,6 +115,7 @@ export {
isUserWebTorrentEnabledValid,
isUserAutoPlayVideoValid,
isUserAutoPlayNextVideoValid,
isUserAutoPlayNextVideoPlaylistValid,
isUserDisplayNameValid,
isUserDescriptionValid,
isNoInstanceConfigWarningModal,

View File

@ -14,7 +14,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
// ---------------------------------------------------------------------------
const LAST_MIGRATION_VERSION = 455
const LAST_MIGRATION_VERSION = 460
// ---------------------------------------------------------------------------

View File

@ -0,0 +1,27 @@
import * as Sequelize from 'sequelize'
async function up (utils: {
transaction: Sequelize.Transaction,
queryInterface: Sequelize.QueryInterface,
sequelize: Sequelize.Sequelize,
db: any
}): Promise<void> {
{
const data = {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true
}
await utils.queryInterface.addColumn('user', 'autoPlayNextVideoPlaylist', data)
}
}
function down (options) {
throw new Error('Not implemented.')
}
export {
up,
down
}

View File

@ -26,6 +26,7 @@ import {
isUserAdminFlagsValid,
isUserAutoPlayVideoValid,
isUserAutoPlayNextVideoValid,
isUserAutoPlayNextVideoPlaylistValid,
isUserBlockedReasonValid,
isUserBlockedValid,
isUserEmailVerifiedValid,
@ -167,6 +168,12 @@ export class UserModel extends Model<UserModel> {
@Column
autoPlayNextVideo: boolean
@AllowNull(false)
@Default(true)
@Is('UserAutoPlayNextVideoPlaylist', value => throwIfNotValid(value, isUserAutoPlayNextVideoPlaylistValid, 'auto play next video for playlists boolean'))
@Column
autoPlayNextVideoPlaylist: boolean
@AllowNull(true)
@Default(null)
@Is('UserVideoLanguages', value => throwIfNotValid(value, isUserVideoLanguages, 'video languages'))
@ -619,6 +626,7 @@ export class UserModel extends Model<UserModel> {
videosHistoryEnabled: this.videosHistoryEnabled,
autoPlayVideo: this.autoPlayVideo,
autoPlayNextVideo: this.autoPlayNextVideo,
autoPlayNextVideoPlaylist: this.autoPlayNextVideoPlaylist,
videoLanguages: this.videoLanguages,
role: this.role,

View File

@ -8,6 +8,7 @@ export interface UserUpdateMe {
webTorrentEnabled?: boolean
autoPlayVideo?: boolean
autoPlayNextVideo?: boolean
autoPlayNextVideoPlaylist?: boolean
videosHistoryEnabled?: boolean
videoLanguages?: string[]

View File

@ -18,6 +18,7 @@ export interface User {
autoPlayVideo: boolean
autoPlayNextVideo: boolean
autoPlayNextVideoPlaylist: boolean
webTorrentEnabled: boolean
videosHistoryEnabled: boolean
videoLanguages: string[]