mirror of https://github.com/FreeTubeApp/FreeTube
425 lines
12 KiB
JavaScript
425 lines
12 KiB
JavaScript
import { defineComponent, nextTick } from 'vue'
|
|
import { mapActions } from 'vuex'
|
|
import FtShareButton from '../ft-share-button/ft-share-button.vue'
|
|
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
|
|
import FtIconButton from '../ft-icon-button/ft-icon-button.vue'
|
|
import FtInput from '../ft-input/ft-input.vue'
|
|
import FtPrompt from '../ft-prompt/ft-prompt.vue'
|
|
import FtButton from '../ft-button/ft-button.vue'
|
|
import {
|
|
ctrlFHandler,
|
|
formatNumber,
|
|
showToast,
|
|
} from '../../helpers/utils'
|
|
import debounce from 'lodash.debounce'
|
|
|
|
export default defineComponent({
|
|
name: 'PlaylistInfo',
|
|
components: {
|
|
'ft-share-button': FtShareButton,
|
|
'ft-flex-box': FtFlexBox,
|
|
'ft-icon-button': FtIconButton,
|
|
'ft-input': FtInput,
|
|
'ft-prompt': FtPrompt,
|
|
'ft-button': FtButton,
|
|
},
|
|
props: {
|
|
id: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
firstVideoId: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
firstVideoPlaylistItemId: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
playlistThumbnail: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
title: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
channelThumbnail: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
channelName: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
channelId: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
videoCount: {
|
|
type: Number,
|
|
required: true,
|
|
},
|
|
videos: {
|
|
type: Array,
|
|
required: true
|
|
},
|
|
viewCount: {
|
|
type: Number,
|
|
required: true,
|
|
},
|
|
lastUpdated: {
|
|
type: String,
|
|
default: undefined,
|
|
},
|
|
description: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
infoSource: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
moreVideoDataAvailable: {
|
|
type: Boolean,
|
|
required: true,
|
|
},
|
|
searchVideoModeAllowed: {
|
|
type: Boolean,
|
|
required: true,
|
|
},
|
|
searchVideoModeEnabled: {
|
|
type: Boolean,
|
|
required: true,
|
|
},
|
|
searchQueryText: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
},
|
|
data: function () {
|
|
return {
|
|
searchVideoMode: false,
|
|
query: '',
|
|
updateQueryDebounce: function() {},
|
|
editMode: false,
|
|
showDeletePlaylistPrompt: false,
|
|
showRemoveVideosOnWatchPrompt: false,
|
|
newTitle: '',
|
|
newDescription: '',
|
|
deletePlaylistPromptValues: [
|
|
'yes',
|
|
'no'
|
|
],
|
|
}
|
|
},
|
|
computed: {
|
|
hideSharingActions: function () {
|
|
return this.$store.getters.getHideSharingActions
|
|
},
|
|
|
|
currentInvidiousInstance: function () {
|
|
return this.$store.getters.getCurrentInvidiousInstance
|
|
},
|
|
|
|
historyCacheById: function () {
|
|
return this.$store.getters.getHistoryCacheById
|
|
},
|
|
|
|
thumbnailPreference: function () {
|
|
return this.$store.getters.getThumbnailPreference
|
|
},
|
|
|
|
blurThumbnails: function () {
|
|
return this.$store.getters.getBlurThumbnails
|
|
},
|
|
|
|
blurThumbnailsStyle: function () {
|
|
return this.blurThumbnails ? 'blur(20px)' : null
|
|
},
|
|
|
|
backendPreference: function () {
|
|
return this.$store.getters.getBackendPreference
|
|
},
|
|
|
|
hideViews: function () {
|
|
return this.$store.getters.getHideVideoViews
|
|
},
|
|
|
|
showPlaylists: function () {
|
|
return !this.$store.getters.getHidePlaylists
|
|
},
|
|
|
|
selectedUserPlaylist: function () {
|
|
return this.$store.getters.getPlaylist(this.id)
|
|
},
|
|
|
|
deletePlaylistPromptNames: function () {
|
|
return [
|
|
this.$t('Yes'),
|
|
this.$t('No')
|
|
]
|
|
},
|
|
|
|
firstVideoIdExists() {
|
|
return this.firstVideoId !== ''
|
|
},
|
|
|
|
parsedViewCount() {
|
|
return formatNumber(this.viewCount)
|
|
},
|
|
|
|
parsedVideoCount() {
|
|
return formatNumber(this.videoCount)
|
|
},
|
|
|
|
thumbnail: function () {
|
|
if (this.thumbnailPreference === 'hidden' || !this.firstVideoIdExists) {
|
|
return require('../../assets/img/thumbnail_placeholder.svg')
|
|
}
|
|
|
|
let baseUrl = 'https://i.ytimg.com'
|
|
if (this.backendPreference === 'invidious') {
|
|
baseUrl = this.currentInvidiousInstance
|
|
} else if (typeof this.playlistThumbnail === 'string' && this.playlistThumbnail.length > 0) {
|
|
// Use playlist thumbnail provided by YT when available
|
|
return this.playlistThumbnail
|
|
}
|
|
|
|
switch (this.thumbnailPreference) {
|
|
case 'start':
|
|
return `${baseUrl}/vi/${this.firstVideoId}/mq1.jpg`
|
|
case 'middle':
|
|
return `${baseUrl}/vi/${this.firstVideoId}/mq2.jpg`
|
|
case 'end':
|
|
return `${baseUrl}/vi/${this.firstVideoId}/mq3.jpg`
|
|
default:
|
|
return `${baseUrl}/vi/${this.firstVideoId}/mqdefault.jpg`
|
|
}
|
|
},
|
|
|
|
isUserPlaylist() {
|
|
return this.infoSource === 'user'
|
|
},
|
|
|
|
videoPlaylistType() {
|
|
return this.isUserPlaylist ? 'user' : ''
|
|
},
|
|
|
|
deletePlaylistButtonVisible: function() {
|
|
if (!this.isUserPlaylist) { return false }
|
|
// Cannot delete during edit
|
|
if (this.editMode) { return false }
|
|
|
|
// Cannot delete protected playlist
|
|
return !this.selectedUserPlaylist.protected
|
|
},
|
|
|
|
sharePlaylistButtonVisible: function() {
|
|
// Only online playlists can be shared
|
|
if (this.isUserPlaylist) { return false }
|
|
|
|
// Cannot delete protected playlist
|
|
return !this.hideSharingActions
|
|
},
|
|
|
|
quickBookmarkPlaylistId() {
|
|
return this.$store.getters.getQuickBookmarkTargetPlaylistId
|
|
},
|
|
quickBookmarkPlaylist() {
|
|
return this.$store.getters.getPlaylist(this.quickBookmarkPlaylistId)
|
|
},
|
|
quickBookmarkEnabled() {
|
|
return this.quickBookmarkPlaylist != null
|
|
},
|
|
markedAsQuickBookmarkTarget() {
|
|
// Only user playlists can be target
|
|
if (this.selectedUserPlaylist == null) { return false }
|
|
if (this.quickBookmarkPlaylist == null) { return false }
|
|
|
|
return this.quickBookmarkPlaylist._id === this.selectedUserPlaylist._id
|
|
},
|
|
},
|
|
watch: {
|
|
showDeletePlaylistPrompt(shown) {
|
|
this.$emit(shown ? 'prompt-open' : 'prompt-close')
|
|
},
|
|
showRemoveVideosOnWatchPrompt(shown) {
|
|
this.$emit(shown ? 'prompt-open' : 'prompt-close')
|
|
},
|
|
},
|
|
created: function () {
|
|
this.newTitle = this.title
|
|
this.newDescription = this.description
|
|
|
|
if (this.videoCount > 0) {
|
|
// Only enable search video mode when viewing non empty playlists
|
|
this.searchVideoMode = this.searchVideoModeEnabled
|
|
this.query = this.searchQueryText
|
|
}
|
|
|
|
this.updateQueryDebounce = debounce(this.updateQuery, 500)
|
|
},
|
|
mounted: function () {
|
|
document.addEventListener('keydown', this.keyboardShortcutHandler)
|
|
},
|
|
beforeDestroy: function () {
|
|
document.removeEventListener('keydown', this.keyboardShortcutHandler)
|
|
},
|
|
methods: {
|
|
toggleCopyVideosPrompt: function (force = false) {
|
|
if (this.moreVideoDataAvailable && !this.isUserPlaylist && !force) {
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast["Some videos in the playlist are not loaded yet. Click here to copy anyway."]'), 5000, () => {
|
|
this.toggleCopyVideosPrompt(true)
|
|
})
|
|
return
|
|
}
|
|
|
|
this.showAddToPlaylistPromptForManyVideos({
|
|
videos: this.videos,
|
|
newPlaylistDefaultProperties: { title: this.title },
|
|
})
|
|
},
|
|
|
|
savePlaylistInfo: function () {
|
|
if (this.newTitle === '') {
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast["Playlist name cannot be empty. Please input a name."]'))
|
|
return
|
|
}
|
|
|
|
const playlist = {
|
|
playlistName: this.newTitle,
|
|
protected: this.selectedUserPlaylist.protected,
|
|
description: this.newDescription,
|
|
videos: this.selectedUserPlaylist.videos,
|
|
_id: this.id,
|
|
}
|
|
try {
|
|
this.updatePlaylist(playlist)
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast["Playlist has been updated."]'))
|
|
} catch (e) {
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast["There was an issue with updating this playlist."]'))
|
|
console.error(e)
|
|
} finally {
|
|
this.exitEditMode()
|
|
}
|
|
},
|
|
|
|
enterEditMode: function () {
|
|
this.newTitle = this.title
|
|
this.newDescription = this.description
|
|
this.editMode = true
|
|
|
|
this.$emit('enter-edit-mode')
|
|
|
|
nextTick(() => {
|
|
// Some elements only present after rendering update
|
|
this.$refs.playlistTitleInput.focus()
|
|
})
|
|
},
|
|
|
|
exitEditMode: function () {
|
|
this.editMode = false
|
|
|
|
this.$emit('exit-edit-mode')
|
|
},
|
|
|
|
handleRemoveVideosOnWatchPromptAnswer: function (option) {
|
|
if (option === 'yes') {
|
|
const videosToWatch = this.selectedUserPlaylist.videos.filter((video) => {
|
|
return this.historyCacheById[video.videoId] == null
|
|
})
|
|
|
|
const removedVideosCount = this.selectedUserPlaylist.videos.length - videosToWatch.length
|
|
|
|
if (removedVideosCount === 0) {
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast["There were no videos to remove."]'))
|
|
this.showRemoveVideosOnWatchPrompt = false
|
|
return
|
|
}
|
|
|
|
const playlist = {
|
|
playlistName: this.title,
|
|
protected: this.selectedUserPlaylist.protected,
|
|
description: this.description,
|
|
videos: videosToWatch,
|
|
_id: this.id
|
|
}
|
|
try {
|
|
this.updatePlaylist(playlist)
|
|
showToast(this.$tc('User Playlists.SinglePlaylistView.Toast.{videoCount} video(s) have been removed', removedVideosCount, {
|
|
videoCount: removedVideosCount,
|
|
}))
|
|
} catch (e) {
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast["There was an issue with updating this playlist."]'))
|
|
console.error(e)
|
|
}
|
|
}
|
|
this.showRemoveVideosOnWatchPrompt = false
|
|
},
|
|
|
|
handleDeletePlaylistPromptAnswer: function (option) {
|
|
if (option === 'yes') {
|
|
if (this.selectedUserPlaylist.protected) {
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast["This playlist is protected and cannot be removed."]'))
|
|
} else {
|
|
this.removePlaylist(this.id)
|
|
this.$router.push(
|
|
{
|
|
path: '/userPlaylists'
|
|
}
|
|
)
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast["Playlist {playlistName} has been deleted."]', {
|
|
playlistName: this.title,
|
|
}))
|
|
}
|
|
}
|
|
this.showDeletePlaylistPrompt = false
|
|
},
|
|
|
|
enableQuickBookmarkForThisPlaylist() {
|
|
const currentQuickBookmarkTargetPlaylist = this.quickBookmarkPlaylist
|
|
|
|
this.updateQuickBookmarkTargetPlaylistId(this.id)
|
|
if (currentQuickBookmarkTargetPlaylist != null) {
|
|
showToast(
|
|
this.$t('User Playlists.SinglePlaylistView.Toast["This playlist is now used for quick bookmark instead of {oldPlaylistName}. Click here to undo"]', {
|
|
oldPlaylistName: currentQuickBookmarkTargetPlaylist.playlistName,
|
|
}),
|
|
5000,
|
|
() => {
|
|
this.updateQuickBookmarkTargetPlaylistId(currentQuickBookmarkTargetPlaylist._id)
|
|
showToast(
|
|
this.$t('User Playlists.SinglePlaylistView.Toast["Reverted to use {oldPlaylistName} for quick bookmark"]', {
|
|
oldPlaylistName: currentQuickBookmarkTargetPlaylist.playlistName,
|
|
}),
|
|
5000,
|
|
)
|
|
},
|
|
)
|
|
} else {
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast.This playlist is now used for quick bookmark'))
|
|
}
|
|
},
|
|
disableQuickBookmark() {
|
|
this.updateQuickBookmarkTargetPlaylistId(null)
|
|
showToast(this.$t('User Playlists.SinglePlaylistView.Toast.Quick bookmark disabled'))
|
|
},
|
|
|
|
updateQuery(query) {
|
|
this.query = query
|
|
this.$emit('search-video-query-change', query)
|
|
},
|
|
|
|
keyboardShortcutHandler: function (event) {
|
|
ctrlFHandler(event, this.$refs.searchInput)
|
|
},
|
|
...mapActions([
|
|
'showAddToPlaylistPromptForManyVideos',
|
|
'updatePlaylist',
|
|
'removePlaylist',
|
|
'updateQuickBookmarkTargetPlaylistId',
|
|
]),
|
|
},
|
|
})
|