Move video watch playlist in its own component
This commit is contained in:
parent
722bca907b
commit
72675ebe01
|
@ -2,6 +2,13 @@
|
||||||
@import '_mixins';
|
@import '_mixins';
|
||||||
@import '_miniature';
|
@import '_miniature';
|
||||||
|
|
||||||
|
my-video-thumbnail {
|
||||||
|
@include thumbnail-size-component(130px, 72px);
|
||||||
|
|
||||||
|
display: flex; // Avoids an issue with line-height that adds space below the element
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.video {
|
.video {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -44,13 +51,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my-video-thumbnail {
|
|
||||||
@include thumbnail-size-component(130px, 72px);
|
|
||||||
|
|
||||||
display: flex; // Avoids an issue with line-height that adds space below the element
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-info {
|
.video-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { AfterContentInit, AfterViewInit, Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core'
|
import { AfterViewInit, Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { DropdownAction, DropdownButtonSize, DropdownDirection } from '@app/shared/buttons/action-dropdown.component'
|
import { DropdownAction, DropdownButtonSize, DropdownDirection } from '@app/shared/buttons/action-dropdown.component'
|
||||||
import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core'
|
import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core'
|
||||||
|
@ -133,6 +133,10 @@ export class VideoActionsDropdownComponent implements AfterViewInit, OnChanges {
|
||||||
return this.video.isUnblacklistableBy(this.user)
|
return this.video.isUnblacklistableBy(this.user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isVideoDownloadable () {
|
||||||
|
return this.video && this.video instanceof VideoDetails && this.video.downloadEnabled
|
||||||
|
}
|
||||||
|
|
||||||
/* Action handlers */
|
/* Action handlers */
|
||||||
|
|
||||||
async unblacklistVideo () {
|
async unblacklistVideo () {
|
||||||
|
@ -202,7 +206,7 @@ export class VideoActionsDropdownComponent implements AfterViewInit, OnChanges {
|
||||||
{
|
{
|
||||||
label: this.i18n('Download'),
|
label: this.i18n('Download'),
|
||||||
handler: () => this.showDownloadModal(),
|
handler: () => this.showDownloadModal(),
|
||||||
isDisplayed: () => this.displayOptions.download,
|
isDisplayed: () => this.displayOptions.download && this.isVideoDownloadable(),
|
||||||
iconName: 'download'
|
iconName: 'download'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div *ngIf="playlist && video" class="playlist" myInfiniteScroller [autoInit]="true" [onItself]="true" (nearOfBottom)="onPlaylistVideosNearOfBottom()">
|
||||||
|
<div class="playlist-info">
|
||||||
|
<div class="playlist-display-name">
|
||||||
|
{{ playlist.displayName }}
|
||||||
|
|
||||||
|
<span *ngIf="isUnlistedPlaylist()" class="badge badge-warning" i18n>Unlisted</span>
|
||||||
|
<span *ngIf="isPrivatePlaylist()" class="badge badge-danger" i18n>Private</span>
|
||||||
|
<span *ngIf="isPublicPlaylist()" class="badge badge-info" i18n>Public</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="playlist-by-index">
|
||||||
|
<div class="playlist-by">{{ playlist.ownerBy }}</div>
|
||||||
|
<div class="playlist-index">
|
||||||
|
<span>{{ currentPlaylistPosition }}</span><span>{{ playlistPagination.totalItems }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngFor="let playlistVideo of playlistVideos">
|
||||||
|
<my-video-playlist-element-miniature
|
||||||
|
[video]="playlistVideo" [playlist]="playlist" [owned]="isPlaylistOwned()" (elementRemoved)="onElementRemoved($event)"
|
||||||
|
[playing]="currentPlaylistPosition === playlistVideo.playlistElement.position" [accountLink]="false" [position]="playlistVideo.playlistElement.position"
|
||||||
|
></my-video-playlist-element-miniature>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,59 @@
|
||||||
|
@import '_variables';
|
||||||
|
@import '_mixins';
|
||||||
|
@import '_bootstrap-variables';
|
||||||
|
@import '_miniature';
|
||||||
|
|
||||||
|
.playlist {
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 470px;
|
||||||
|
height: 66vh;
|
||||||
|
background-color: var(--mainBackgroundColor);
|
||||||
|
overflow-y: auto;
|
||||||
|
border-bottom: 1px solid $separator-border-color;
|
||||||
|
|
||||||
|
.playlist-info {
|
||||||
|
padding: 5px 30px;
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
|
||||||
|
.playlist-display-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: $font-semibold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-by-index {
|
||||||
|
color: $grey-foreground-color;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.playlist-by {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playlist-index span:first-child::after {
|
||||||
|
content: '/';
|
||||||
|
margin: 0 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my-video-playlist-element-miniature {
|
||||||
|
/deep/ {
|
||||||
|
.video {
|
||||||
|
.position {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-info {
|
||||||
|
.video-info-name {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my-video-thumbnail {
|
||||||
|
@include thumbnail-size-component(90px, 50px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
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'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-video-watch-playlist',
|
||||||
|
templateUrl: './video-watch-playlist.component.html',
|
||||||
|
styleUrls: [ './video-watch-playlist.component.scss' ]
|
||||||
|
})
|
||||||
|
export class VideoWatchPlaylistComponent {
|
||||||
|
@Input() video: VideoDetails
|
||||||
|
@Input() playlist: VideoPlaylist
|
||||||
|
|
||||||
|
playlistVideos: Video[] = []
|
||||||
|
playlistPagination: ComponentPagination = {
|
||||||
|
currentPage: 1,
|
||||||
|
itemsPerPage: 30,
|
||||||
|
totalItems: null
|
||||||
|
}
|
||||||
|
|
||||||
|
noPlaylistVideos = false
|
||||||
|
currentPlaylistPosition = 1
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
private auth: AuthService,
|
||||||
|
private videoService: VideoService,
|
||||||
|
private router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
|
onPlaylistVideosNearOfBottom () {
|
||||||
|
// Last page
|
||||||
|
if (this.playlistPagination.totalItems <= (this.playlistPagination.currentPage * this.playlistPagination.itemsPerPage)) return
|
||||||
|
|
||||||
|
this.playlistPagination.currentPage += 1
|
||||||
|
this.loadPlaylistElements(this.playlist,false)
|
||||||
|
}
|
||||||
|
|
||||||
|
onElementRemoved (video: Video) {
|
||||||
|
this.playlistVideos = this.playlistVideos.filter(v => v.id !== video.id)
|
||||||
|
|
||||||
|
this.playlistPagination.totalItems--
|
||||||
|
}
|
||||||
|
|
||||||
|
isPlaylistOwned () {
|
||||||
|
return this.playlist.isLocal === true && this.playlist.ownerAccount.name === this.auth.getUser().username
|
||||||
|
}
|
||||||
|
|
||||||
|
isUnlistedPlaylist () {
|
||||||
|
return this.playlist.privacy.id === VideoPlaylistPrivacy.UNLISTED
|
||||||
|
}
|
||||||
|
|
||||||
|
isPrivatePlaylist () {
|
||||||
|
return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE
|
||||||
|
}
|
||||||
|
|
||||||
|
isPublicPlaylist () {
|
||||||
|
return this.playlist.privacy.id === VideoPlaylistPrivacy.PUBLIC
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPlaylistElements (playlist: VideoPlaylist, redirectToFirst = false) {
|
||||||
|
this.videoService.getPlaylistVideos(playlist.uuid, this.playlistPagination)
|
||||||
|
.subscribe(({ totalVideos, videos }) => {
|
||||||
|
this.playlistVideos = this.playlistVideos.concat(videos)
|
||||||
|
this.playlistPagination.totalItems = totalVideos
|
||||||
|
|
||||||
|
if (totalVideos === 0) {
|
||||||
|
this.noPlaylistVideos = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updatePlaylistIndex(this.video)
|
||||||
|
|
||||||
|
if (redirectToFirst) {
|
||||||
|
const extras = {
|
||||||
|
queryParams: { videoId: this.playlistVideos[ 0 ].uuid },
|
||||||
|
replaceUrl: true
|
||||||
|
}
|
||||||
|
this.router.navigate([], extras)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePlaylistIndex (video: VideoDetails) {
|
||||||
|
if (this.playlistVideos.length === 0 || !video) return
|
||||||
|
|
||||||
|
for (const playlistVideo of this.playlistVideos) {
|
||||||
|
if (playlistVideo.id === video.id) {
|
||||||
|
this.currentPlaylistPosition = playlistVideo.playlistElement.position
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load more videos to find our video
|
||||||
|
this.onPlaylistVideosNearOfBottom()
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToNextPlaylistVideo () {
|
||||||
|
if (this.currentPlaylistPosition < this.playlistPagination.totalItems) {
|
||||||
|
const next = this.playlistVideos.find(v => v.playlistElement.position === this.currentPlaylistPosition + 1)
|
||||||
|
|
||||||
|
const start = next.playlistElement.startTimestamp
|
||||||
|
const stop = next.playlistElement.stopTimestamp
|
||||||
|
this.router.navigate([],{ queryParams: { videoId: next.uuid, start, stop } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,31 +9,10 @@
|
||||||
|
|
||||||
<div id="videojs-wrapper"></div>
|
<div id="videojs-wrapper"></div>
|
||||||
|
|
||||||
<div *ngIf="playlist && video" class="playlist" myInfiniteScroller [autoInit]="true" [onItself]="true" (nearOfBottom)="onPlaylistVideosNearOfBottom()">
|
<my-video-watch-playlist
|
||||||
<div class="playlist-info">
|
#videoWatchPlaylist
|
||||||
<div class="playlist-display-name">
|
[video]="video" [playlist]="playlist" class="playlist"
|
||||||
{{ playlist.displayName }}
|
></my-video-watch-playlist>
|
||||||
|
|
||||||
<span *ngIf="isUnlistedPlaylist()" class="badge badge-warning" i18n>Unlisted</span>
|
|
||||||
<span *ngIf="isPrivatePlaylist()" class="badge badge-danger" i18n>Private</span>
|
|
||||||
<span *ngIf="isPublicPlaylist()" class="badge badge-info" i18n>Public</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="playlist-by-index">
|
|
||||||
<div class="playlist-by">{{ playlist.ownerBy }}</div>
|
|
||||||
<div class="playlist-index">
|
|
||||||
<span>{{ currentPlaylistPosition }}</span><span>{{ playlistPagination.totalItems }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngFor="let playlistVideo of playlistVideos">
|
|
||||||
<my-video-playlist-element-miniature
|
|
||||||
[video]="playlistVideo" [playlist]="playlist" [owned]="isPlaylistOwned()" (elementRemoved)="onElementRemoved($event)"
|
|
||||||
[playing]="currentPlaylistPosition === playlistVideo.playlistElement.position" [accountLink]="false" [position]="playlistVideo.playlistElement.position"
|
|
||||||
></my-video-playlist-element-miniature>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -15,10 +15,10 @@ $player-factor: 1.7; // 16/9
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin playlist-below-player {
|
@mixin playlist-below-player {
|
||||||
width: 100%;
|
width: 100% !important;
|
||||||
height: auto;
|
height: auto !important;
|
||||||
max-height: 300px;
|
max-height: 300px !important;
|
||||||
border-bottom: 1px solid $separator-border-color;
|
border-bottom: 1px solid $separator-border-color !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
|
@ -37,7 +37,7 @@ $player-factor: 1.7; // 16/9
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlist {
|
my-video-watch-playlist /deep/ .playlist {
|
||||||
@include playlist-below-player;
|
@include playlist-below-player;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,60 +80,6 @@ $player-factor: 1.7; // 16/9
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlist {
|
|
||||||
min-width: 200px;
|
|
||||||
max-width: 470px;
|
|
||||||
height: 66vh;
|
|
||||||
background-color: var(--mainBackgroundColor);
|
|
||||||
overflow-y: auto;
|
|
||||||
border-bottom: 1px solid $separator-border-color;
|
|
||||||
|
|
||||||
.playlist-info {
|
|
||||||
padding: 5px 30px;
|
|
||||||
background-color: #e4e4e4;
|
|
||||||
|
|
||||||
.playlist-display-name {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: $font-semibold;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-by-index {
|
|
||||||
color: $grey-foreground-color;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.playlist-by {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-index span:first-child::after {
|
|
||||||
content: '/';
|
|
||||||
margin: 0 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my-video-playlist-element-miniature {
|
|
||||||
/deep/ {
|
|
||||||
.video {
|
|
||||||
.position {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-info {
|
|
||||||
.video-info-name {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my-video-thumbnail {
|
|
||||||
@include thumbnail-size-component(90px, 50px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/deep/ .video-js {
|
/deep/ .video-js {
|
||||||
width: getPlayerWidth(66vh);
|
width: getPlayerWidth(66vh);
|
||||||
height: 66vh;
|
height: 66vh;
|
||||||
|
@ -508,7 +454,7 @@ my-video-comments {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
.playlist {
|
my-video-watch-playlist /deep/ .playlist {
|
||||||
@include playlist-below-player;
|
@include playlist-below-player;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { MetaService } from '@ngx-meta/core'
|
||||||
import { Notifier, ServerService } from '@app/core'
|
import { Notifier, ServerService } from '@app/core'
|
||||||
import { forkJoin, Subscription } from 'rxjs'
|
import { forkJoin, Subscription } from 'rxjs'
|
||||||
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
|
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
|
||||||
import { UserVideoRateType, VideoCaption, VideoPlaylistPrivacy, VideoPrivacy, VideoState } from '../../../../../shared'
|
import { UserVideoRateType, VideoCaption, VideoPrivacy, VideoState } from '../../../../../shared'
|
||||||
import { AuthService, ConfirmService } from '../../core'
|
import { AuthService, ConfirmService } from '../../core'
|
||||||
import { RestExtractor, VideoBlacklistService } from '../../shared'
|
import { RestExtractor, VideoBlacklistService } from '../../shared'
|
||||||
import { VideoDetails } from '../../shared/video/video-details.model'
|
import { VideoDetails } from '../../shared/video/video-details.model'
|
||||||
|
@ -27,9 +27,9 @@ import {
|
||||||
} from '../../../assets/player/peertube-player-manager'
|
} from '../../../assets/player/peertube-player-manager'
|
||||||
import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
|
import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
|
||||||
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
|
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
|
||||||
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
|
||||||
import { Video } from '@app/shared/video/video.model'
|
import { Video } from '@app/shared/video/video.model'
|
||||||
import { isWebRTCDisabled } from '../../../assets/player/utils'
|
import { isWebRTCDisabled } from '../../../assets/player/utils'
|
||||||
|
import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-watch',
|
selector: 'my-video-watch',
|
||||||
|
@ -39,6 +39,7 @@ import { isWebRTCDisabled } from '../../../assets/player/utils'
|
||||||
export class VideoWatchComponent implements OnInit, OnDestroy {
|
export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
private static LOCAL_STORAGE_PRIVACY_CONCERN_KEY = 'video-watch-privacy-concern'
|
private static LOCAL_STORAGE_PRIVACY_CONCERN_KEY = 'video-watch-privacy-concern'
|
||||||
|
|
||||||
|
@ViewChild('videoWatchPlaylist') videoWatchPlaylist: VideoWatchPlaylistComponent
|
||||||
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
|
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
|
||||||
@ViewChild('videoSupportModal') videoSupportModal: VideoSupportComponent
|
@ViewChild('videoSupportModal') videoSupportModal: VideoSupportComponent
|
||||||
@ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent
|
@ViewChild('subscribeButton') subscribeButton: SubscribeButtonComponent
|
||||||
|
@ -51,14 +52,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
descriptionLoading = false
|
descriptionLoading = false
|
||||||
|
|
||||||
playlist: VideoPlaylist = null
|
playlist: VideoPlaylist = null
|
||||||
playlistVideos: Video[] = []
|
|
||||||
playlistPagination: ComponentPagination = {
|
|
||||||
currentPage: 1,
|
|
||||||
itemsPerPage: 30,
|
|
||||||
totalItems: null
|
|
||||||
}
|
|
||||||
noPlaylistVideos = false
|
|
||||||
currentPlaylistPosition = 1
|
|
||||||
|
|
||||||
completeDescriptionShown = false
|
completeDescriptionShown = false
|
||||||
completeVideoDescription: string
|
completeVideoDescription: string
|
||||||
|
@ -230,10 +223,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
return this.video.tags
|
return this.video.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
isVideoRemovable () {
|
|
||||||
return this.video.isRemovableBy(this.authService.getUser())
|
|
||||||
}
|
|
||||||
|
|
||||||
onVideoRemoved () {
|
onVideoRemoved () {
|
||||||
this.redirectService.redirectToHomepage()
|
this.redirectService.redirectToHomepage()
|
||||||
}
|
}
|
||||||
|
@ -247,10 +236,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
return this.video && this.video.state.id === VideoState.TO_TRANSCODE
|
return this.video && this.video.state.id === VideoState.TO_TRANSCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
isVideoDownloadable () {
|
|
||||||
return this.video && this.video.downloadEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
isVideoToImport () {
|
isVideoToImport () {
|
||||||
return this.video && this.video.state.id === VideoState.TO_IMPORT
|
return this.video && this.video.state.id === VideoState.TO_IMPORT
|
||||||
}
|
}
|
||||||
|
@ -263,36 +248,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
return video.isVideoNSFWForUser(this.user, this.serverService.getConfig())
|
return video.isVideoNSFWForUser(this.user, this.serverService.getConfig())
|
||||||
}
|
}
|
||||||
|
|
||||||
isPlaylistOwned () {
|
|
||||||
return this.playlist.isLocal === true && this.playlist.ownerAccount.name === this.user.username
|
|
||||||
}
|
|
||||||
|
|
||||||
isUnlistedPlaylist () {
|
|
||||||
return this.playlist.privacy.id === VideoPlaylistPrivacy.UNLISTED
|
|
||||||
}
|
|
||||||
|
|
||||||
isPrivatePlaylist () {
|
|
||||||
return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE
|
|
||||||
}
|
|
||||||
|
|
||||||
isPublicPlaylist () {
|
|
||||||
return this.playlist.privacy.id === VideoPlaylistPrivacy.PUBLIC
|
|
||||||
}
|
|
||||||
|
|
||||||
onPlaylistVideosNearOfBottom () {
|
|
||||||
// Last page
|
|
||||||
if (this.playlistPagination.totalItems <= (this.playlistPagination.currentPage * this.playlistPagination.itemsPerPage)) return
|
|
||||||
|
|
||||||
this.playlistPagination.currentPage += 1
|
|
||||||
this.loadPlaylistElements(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
onElementRemoved (video: Video) {
|
|
||||||
this.playlistVideos = this.playlistVideos.filter(v => v.id !== video.id)
|
|
||||||
|
|
||||||
this.playlistPagination.totalItems--
|
|
||||||
}
|
|
||||||
|
|
||||||
private loadVideo (videoId: string) {
|
private loadVideo (videoId: string) {
|
||||||
// Video did not change
|
// Video did not change
|
||||||
if (this.video && this.video.uuid === videoId) return
|
if (this.video && this.video.uuid === videoId) return
|
||||||
|
@ -333,33 +288,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.playlist = playlist
|
this.playlist = playlist
|
||||||
|
|
||||||
const videoId = this.route.snapshot.queryParams['videoId']
|
const videoId = this.route.snapshot.queryParams['videoId']
|
||||||
this.loadPlaylistElements(!videoId)
|
this.videoWatchPlaylist.loadPlaylistElements(playlist, !videoId)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadPlaylistElements (redirectToFirst = false) {
|
|
||||||
this.videoService.getPlaylistVideos(this.playlist.uuid, this.playlistPagination)
|
|
||||||
.subscribe(({ totalVideos, videos }) => {
|
|
||||||
this.playlistVideos = this.playlistVideos.concat(videos)
|
|
||||||
this.playlistPagination.totalItems = totalVideos
|
|
||||||
|
|
||||||
if (totalVideos === 0) {
|
|
||||||
this.noPlaylistVideos = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updatePlaylistIndex()
|
|
||||||
|
|
||||||
if (redirectToFirst) {
|
|
||||||
const extras = {
|
|
||||||
queryParams: { videoId: this.playlistVideos[ 0 ].uuid },
|
|
||||||
replaceUrl: true
|
|
||||||
}
|
|
||||||
this.router.navigate([], extras)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateVideoDescription (description: string) {
|
private updateVideoDescription (description: string) {
|
||||||
this.video.description = description
|
this.video.description = description
|
||||||
this.setVideoDescriptionHTML()
|
this.setVideoDescriptionHTML()
|
||||||
|
@ -421,7 +353,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.remoteServerDown = false
|
this.remoteServerDown = false
|
||||||
this.currentTime = undefined
|
this.currentTime = undefined
|
||||||
|
|
||||||
this.updatePlaylistIndex()
|
this.videoWatchPlaylist.updatePlaylistIndex(video)
|
||||||
|
|
||||||
let startTime = urlOptions.startTime || (this.video.userHistory ? this.video.userHistory.currentTime : 0)
|
let startTime = urlOptions.startTime || (this.video.userHistory ? this.video.userHistory.currentTime : 0)
|
||||||
// If we are at the end of the video, reset the timer
|
// If we are at the end of the video, reset the timer
|
||||||
|
@ -519,13 +451,13 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.player.one('ended', () => {
|
this.player.one('ended', () => {
|
||||||
if (this.playlist) {
|
if (this.playlist) {
|
||||||
this.zone.run(() => this.navigateToNextPlaylistVideo())
|
this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.player.one('stopped', () => {
|
this.player.one('stopped', () => {
|
||||||
if (this.playlist) {
|
if (this.playlist) {
|
||||||
this.zone.run(() => this.navigateToNextPlaylistVideo())
|
this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -586,20 +518,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.setVideoLikesBarTooltipText()
|
this.setVideoLikesBarTooltipText()
|
||||||
}
|
}
|
||||||
|
|
||||||
private updatePlaylistIndex () {
|
|
||||||
if (this.playlistVideos.length === 0 || !this.video) return
|
|
||||||
|
|
||||||
for (const video of this.playlistVideos) {
|
|
||||||
if (video.id === this.video.id) {
|
|
||||||
this.currentPlaylistPosition = video.playlistElement.position
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load more videos to find our video
|
|
||||||
this.onPlaylistVideosNearOfBottom()
|
|
||||||
}
|
|
||||||
|
|
||||||
private setOpenGraphTags () {
|
private setOpenGraphTags () {
|
||||||
this.metaService.setTitle(this.video.name)
|
this.metaService.setTitle(this.video.name)
|
||||||
|
|
||||||
|
@ -639,14 +557,4 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.player = undefined
|
this.player = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private navigateToNextPlaylistVideo () {
|
|
||||||
if (this.currentPlaylistPosition < this.playlistPagination.totalItems) {
|
|
||||||
const next = this.playlistVideos.find(v => v.playlistElement.position === this.currentPlaylistPosition + 1)
|
|
||||||
|
|
||||||
const start = next.playlistElement.startTimestamp
|
|
||||||
const stop = next.playlistElement.stopTimestamp
|
|
||||||
this.router.navigate([],{ queryParams: { videoId: next.uuid, start, stop } })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { VideoWatchComponent } from './video-watch.component'
|
||||||
import { NgxQRCodeModule } from 'ngx-qrcode2'
|
import { NgxQRCodeModule } from 'ngx-qrcode2'
|
||||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { RecommendationsModule } from '@app/videos/recommendations/recommendations.module'
|
import { RecommendationsModule } from '@app/videos/recommendations/recommendations.module'
|
||||||
|
import { VideoWatchPlaylistComponent } from '@app/videos/+video-watch/video-watch-playlist.component'
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -23,6 +24,7 @@ import { RecommendationsModule } from '@app/videos/recommendations/recommendatio
|
||||||
|
|
||||||
declarations: [
|
declarations: [
|
||||||
VideoWatchComponent,
|
VideoWatchComponent,
|
||||||
|
VideoWatchPlaylistComponent,
|
||||||
|
|
||||||
VideoShareComponent,
|
VideoShareComponent,
|
||||||
VideoSupportComponent,
|
VideoSupportComponent,
|
||||||
|
|
Loading…
Reference in New Issue