Reorganize client shared modules
[oweals/peertube.git] / client / src / app / videos / +video-watch / video-watch-playlist.component.ts
1 import { Component, Input } from '@angular/core'
2 import { Router } from '@angular/router'
3 import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core'
4 import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers/peertube-web-storage'
5 import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
6 import { I18n } from '@ngx-translate/i18n-polyfill'
7 import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models'
8
9 @Component({
10   selector: 'my-video-watch-playlist',
11   templateUrl: './video-watch-playlist.component.html',
12   styleUrls: [ './video-watch-playlist.component.scss' ]
13 })
14 export class VideoWatchPlaylistComponent {
15   static LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST = 'auto_play_video_playlist'
16   static SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST = 'loop_playlist'
17
18   @Input() video: VideoDetails
19   @Input() playlist: VideoPlaylist
20
21   playlistElements: VideoPlaylistElement[] = []
22   playlistPagination: ComponentPagination = {
23     currentPage: 1,
24     itemsPerPage: 30,
25     totalItems: null
26   }
27
28   autoPlayNextVideoPlaylist: boolean
29   autoPlayNextVideoPlaylistSwitchText = ''
30   loopPlaylist: boolean
31   loopPlaylistSwitchText = ''
32   noPlaylistVideos = false
33   currentPlaylistPosition = 1
34
35   constructor (
36     private userService: UserService,
37     private auth: AuthService,
38     private notifier: Notifier,
39     private i18n: I18n,
40     private videoPlaylist: VideoPlaylistService,
41     private localStorageService: LocalStorageService,
42     private sessionStorageService: SessionStorageService,
43     private router: Router
44   ) {
45     // defaults to true
46     this.autoPlayNextVideoPlaylist = this.auth.isLoggedIn()
47       ? this.auth.getUser().autoPlayNextVideoPlaylist
48       : this.localStorageService.getItem(VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) !== 'false'
49     this.setAutoPlayNextVideoPlaylistSwitchText()
50
51     // defaults to false
52     this.loopPlaylist = this.sessionStorageService.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true'
53     this.setLoopPlaylistSwitchText()
54   }
55
56   onPlaylistVideosNearOfBottom () {
57     // Last page
58     if (this.playlistPagination.totalItems <= (this.playlistPagination.currentPage * this.playlistPagination.itemsPerPage)) return
59
60     this.playlistPagination.currentPage += 1
61     this.loadPlaylistElements(this.playlist,false)
62   }
63
64   onElementRemoved (playlistElement: VideoPlaylistElement) {
65     this.playlistElements = this.playlistElements.filter(e => e.id !== playlistElement.id)
66
67     this.playlistPagination.totalItems--
68   }
69
70   isPlaylistOwned () {
71     return this.playlist.isLocal === true &&
72       this.auth.isLoggedIn() &&
73       this.playlist.ownerAccount.name === this.auth.getUser().username
74   }
75
76   isUnlistedPlaylist () {
77     return this.playlist.privacy.id === VideoPlaylistPrivacy.UNLISTED
78   }
79
80   isPrivatePlaylist () {
81     return this.playlist.privacy.id === VideoPlaylistPrivacy.PRIVATE
82   }
83
84   isPublicPlaylist () {
85     return this.playlist.privacy.id === VideoPlaylistPrivacy.PUBLIC
86   }
87
88   loadPlaylistElements (playlist: VideoPlaylist, redirectToFirst = false) {
89     this.videoPlaylist.getPlaylistVideos(playlist.uuid, this.playlistPagination)
90         .subscribe(({ total, data }) => {
91           this.playlistElements = this.playlistElements.concat(data)
92           this.playlistPagination.totalItems = total
93
94           const firstAvailableVideos = this.playlistElements.find(e => !!e.video)
95           if (!firstAvailableVideos) {
96             this.noPlaylistVideos = true
97             return
98           }
99
100           this.updatePlaylistIndex(this.video)
101
102           if (redirectToFirst) {
103             const extras = {
104               queryParams: {
105                 start: firstAvailableVideos.startTimestamp,
106                 stop: firstAvailableVideos.stopTimestamp,
107                 videoId: firstAvailableVideos.video.uuid
108               },
109               replaceUrl: true
110             }
111             this.router.navigate([], extras)
112           }
113         })
114   }
115
116   updatePlaylistIndex (video: VideoDetails) {
117     if (this.playlistElements.length === 0 || !video) return
118
119     for (const playlistElement of this.playlistElements) {
120       if (playlistElement.video && playlistElement.video.id === video.id) {
121         this.currentPlaylistPosition = playlistElement.position
122         return
123       }
124     }
125
126     // Load more videos to find our video
127     this.onPlaylistVideosNearOfBottom()
128   }
129
130   findNextPlaylistVideo (position = this.currentPlaylistPosition): VideoPlaylistElement {
131     if (this.currentPlaylistPosition >= this.playlistPagination.totalItems) {
132       // we have reached the end of the playlist: either loop or stop
133       if (this.loopPlaylist) {
134         this.currentPlaylistPosition = position = 0
135       } else {
136         return
137       }
138     }
139
140     const next = this.playlistElements.find(e => e.position === position)
141
142     if (!next || !next.video) {
143       return this.findNextPlaylistVideo(position + 1)
144     }
145
146     return next
147   }
148
149   navigateToNextPlaylistVideo () {
150     const next = this.findNextPlaylistVideo(this.currentPlaylistPosition + 1)
151     if (!next) return
152     const start = next.startTimestamp
153     const stop = next.stopTimestamp
154     this.router.navigate([],{ queryParams: { videoId: next.video.uuid, start, stop } })
155   }
156
157   switchAutoPlayNextVideoPlaylist () {
158     this.autoPlayNextVideoPlaylist = !this.autoPlayNextVideoPlaylist
159     this.setAutoPlayNextVideoPlaylistSwitchText()
160
161     peertubeLocalStorage.setItem(
162       VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST,
163       this.autoPlayNextVideoPlaylist.toString()
164     )
165
166     if (this.auth.isLoggedIn()) {
167       const details = {
168         autoPlayNextVideoPlaylist: this.autoPlayNextVideoPlaylist
169       }
170
171       this.userService.updateMyProfile(details).subscribe(
172         () => {
173           this.auth.refreshUserInformation()
174         },
175         err => this.notifier.error(err.message)
176       )
177     }
178   }
179
180   switchLoopPlaylist () {
181     this.loopPlaylist = !this.loopPlaylist
182     this.setLoopPlaylistSwitchText()
183
184     peertubeSessionStorage.setItem(
185       VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST,
186       this.loopPlaylist.toString()
187     )
188   }
189
190   private setAutoPlayNextVideoPlaylistSwitchText () {
191     this.autoPlayNextVideoPlaylistSwitchText = this.autoPlayNextVideoPlaylist
192       ? this.i18n('Stop autoplaying next video')
193       : this.i18n('Autoplay next video')
194   }
195
196   private setLoopPlaylistSwitchText () {
197     this.loopPlaylistSwitchText = this.loopPlaylist
198       ? this.i18n('Stop looping playlist videos')
199       : this.i18n('Loop playlist videos')
200   }
201 }