From bfbd912886eba17b4aa9a40dcef2fddc685d85bf Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 31 Jul 2019 15:57:32 +0200 Subject: Fix broken playlist api --- .../account-video-channels.component.html | 7 +- .../account-video-channels.component.scss | 5 ++ ...-account-video-playlist-elements.component.html | 6 +- ...my-account-video-playlist-elements.component.ts | 66 ++++---------- .../video-add-to-playlist.component.ts | 8 +- ...video-playlist-element-miniature.component.html | 100 ++++++++++++--------- ...video-playlist-element-miniature.component.scss | 32 ++++--- .../video-playlist-element-miniature.component.ts | 61 ++++++++----- .../video-playlist/video-playlist-element.model.ts | 24 +++++ .../video-playlist/video-playlist.service.ts | 46 +++++++++- client/src/app/shared/video/video.model.ts | 6 +- client/src/app/shared/video/video.service.ts | 18 ---- .../video-watch-playlist.component.html | 6 +- .../video-watch-playlist.component.scss | 5 ++ .../+video-watch/video-watch-playlist.component.ts | 43 +++++---- .../videos/+video-watch/video-watch.component.ts | 2 +- .../src/assets/player/peertube-player-manager.ts | 6 +- .../videojs-components/settings-menu-item.ts | 3 + client/src/standalone/videos/embed.ts | 2 +- 19 files changed, 263 insertions(+), 183 deletions(-) create mode 100644 client/src/app/shared/video-playlist/video-playlist-element.model.ts (limited to 'client/src') diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html index cb23bb522..ea5f61b18 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.html +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.html @@ -18,10 +18,13 @@
This channel does not have videos.
- +
- + Show this channel diff --git a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss index 98931f0c2..7f7652460 100644 --- a/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss +++ b/client/src/app/+accounts/account-video-channels/account-video-channels.component.scss @@ -23,6 +23,11 @@ height: 50px; } } + + my-video-miniature ::ng-deep my-video-actions-dropdown > my-action-dropdown { + // Fix our overflow + position: absolute; + } } diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html index 284694b7f..4de4e69da 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.html @@ -14,10 +14,10 @@ class="videos" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" cdkDropList (cdkDropListDropped)="drop($event)" > -
+
diff --git a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts index d5122aeba..6434b9e50 100644 --- a/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts +++ b/client/src/app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component.ts @@ -3,15 +3,13 @@ import { Notifier, ServerService } from '@app/core' import { AuthService } from '../../core/auth' import { ConfirmService } from '../../core/confirm' import { ComponentPagination } from '@app/shared/rest/component-pagination.model' -import { Video } from '@app/shared/video/video.model' -import { Subject, Subscription } from 'rxjs' +import { Subscription } from 'rxjs' import { ActivatedRoute } from '@angular/router' -import { VideoService } from '@app/shared/video/video.service' import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' import { I18n } from '@ngx-translate/i18n-polyfill' -import { CdkDragDrop, CdkDragMove } from '@angular/cdk/drag-drop' -import { throttleTime } from 'rxjs/operators' +import { CdkDragDrop } from '@angular/cdk/drag-drop' +import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-element.model' @Component({ selector: 'my-account-video-playlist-elements', @@ -19,7 +17,7 @@ import { throttleTime } from 'rxjs/operators' styleUrls: [ './my-account-video-playlist-elements.component.scss' ] }) export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestroy { - videos: Video[] = [] + playlistElements: VideoPlaylistElement[] = [] playlist: VideoPlaylist pagination: ComponentPagination = { @@ -30,7 +28,6 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro private videoPlaylistId: string | number private paramsSub: Subscription - private dragMoveSubject = new Subject() constructor ( private authService: AuthService, @@ -39,7 +36,6 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro private confirmService: ConfirmService, private route: ActivatedRoute, private i18n: I18n, - private videoService: VideoService, private videoPlaylistService: VideoPlaylistService ) {} @@ -50,10 +46,6 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro this.loadPlaylistInfo() }) - - this.dragMoveSubject.asObservable() - .pipe(throttleTime(200)) - .subscribe(y => this.checkScroll(y)) } ngOnDestroy () { @@ -66,8 +58,8 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro if (previousIndex === newIndex) return - const oldPosition = this.videos[previousIndex].playlistElement.position - let insertAfter = this.videos[newIndex].playlistElement.position + const oldPosition = this.playlistElements[previousIndex].position + let insertAfter = this.playlistElements[newIndex].position if (oldPosition > insertAfter) insertAfter-- @@ -78,42 +70,16 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro err => this.notifier.error(err.message) ) - const video = this.videos[previousIndex] + const element = this.playlistElements[previousIndex] - this.videos.splice(previousIndex, 1) - this.videos.splice(newIndex, 0, video) + this.playlistElements.splice(previousIndex, 1) + this.playlistElements.splice(newIndex, 0, element) this.reorderClientPositions() } - onDragMove (event: CdkDragMove) { - this.dragMoveSubject.next(event.pointerPosition.y) - } - - checkScroll (pointerY: number) { - // FIXME: Uncomment when https://github.com/angular/material2/issues/14098 is fixed - // FIXME: Remove when https://github.com/angular/material2/issues/13588 is implemented - // if (pointerY < 150) { - // window.scrollBy({ - // left: 0, - // top: -20, - // behavior: 'smooth' - // }) - // - // return - // } - // - // if (window.innerHeight - pointerY <= 50) { - // window.scrollBy({ - // left: 0, - // top: 20, - // behavior: 'smooth' - // }) - // } - } - - onElementRemoved (video: Video) { - this.videos = this.videos.filter(v => v.id !== video.id) + onElementRemoved (element: VideoPlaylistElement) { + this.playlistElements = this.playlistElements.filter(v => v.id !== element.id) this.reorderClientPositions() } @@ -125,14 +91,14 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro this.loadElements() } - trackByFn (index: number, elem: Video) { + trackByFn (index: number, elem: VideoPlaylistElement) { return elem.id } private loadElements () { - this.videoService.getPlaylistVideos(this.videoPlaylistId, this.pagination) + this.videoPlaylistService.getPlaylistVideos(this.videoPlaylistId, this.pagination) .subscribe(({ total, data }) => { - this.videos = this.videos.concat(data) + this.playlistElements = this.playlistElements.concat(data) this.pagination.totalItems = total }) } @@ -147,8 +113,8 @@ export class MyAccountVideoPlaylistElementsComponent implements OnInit, OnDestro private reorderClientPositions () { let i = 1 - for (const video of this.videos) { - video.playlistElement.position = i + for (const element of this.playlistElements) { + element.position = i i++ } } diff --git a/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts index c6cff03a4..08ceb21bc 100644 --- a/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts +++ b/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts @@ -37,6 +37,8 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, } displayOptions = false + private playlistElementId: number + constructor ( protected formValidatorService: FormValidatorService, private authService: AuthService, @@ -96,6 +98,8 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined, stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined }) + + this.playlistElementId = existingPlaylist ? existingPlaylist.playlistElementId : undefined } this.cd.markForCheck() @@ -177,7 +181,9 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, } private removeVideoFromPlaylist (playlist: PlaylistSummary) { - this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, this.video.id) + if (!this.playlistElementId) return + + this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, this.playlistElementId) .subscribe( () => { this.notifier.success(this.i18n('Video removed from {{name}}', { name: playlist.displayName })) diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html index ab5a78928..25d4783fb 100644 --- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html +++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html @@ -6,66 +6,82 @@
+
+
- {{ video.name }} + + {{ playlistElement.video.name }} + + + - - + {{ formatTimestamp(playlistElement) }} + - {{ formatTimestamp(video) }} + + Unavailable + Private + Deleted +
-
+
- + + -
-
- +
+
+ - -
+ +
-
- +
+ - -
+ +
- -
+ +
+
- + Delete from {{ playlist?.displayName }}
diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss index cb7072d7f..9f4061b02 100644 --- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss +++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss @@ -2,9 +2,21 @@ @import '_mixins'; @import '_miniature'; +$thumbnail-width: 130px; +$thumbnail-height: 72px; + my-video-thumbnail { - @include thumbnail-size-component(130px, 72px); + @include thumbnail-size-component($thumbnail-width, $thumbnail-height); +} +.fake-thumbnail { + width: $thumbnail-width; + height: $thumbnail-height; + background-color: #ececec; +} + +my-video-thumbnail, +.fake-thumbnail { display: flex; // Avoids an issue with line-height that adds space below the element margin-right: 10px; } @@ -31,6 +43,7 @@ my-video-thumbnail { a { @include disable-default-a-behaviour; + color: var(--mainForegroundColor); display: flex; min-width: 0; align-items: center; @@ -58,7 +71,6 @@ my-video-thumbnail { min-width: 0; a { - color: var(--mainForegroundColor); width: auto; &:hover { @@ -66,20 +78,20 @@ my-video-thumbnail { } } - .video-info-name { - font-size: 18px; - font-weight: $font-semibold; - display: inline-block; - - @include ellipsis; - } - .video-info-account, .video-info-timestamp { color: $grey-foreground-color; } } } + .video-info-name { + font-size: 18px; + font-weight: $font-semibold; + display: inline-block; + + @include ellipsis; + } + .more { justify-self: flex-end; margin-left: auto; diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts index 62cf6536d..a8e5a4885 100644 --- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts +++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core' import { Video } from '@app/shared/video/video.model' -import { VideoPlaylistElementUpdate } from '@shared/models' +import { VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models' import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core' import { ActivatedRoute } from '@angular/router' import { I18n } from '@ngx-translate/i18n-polyfill' @@ -9,6 +9,7 @@ import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist. import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' import { secondsToTime } from '../../../assets/player/utils' +import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-element.model' @Component({ selector: 'my-video-playlist-element-miniature', @@ -20,14 +21,14 @@ export class VideoPlaylistElementMiniatureComponent { @ViewChild('moreDropdown', { static: false }) moreDropdown: NgbDropdown @Input() playlist: VideoPlaylist - @Input() video: Video + @Input() playlistElement: VideoPlaylistElement @Input() owned = false @Input() playing = false @Input() rowLink = false @Input() accountLink = true - @Input() position: number + @Input() position: number // Keep this property because we're in the OnPush change detection strategy - @Output() elementRemoved = new EventEmitter
-
+
diff --git a/client/src/app/videos/+video-watch/video-watch-playlist.component.scss b/client/src/app/videos/+video-watch/video-watch-playlist.component.scss index eeb763bd9..4c24d6b05 100644 --- a/client/src/app/videos/+video-watch/video-watch-playlist.component.scss +++ b/client/src/app/videos/+video-watch/video-watch-playlist.component.scss @@ -53,6 +53,11 @@ my-video-thumbnail { @include thumbnail-size-component(90px, 50px); } + + .fake-thumbnail { + width: 90px; + height: 50px; + } } } } diff --git a/client/src/app/videos/+video-watch/video-watch-playlist.component.ts b/client/src/app/videos/+video-watch/video-watch-playlist.component.ts index 2fb0cb0e5..6e8d58cd8 100644 --- a/client/src/app/videos/+video-watch/video-watch-playlist.component.ts +++ b/client/src/app/videos/+video-watch/video-watch-playlist.component.ts @@ -1,11 +1,11 @@ import { Component, Input } from '@angular/core' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' import { ComponentPagination } from '@app/shared/rest/component-pagination.model' -import { Video } from '@app/shared/video/video.model' import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models' -import { VideoService } from '@app/shared/video/video.service' import { Router } from '@angular/router' import { AuthService } from '@app/core' +import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' +import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-element.model' @Component({ selector: 'my-video-watch-playlist', @@ -16,7 +16,7 @@ export class VideoWatchPlaylistComponent { @Input() video: VideoDetails @Input() playlist: VideoPlaylist - playlistVideos: Video[] = [] + playlistElements: VideoPlaylistElement[] = [] playlistPagination: ComponentPagination = { currentPage: 1, itemsPerPage: 30, @@ -28,7 +28,7 @@ export class VideoWatchPlaylistComponent { constructor ( private auth: AuthService, - private videoService: VideoService, + private videoPlaylist: VideoPlaylistService, private router: Router ) {} @@ -40,8 +40,8 @@ export class VideoWatchPlaylistComponent { this.loadPlaylistElements(this.playlist,false) } - onElementRemoved (video: Video) { - this.playlistVideos = this.playlistVideos.filter(v => v.id !== video.id) + onElementRemoved (playlistElement: VideoPlaylistElement) { + this.playlistElements = this.playlistElements.filter(e => e.id !== playlistElement.id) this.playlistPagination.totalItems-- } @@ -65,12 +65,13 @@ export class VideoWatchPlaylistComponent { } loadPlaylistElements (playlist: VideoPlaylist, redirectToFirst = false) { - this.videoService.getPlaylistVideos(playlist.uuid, this.playlistPagination) + this.videoPlaylist.getPlaylistVideos(playlist.uuid, this.playlistPagination) .subscribe(({ total, data }) => { - this.playlistVideos = this.playlistVideos.concat(data) + this.playlistElements = this.playlistElements.concat(data) this.playlistPagination.totalItems = total - if (total === 0) { + const firstAvailableVideos = this.playlistElements.find(e => !!e.video) + if (!firstAvailableVideos) { this.noPlaylistVideos = true return } @@ -79,7 +80,7 @@ export class VideoWatchPlaylistComponent { if (redirectToFirst) { const extras = { - queryParams: { videoId: this.playlistVideos[ 0 ].uuid }, + queryParams: { videoId: firstAvailableVideos.video.uuid }, replaceUrl: true } this.router.navigate([], extras) @@ -88,11 +89,11 @@ export class VideoWatchPlaylistComponent { } updatePlaylistIndex (video: VideoDetails) { - if (this.playlistVideos.length === 0 || !video) return + if (this.playlistElements.length === 0 || !video) return - for (const playlistVideo of this.playlistVideos) { - if (playlistVideo.id === video.id) { - this.currentPlaylistPosition = playlistVideo.playlistElement.position + for (const playlistElement of this.playlistElements) { + if (playlistElement.video && playlistElement.video.id === video.id) { + this.currentPlaylistPosition = playlistElement.position return } } @@ -103,11 +104,17 @@ export class VideoWatchPlaylistComponent { navigateToNextPlaylistVideo () { if (this.currentPlaylistPosition < this.playlistPagination.totalItems) { - const next = this.playlistVideos.find(v => v.playlistElement.position === this.currentPlaylistPosition + 1) + const next = this.playlistElements.find(e => e.position === this.currentPlaylistPosition + 1) - const start = next.playlistElement.startTimestamp - const stop = next.playlistElement.stopTimestamp - this.router.navigate([],{ queryParams: { videoId: next.uuid, start, stop } }) + if (!next || !next.video) { + this.currentPlaylistPosition++ + this.navigateToNextPlaylistVideo() + return + } + + const start = next.startTimestamp + const stop = next.stopTimestamp + this.router.navigate([],{ queryParams: { videoId: next.video.uuid, start, stop } }) } } } diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts index 0d499d47f..d7c7b7497 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -464,7 +464,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { } this.zone.runOutsideAngular(async () => { - this.player = await PeertubePlayerManager.initialize(mode, options) + this.player = await PeertubePlayerManager.initialize(mode, options, player => this.player = player) this.player.on('customError', ({ err }: { err: any }) => this.handleError(err)) diff --git a/client/src/assets/player/peertube-player-manager.ts b/client/src/assets/player/peertube-player-manager.ts index 083c621d2..6c8b13087 100644 --- a/client/src/assets/player/peertube-player-manager.ts +++ b/client/src/assets/player/peertube-player-manager.ts @@ -86,6 +86,7 @@ export class PeertubePlayerManager { private static videojsLocaleCache: { [ path: string ]: any } = {} private static playerElementClassName: string + private static onPlayerChange: (player: any) => void static getServerTranslations (serverUrl: string, locale: string) { const path = PeertubePlayerManager.getLocalePath(serverUrl, locale) @@ -100,9 +101,10 @@ export class PeertubePlayerManager { }) } - static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions) { + static async initialize (mode: PlayerMode, options: PeertubePlayerManagerOptions, onPlayerChange: (player: any) => void) { let p2pMediaLoader: any + this.onPlayerChange = onPlayerChange this.playerElementClassName = options.common.playerElement.className if (mode === 'webtorrent') await import('./webtorrent/webtorrent-plugin') @@ -171,6 +173,8 @@ export class PeertubePlayerManager { const player = this self.addContextMenu(mode, player, options.common.embedUrl) + + PeertubePlayerManager.onPlayerChange(player) }) } diff --git a/client/src/assets/player/videojs-components/settings-menu-item.ts b/client/src/assets/player/videojs-components/settings-menu-item.ts index 78879a2ec..24b7e0c70 100644 --- a/client/src/assets/player/videojs-components/settings-menu-item.ts +++ b/client/src/assets/player/videojs-components/settings-menu-item.ts @@ -43,6 +43,9 @@ class SettingsMenuItem extends MenuItem { player.ready(() => { // Voodoo magic for IOS setTimeout(() => { + // Player was destroyed + if (!this.player_) return + this.build() // Update on rate change diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index cfe8e94b1..6ff3efef1 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -212,7 +212,7 @@ export class PeerTubeEmbed { }) } - this.player = await PeertubePlayerManager.initialize(this.mode, options) + this.player = await PeertubePlayerManager.initialize(this.mode, options, player => this.player = player) this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations)) window[ 'videojsPlayer' ] = this.player -- cgit v1.2.3