Initial Playlist implementation. Functions like basic favorites list

This commit is contained in:
Preston 2021-01-14 18:03:23 -05:00
parent 7505d8cf90
commit 659415edc3
12 changed files with 270 additions and 40 deletions

View File

@ -88,6 +88,7 @@ export default Vue.extend({
this.$store.dispatch('grabUserSettings') this.$store.dispatch('grabUserSettings')
this.$store.dispatch('grabHistory') this.$store.dispatch('grabHistory')
this.$store.dispatch('grabAllProfiles', this.$t('Profile.All Channels')) this.$store.dispatch('grabAllProfiles', this.$t('Profile.All Channels'))
this.$store.dispatch('grabAllPlaylists')
this.$store.commit('setUsingElectron', useElectron) this.$store.commit('setUsingElectron', useElectron)
this.checkThemeSettings() this.checkThemeSettings()
this.checkLocale() this.checkLocale()

View File

@ -52,6 +52,9 @@
&:active &:active
background-color: var(--accent-color-active) background-color: var(--accent-color-active)
&.favorite
color: var(--favorite-icon-color)
.iconDropdown .iconDropdown
display: none display: none
position: absolute position: absolute

View File

@ -165,6 +165,22 @@ export default Vue.extend({
addWatchedStyle: function () { addWatchedStyle: function () {
return this.watched && !this.inHistory return this.watched && !this.inHistory
},
favoritesPlaylist: function () {
return this.$store.getters.getFavorites
},
inFavoritesPlaylist: function () {
const index = this.favoritesPlaylist.videos.findIndex((video) => {
return video.videoId === this.id
})
return index !== -1
},
favoriteIconTheme: function () {
return this.inFavoritesPlaylist ? 'base favorite' : 'base'
} }
}, },
mounted: function () { mounted: function () {
@ -173,10 +189,11 @@ export default Vue.extend({
}, },
methods: { methods: {
toggleSave: function () { toggleSave: function () {
console.log('TODO: ft-list-video method toggleSave') if (this.inFavoritesPlaylist) {
this.showToast({ this.removeFromPlaylist()
message: this.$t('Saving videos are currently not available. Please wait for a future update') } else {
}) this.addToPlaylist()
}
}, },
handleOptionsClick: function (option) { handleOptionsClick: function (option) {
@ -396,11 +413,54 @@ export default Vue.extend({
this.watched = false this.watched = false
}, },
addToPlaylist: function () {
const videoData = {
videoId: this.id,
title: this.title,
author: this.channelName,
authorId: this.channelId,
published: '',
description: this.description,
viewCount: this.viewCount,
lengthSeconds: this.data.lengthSeconds,
timeAdded: new Date().getTime(),
isLive: false,
paid: false,
type: 'video'
}
const payload = {
playlistName: 'Favorites',
videoData: videoData
}
this.addVideo(payload)
this.showToast({
message: this.$t('Video.Video has been saved')
})
},
removeFromPlaylist: function () {
const payload = {
playlistName: 'Favorites',
videoId: this.id
}
this.removeVideo(payload)
this.showToast({
message: this.$t('Video.Video has been removed from your saved list')
})
},
...mapActions([ ...mapActions([
'showToast', 'showToast',
'toLocalePublicationString', 'toLocalePublicationString',
'updateHistory', 'updateHistory',
'removeFromHistory' 'removeFromHistory',
'addVideo',
'removeVideo'
]) ])
} }
}) })

View File

@ -32,13 +32,13 @@
</div> </div>
<ft-icon-button <ft-icon-button
v-if="!isLive" v-if="!isLive"
:title="$t('Video.Save Video')"
icon="star" icon="star"
class="favoritesIcon" class="favoritesIcon"
theme="base" :theme="favoriteIconTheme"
:padding="appearance === `watchPlaylistItem` ? 5 : 6" :padding="appearance === `watchPlaylistItem` ? 5 : 6"
:size="appearance === `watchPlaylistItem` ? 14 : 18" :size="appearance === `watchPlaylistItem` ? 14 : 18"
:class="{ favorited: isFavorited }" @click="toggleSave"
@click="toggleSave(id)"
/> />
<div <div
v-if="addWatchedStyle" v-if="addWatchedStyle"

View File

@ -86,6 +86,10 @@ export default Vue.extend({
theatrePossible: { theatrePossible: {
type: Boolean, type: Boolean,
required: true required: true
},
lengthSeconds: {
type: Number,
required: true
} }
}, },
data: function () { data: function () {
@ -131,6 +135,22 @@ export default Vue.extend({
return this.$store.getters.getHideVideoViews return this.$store.getters.getHideVideoViews
}, },
favoritesPlaylist: function () {
return this.$store.getters.getFavorites
},
inFavoritesPlaylist: function () {
const index = this.favoritesPlaylist.videos.findIndex((video) => {
return video.videoId === this.id
})
return index !== -1
},
favoriteIconTheme: function () {
return this.inFavoritesPlaylist ? 'base favorite' : 'base'
},
downloadLinkNames: function () { downloadLinkNames: function () {
return this.downloadLinks.map((download) => { return this.downloadLinks.map((download) => {
return download.label return download.label
@ -208,6 +228,14 @@ export default Vue.extend({
this.$router.push({ path: `/channel/${this.channelId}` }) this.$router.push({ path: `/channel/${this.channelId}` })
}, },
toggleSave: function () {
if (this.inFavoritesPlaylist) {
this.removeFromPlaylist()
} else {
this.addToPlaylist()
}
},
handleSubscription: function () { handleSubscription: function () {
if (this.channelId === '') { if (this.channelId === '') {
return return
@ -303,9 +331,48 @@ export default Vue.extend({
shell.openExternal(url) shell.openExternal(url)
}, },
addToPlaylist: function () {
const videoData = {
videoId: this.id,
title: this.title,
author: this.channelName,
authorId: this.channelId,
published: '',
description: this.description,
viewCount: this.viewCount,
lengthSeconds: this.lengthSeconds,
timeAdded: new Date().getTime(),
isLive: false,
paid: false,
type: 'video'
}
const payload = {
playlistName: 'Favorites',
videoData: videoData
}
this.addVideo(payload)
this.showToast({
message: this.$t('Video.Video has been marked as watched')
})
},
removeFromPlaylist: function () {
const payload = {
playlistName: 'Favorites',
videoId: this.id
}
this.removeVideo(payload)
},
...mapActions([ ...mapActions([
'showToast', 'showToast',
'updateProfile' 'updateProfile',
'addVideo',
'removeVideo'
]) ])
} }
}) })

View File

@ -62,6 +62,14 @@
</div> </div>
</div> </div>
<div class="videoOptions"> <div class="videoOptions">
<ft-icon-button
v-if="!isUpcoming"
:title="$t('Video.Save Video')"
icon="star"
class="option"
:theme="favoriteIconTheme"
@click="toggleSave"
/>
<ft-icon-button <ft-icon-button
v-if="theatrePossible" v-if="theatrePossible"
:title="$t('Toggle Theatre Mode')" :title="$t('Toggle Theatre Mode')"

View File

@ -4,14 +4,19 @@ let dbLocation
if (window && window.process && window.process.type === 'renderer') { if (window && window.process && window.process.type === 'renderer') {
// Electron is being used // Electron is being used
let dbLocation = localStorage.getItem('dbLocation') // let dbLocation = localStorage.getItem('dbLocation')
//
// if (dbLocation === null) {
// const electron = require('electron')
// dbLocation = electron.remote.app.getPath('userData')
// }
//
// dbLocation += '/playlists.db'
if (dbLocation === null) { const electron = require('electron')
const electron = require('electron') dbLocation = electron.remote.app.getPath('userData')
dbLocation = electron.remote.app.getPath('userData')
}
dbLocation += '/playlists.db' dbLocation = dbLocation + '/playlists.db'
} else { } else {
dbLocation = 'playlists.db' dbLocation = 'playlists.db'
} }
@ -24,12 +29,12 @@ const playlistDb = new Datastore({
const state = { const state = {
playlists: [ playlists: [
{ {
_id: 'favorites', playlistName: 'Favorites',
protected: true, protected: true,
videos: [] videos: []
}, },
{ {
_id: 'watchLater', playlistName: 'WatchLater',
protected: true, protected: true,
removeOnWatched: true, removeOnWatched: true,
videos: [] videos: []
@ -64,7 +69,7 @@ const actions = {
}) })
}, },
addVideo ({ commit }, payload) { addVideo ({ commit }, payload) {
playlistDb.update({ _id: payload.playlistId }, { $push: { videos: payload.videoId } }, { upsert: true }, err => { playlistDb.update({ playlistName: payload.playlistName }, { $push: { videos: payload.videoData } }, { upsert: true }, err => {
if (err) { if (err) {
console.error(err) console.error(err)
} else { } else {
@ -81,12 +86,17 @@ const actions = {
} }
}) })
}, },
grabAllPlaylists({ commit }) { grabAllPlaylists({ commit, dispatch }) {
playlistDb.getAllData((err, payload) => { playlistDb.find({}, (err, payload) => {
if (err) { if (err) {
console.error(err) console.error(err)
} else { } else {
commit('setAllPlaylists', payload) if (payload.length === 0) {
commit('setAllPlaylists', state.playlists)
dispatch('addPlaylists', payload)
} else {
commit('setAllPlaylists', payload)
}
} }
}) })
}, },
@ -99,12 +109,12 @@ const actions = {
} }
}) })
}, },
removeAllVideos ({ commit }, playlistId) { removeAllVideos ({ commit }, playlistName) {
playlistDb.update({ _id: playlistId }, { $set: { videos: [] } }, { upsert: true }, err => { playlistDb.update({ playlistName: playlistName }, { $set: { videos: [] } }, { upsert: true }, err => {
if (err) { if (err) {
console.error(err) console.error(err)
} else { } else {
commit('removeAllVideos', playlistId) commit('removeAllVideos', playlistName)
} }
}) })
}, },
@ -127,7 +137,7 @@ const actions = {
}) })
}, },
removeVideo ({ commit }, payload) { removeVideo ({ commit }, payload) {
playlistDb.update({ _id: payload.playlistId }, { $pull: { videos: payload.videoId } }, { upsert: true }, err => { playlistDb.update({ playlistName: payload.playlistName }, { $pull: { videos: { videoId: payload.videoId } } }, { upsert: true }, (err, numRemoved) => {
if (err) { if (err) {
console.error(err) console.error(err)
} else { } else {
@ -136,7 +146,7 @@ const actions = {
}) })
}, },
removeVideos ({ commit }, payload) { removeVideos ({ commit }, payload) {
playlistDb.update({ _id: payload.playlistId }, { $pull: { videos: { $in: payload.videoIds } } }, { upsert: true }, err => { playlistDb.update({ _id: payload.playlistName }, { $pull: { videos: { $in: payload.videoId } } }, { upsert: true }, err => {
if (err) { if (err) {
console.error(err) console.error(err)
} else { } else {
@ -154,9 +164,9 @@ const mutations = {
state.playlists = state.playlists.concat(payload) state.playlists = state.playlists.concat(payload)
}, },
addVideo (state, payload) { addVideo (state, payload) {
const playlist = state.playlists.find(playlist => playlist._id === payload.playlistId) const playlist = state.playlists.find(playlist => playlist.playlistName === payload.playlistName)
if (playlist) { if (playlist) {
playlist.videos.push(payload.videoId) playlist.videos.push(payload.videoData)
} }
}, },
addVideos (state, payload) { addVideos (state, payload) {
@ -168,22 +178,22 @@ const mutations = {
removeAllPlaylists (state) { removeAllPlaylists (state) {
state.playlists = state.playlists.filter(playlist => playlist.protected !== true) state.playlists = state.playlists.filter(playlist => playlist.protected !== true)
}, },
removeAllVideos (state, playlistId) { removeAllVideos (state, playlistName) {
const playlist = state.playlists.find(playlist => playlist._id === playlistId) const playlist = state.playlists.find(playlist => playlist.playlistName === playlistName)
if (playlist) { if (playlist) {
playlist.videos = [] playlist.videos = []
} }
}, },
removeVideo (state, payload) { removeVideo (state, payload) {
const playlist = state.playlists.find(playlist => playlist._id === payload.playlistId) const playlist = state.playlists.findIndex(playlist => playlist.playlistName === payload.playlistName)
if (playlist) { if (playlist !== -1) {
playlist.videos = playlist.videos.filter(video => video !== payload.videoId) state.playlists[playlist].videos = state.playlists[playlist].videos.filter(video => video.videoId !== payload.videoId)
} }
}, },
removeVideos (state, payload) { removeVideos (state, payload) {
const playlist = state.playlists.find(playlist => playlist._id === payload.playlistId) const playlist = state.playlists.findIndex(playlist => playlist._id === payload.playlistId)
if (playlist) { if (playlist !== -1) {
playlist.videos = playlist.videos.filter(video => payload.videoIds.indexOf(video) === -1) playlist.videos = playlist.videos.filter(video => payload.videoId.indexOf(video) === -1)
} }
}, },
removePlaylist (state, playlistId) { removePlaylist (state, playlistId) {

View File

@ -8,6 +8,7 @@
--bg-color: #f1f1f1; --bg-color: #f1f1f1;
--link-color: var(--accent-color); --link-color: var(--accent-color);
--link-visited-color: var(--accent-color-visited); --link-visited-color: var(--accent-color-visited);
--favorite-icon-color: #FFD600;
--card-bg-color: #FFFFFF; --card-bg-color: #FFFFFF;
--secondary-card-bg-color: #eeeeee; --secondary-card-bg-color: #eeeeee;
--scrollbar-color: #CCCCCC; --scrollbar-color: #CCCCCC;
@ -32,6 +33,7 @@
--bg-color: #212121; --bg-color: #212121;
--link-color: var(--accent-color); --link-color: var(--accent-color);
--link-visited-color: var(--accent-color-visited); --link-visited-color: var(--accent-color-visited);
--favorite-icon-color: #FFEA00;
--card-bg-color: #303030; --card-bg-color: #303030;
--secondary-card-bg-color: rgba(0, 0, 0, 0.75); --secondary-card-bg-color: rgba(0, 0, 0, 0.75);
--scrollbar-color: #414141; --scrollbar-color: #414141;
@ -55,6 +57,7 @@
--bg-color: #000000; --bg-color: #000000;
--link-color: var(--accent-color); --link-color: var(--accent-color);
--link-visited-color: var(--accent-color-visited); --link-visited-color: var(--accent-color-visited);
--favorite-icon-color: #FFEA00;
--card-bg-color: #000000; --card-bg-color: #000000;
--secondary-card-bg-color: rgba(0, 0, 0, 0.75); --secondary-card-bg-color: rgba(0, 0, 0, 0.75);
--scrollbar-color: #515151; --scrollbar-color: #515151;

View File

@ -1,6 +1,8 @@
import Vue from 'vue' import Vue from 'vue'
import FtCard from '../../components/ft-card/ft-card.vue' import FtCard from '../../components/ft-card/ft-card.vue'
import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue' import FtFlexBox from '../../components/ft-flex-box/ft-flex-box.vue'
import FtTooltip from '../../components/ft-tooltip/ft-tooltip.vue'
import FtLoader from '../../components/ft-loader/ft-loader.vue'
import FtElementList from '../../components/ft-element-list/ft-element-list.vue' import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
export default Vue.extend({ export default Vue.extend({
@ -8,8 +10,48 @@ export default Vue.extend({
components: { components: {
'ft-card': FtCard, 'ft-card': FtCard,
'ft-flex-box': FtFlexBox, 'ft-flex-box': FtFlexBox,
'ft-tooltip': FtTooltip,
'ft-loader': FtLoader,
'ft-element-list': FtElementList 'ft-element-list': FtElementList
}, },
data: function () {
return {
isLoading: false,
dataLimit: 100
}
},
computed: {
favoritesPlaylist: function () {
return this.$store.getters.getFavorites
},
activeData: function () {
if (this.favoritesPlaylist.videos.length < this.dataLimit) {
return this.favoritesPlaylist.videos
} else {
return this.favoritesPlaylist.videos.slice(0, this.dataLimit)
}
}
},
watch: {
activeData() {
this.isLoading = true
setTimeout(() => {
this.isLoading = false
}, 100)
}
},
mounted: function () { mounted: function () {
const limit = sessionStorage.getItem('favoritesLimit')
if (limit !== null) {
this.dataLimit = limit
}
},
methods: {
increaseLimit: function () {
this.dataLimit += 100
sessionStorage.setItem('favoritesLimit', this.dataLimit)
}
} }
}) })

View File

@ -1,12 +1,42 @@
<template> <template>
<div> <div>
<ft-card class="card"> <ft-loader
<h3>{{ $t("User Playlists.Your Playlists") }}</h3> v-if="isLoading"
<ft-flex-box> :fullscreen="true"
/>
<ft-card
v-else
class="card"
>
<h3>
{{ $t("User Playlists.Your Playlists") }}
<ft-tooltip
class="selectTooltip"
position="bottom"
:tooltip="$t('User Playlists.Playlist Message')"
/>
</h3>
<ft-flex-box
v-if="activeData.length === 0"
>
<p class="message"> <p class="message">
{{ $t("This part of the app is not ready yet. Come back later when progress has been made.") }} {{ $t("User Playlists['Your saved videos are empty. Click on the save button on the corner of a video to have it listed here']") }}
</p> </p>
</ft-flex-box> </ft-flex-box>
<ft-element-list
v-else
:data="activeData"
/>
<ft-flex-box
v-if="activeData.length < favoritesPlaylist.videos.length"
>
<ft-button
label="Load More"
background-color="var(--primary-color)"
text-color="var(--text-with-main-color)"
@click="increaseLimit"
/>
</ft-flex-box>
</ft-card> </ft-card>
</div> </div>
</template> </template>

View File

@ -81,6 +81,7 @@
:download-links="downloadLinks" :download-links="downloadLinks"
:watching-playlist="watchingPlaylist" :watching-playlist="watchingPlaylist"
:theatre-possible="theatrePossible" :theatre-possible="theatrePossible"
:length-seconds="videoLengthSeconds"
class="watchVideo" class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }" :class="{ theatreWatchVideo: useTheatreMode }"
@theatre-mode="toggleTheatreMode" @theatre-mode="toggleTheatreMode"

View File

@ -86,6 +86,8 @@ Most Popular: Most Popular
Playlists: Playlists Playlists: Playlists
User Playlists: User Playlists:
Your Playlists: Your Playlists Your Playlists: Your Playlists
Playlist Message: This page is not reflective of fully working playlists. It only lists videos that you have saved or favorited. When the work has finished, all videos currently here will be migrated to a 'Favorites' playlist.
Your saved videos are empty. Click on the save button on the corner of a video to have it listed here: Your saved videos are empty. Click on the save button on the corner of a video to have it listed here
History: History:
# On History Page # On History Page
History: History History: History
@ -368,6 +370,9 @@ Video:
Remove From History: Remove From History Remove From History: Remove From History
Video has been marked as watched: Video has been marked as watched Video has been marked as watched: Video has been marked as watched
Video has been removed from your history: Video has been removed from your history Video has been removed from your history: Video has been removed from your history
Save Video: Save Video
Video has been saved: Video has been saved
Video has been removed from your saved list: Video has been removed from your saved list
Open in YouTube: Open in YouTube Open in YouTube: Open in YouTube
Copy YouTube Link: Copy YouTube Link Copy YouTube Link: Copy YouTube Link
Open YouTube Embedded Player: Open YouTube Embedded Player Open YouTube Embedded Player: Open YouTube Embedded Player