-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathwatch-video-playlist.js
More file actions
393 lines (356 loc) · 11.2 KB
/
watch-video-playlist.js
File metadata and controls
393 lines (356 loc) · 11.2 KB
Edit and raw actions
OlderNewer
1
import Vue from 'vue'
2
import { mapActions } from 'vuex'
3
import FtLoader from '../ft-loader/ft-loader.vue'
4
import FtCard from '../ft-card/ft-card.vue'
5
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
6
import FtListVideo from '../ft-list-video/ft-list-video.vue'
7
8
export default Vue.extend({
9
name: 'WatchVideoPlaylist',
10
components: {
11
'ft-loader': FtLoader,
12
'ft-card': FtCard,
13
'ft-flex-box': FtFlexBox,
14
'ft-list-video': FtListVideo
15
},
16
props: {
17
playlistId: {
18
type: String,
19
required: true
20
},
21
videoId: {
22
type: String,
23
required: true
24
}
25
},
26
data: function () {
27
return {
28
isLoading: false,
29
shuffleEnabled: false,
30
loopEnabled: false,
31
reversePlaylist: false,
32
channelName: '',
33
channelId: '',
34
channelThumbnail: '',
35
playlistTitle: '',
36
playlistItems: [],
37
randomizedPlaylistItems: []
38
}
39
},
40
computed: {
41
backendPreference: function () {
42
return this.$store.getters.getBackendPreference
43
},
44
45
backendFallback: function () {
46
return this.$store.getters.getBackendFallback
47
},
48
49
currentVideoIndex: function () {
50
const index = this.playlistItems.findIndex((item) => {
51
if (typeof item.videoId !== 'undefined') {
52
return item.videoId === this.videoId
53
} else {
54
return item.id === this.videoId
55
}
56
})
57
58
return index + 1
59
},
60
61
playlistVideoCount: function () {
62
return this.playlistItems.length
63
}
64
},
65
watch: {
66
videoId: function (newId, oldId) {
67
// Check if next video is from the shuffled list or if the user clicked a different video
68
if (this.shuffleEnabled) {
69
const newVideoIndex = this.randomizedPlaylistItems.findIndex((item) => {
70
return item === newId
71
})
72
73
const oldVideoIndex = this.randomizedPlaylistItems.findIndex((item) => {
74
return item === oldId
75
})
76
77
if ((newVideoIndex - 1) !== oldVideoIndex) {
78
// User clicked a different video than expected. Re-shuffle the list
79
this.shufflePlaylistItems()
80
}
81
}
82
}
83
},
84
mounted: function () {
85
if (!process.env.IS_ELECTRON || this.backendPreference === 'invidious') {
86
this.getPlaylistInformationInvidious()
87
} else {
88
this.getPlaylistInformationLocal()
89
}
90
91
if ('mediaSession' in navigator) {
92
navigator.mediaSession.setActionHandler('previoustrack', this.playPreviousVideo)
93
navigator.mediaSession.setActionHandler('nexttrack', this.playNextVideo)
94
}
95
},
96
beforeDestroy: function () {
97
if ('mediaSession' in navigator) {
98
navigator.mediaSession.setActionHandler('previoustrack', null)
99
navigator.mediaSession.setActionHandler('nexttrack', null)
100
}
101
},
102
methods: {
103
goToPlaylist: function () {
104
this.$router.push({ path: `/playlist/${this.playlistId}` })
105
},
106
107
goToChannel: function () {
108
this.$router.push({ path: `/channel/${this.channelId}` })
109
},
110
111
toggleLoop: function () {
112
if (this.loopEnabled) {
113
this.loopEnabled = false
114
this.showToast({
115
message: this.$t('Loop is now disabled')
116
})
117
} else {
118
this.loopEnabled = true
119
this.showToast({
120
message: this.$t('Loop is now enabled')
121
})
122
}
123
},
124
125
toggleShuffle: function () {
126
if (this.shuffleEnabled) {
127
this.shuffleEnabled = false
128
this.showToast({
129
message: this.$t('Shuffle is now disabled')
130
})
131
} else {
132
this.shuffleEnabled = true
133
this.showToast({
134
message: this.$t('Shuffle is now enabled')
135
})
136
this.shufflePlaylistItems()
137
}
138
},
139
140
toggleReversePlaylist: function () {
141
this.isLoading = true
142
this.showToast({
143
message: this.$t('The playlist has been reversed')
144
})
145
146
this.reversePlaylist = !this.reversePlaylist
147
this.playlistItems = this.playlistItems.reverse()
148
setTimeout(() => {
149
this.isLoading = false
150
}, 1)
151
},
152
153
playNextVideo: function () {
154
const playlistInfo = {
155
playlistId: this.playlistId
156
}
157
158
if (this.shuffleEnabled) {
159
const videoIndex = this.randomizedPlaylistItems.findIndex((item) => {
160
return item === this.videoId
161
})
162
163
if (videoIndex === this.randomizedPlaylistItems.length - 1) {
164
if (this.loopEnabled) {
165
this.$router.push(
166
{
167
path: `/watch/${this.randomizedPlaylistItems[0]}`,
168
query: playlistInfo
169
}
170
)
171
this.showToast({
172
message: this.$t('Playing Next Video')
173
})
174
this.shufflePlaylistItems()
175
} else {
176
this.showToast({
177
message: this.$t('The playlist has ended. Enable loop to continue playing')
178
})
179
}
180
} else {
181
this.$router.push(
182
{
183
path: `/watch/${this.randomizedPlaylistItems[videoIndex + 1]}`,
184
query: playlistInfo
185
}
186
)
187
this.showToast({
188
message: this.$t('Playing Next Video')
189
})
190
}
191
} else {
192
const videoIndex = this.playlistItems.findIndex((item) => {
193
return (item.id ?? item.videoId) === this.videoId
194
})
195
196
if (videoIndex === this.playlistItems.length - 1) {
197
if (this.loopEnabled) {
198
this.$router.push(
199
{
200
path: `/watch/${this.playlistItems[0].id ?? this.playlistItems[0].videoId}`,
201
query: playlistInfo
202
}
203
)
204
this.showToast({
205
message: this.$t('Playing Next Video')
206
})
207
}
208
this.showToast({
209
message: this.$t('The playlist has ended. Enable loop to continue playing')
210
})
211
} else {
212
this.$router.push(
213
{
214
path: `/watch/${this.playlistItems[videoIndex + 1].id ?? this.playlistItems[videoIndex + 1].videoId}`,
215
query: playlistInfo
216
}
217
)
218
this.showToast({
219
message: this.$t('Playing Next Video')
220
})
221
}
222
}
223
},
224
225
playPreviousVideo: function () {
226
this.showToast({
227
message: 'Playing previous video'
228
})
229
230
const playlistInfo = {
231
playlistId: this.playlistId
232
}
233
234
if (this.shuffleEnabled) {
235
const videoIndex = this.randomizedPlaylistItems.findIndex((item) => {
236
return item === this.videoId
237
})
238
239
if (videoIndex === 0) {
240
this.$router.push(
241
{
242
path: `/watch/${this.randomizedPlaylistItems[this.randomizedPlaylistItems.length - 1]}`,
243
query: playlistInfo
244
}
245
)
246
} else {
247
this.$router.push(
248
{
249
path: `/watch/${this.randomizedPlaylistItems[videoIndex - 1]}`,
250
query: playlistInfo
251
}
252
)
253
}
254
} else {
255
const videoIndex = this.playlistItems.findIndex((item) => {
256
return (item.id ?? item.videoId) === this.videoId
257
})
258
259
if (videoIndex === 0) {
260
this.$router.push(
261
{
262
path: `/watch/${this.playlistItems[this.randomizedPlaylistItems.length - 1].id ?? this.playlistItems[this.randomizedPlaylistItems.length - 1].videoId}`,
263
query: playlistInfo
264
}
265
)
266
} else {
267
this.$router.push(
268
{
269
path: `/watch/${this.playlistItems[videoIndex - 1].id ?? this.playlistItems[videoIndex - 1].videoId}`,
270
query: playlistInfo
271
}
272
)
273
}
274
}
275
},
276
277
getPlaylistInformationLocal: function () {
278
this.isLoading = true
279
280
this.ytGetPlaylistInfo(this.playlistId).then((result) => {
281
this.playlistTitle = result.title
282
this.playlistItems = result.items
283
this.videoCount = result.estimatedItemCount
284
this.channelName = result.author.name
285
this.channelThumbnail = result.author.bestAvatar.url
286
this.channelId = result.author.channelID
287
288
this.playlistItems = result.items.filter((video) => {
289
return !(video.title === '[Private video]' || video.title === '[Deleted video]')
290
}).map((video) => {
291
if (typeof video.author !== 'undefined') {
292
const channelName = video.author.name
293
const channelId = video.author.channelID
294
video.author = channelName
295
video.authorId = channelId
296
} else {
297
video.author = ''
298
video.authorId = ''
299
}
300
video.videoId = video.id
301
video.lengthSeconds = video.duration
302
return video
303
})
304
305
this.isLoading = false
306
}).catch((err) => {
307
console.error(err)
308
const errorMessage = this.$t('Local API Error (Click to copy)')
309
this.showToast({
310
message: `${errorMessage}: ${err}`,
311
time: 10000,
312
action: () => {
313
this.copyToClipboard({ content: err })
314
}
315
})
316
if (this.backendPreference === 'local' && this.backendFallback) {
317
this.showToast({
318
message: this.$t('Falling back to Invidious API')
319
})
320
this.getPlaylistInformationInvidious()
321
} else {
322
this.isLoading = false
323
}
324
})
325
},
326
327
getPlaylistInformationInvidious: function () {
328
this.isLoading = true
329
330
const payload = {
331
resource: 'playlists',
332
id: this.playlistId
333
}
334
335
this.invidiousGetPlaylistInfo(payload).then((result) => {
336
this.playlistTitle = result.title
337
this.videoCount = result.videoCount
338
this.channelName = result.author
339
this.channelThumbnail = result.authorThumbnails[2].url
340
this.channelId = result.authorId
341
this.playlistItems = this.playlistItems.concat(result.videos)
342
343
this.isLoading = false
344
}).catch((err) => {
345
console.error(err)
346
const errorMessage = this.$t('Invidious API Error (Click to copy)')
347
this.showToast({
348
message: `${errorMessage}: ${err}`,
349
time: 10000,
350
action: () => {
351
this.copyToClipboard({ content: err })
352
}
353
})
354
if (this.backendPreference === 'invidious' && this.backendFallback) {
355
this.showToast({
356
message: this.$t('Falling back to Local API')
357
})
358
this.getPlaylistInformationLocal()
359
} else {
360
this.isLoading = false
361
// TODO: Show toast with error message
362
}
363
})
364
},
365
366
shufflePlaylistItems: function () {
367
// Prevents the array from affecting the original object
368
const remainingItems = [].concat(this.playlistItems)
369
const items = []
370
371
items.push(this.videoId)
372
373
this.playlistItems.forEach((item) => {
374
const randomInt = Math.floor(Math.random() * remainingItems.length)
375
376
if ((remainingItems[randomInt].id ?? remainingItems[randomInt].videoId) !== this.videoId) {
377
items.push(remainingItems[randomInt].id ?? remainingItems[randomInt].videoId)
378
}
379
380
remainingItems.splice(randomInt, 1)
381
})
382
383
this.randomizedPlaylistItems = items
384
},
385
386
...mapActions([
387
'showToast',
388
'ytGetPlaylistInfo',
389
'invidiousGetPlaylistInfo',
390
'copyToClipboard'
391
])
392
}
393
})