Add full playlist functionality (Shuffle, loop, autoplay)
This commit is contained in:
parent
1faa075a7b
commit
8980dc74d2
|
@ -34,9 +34,9 @@ At this time, here is the list of things to do/need to do:
|
|||
- [x] Playlist View
|
||||
- [x] Video Watch Page (Recommendations, Comments)
|
||||
- [ ] Video player logic (Switching formats / quality, live video, fallback logic)
|
||||
- [ ] Playlist logic (Autoplay next video, shuffle list)
|
||||
- [ ] Database Setup and Logic (Updating and creating data)
|
||||
- [ ] Settings Page
|
||||
- [x] Playlist logic (Autoplay next video, shuffle list)
|
||||
- [x] Database Setup and Logic (Updating and creating data)
|
||||
- [x] Settings Page
|
||||
- [ ] Subscriptions Page and Logic
|
||||
- [ ] Playlists Page (Will allow for creating user playlists. Will replace the "Favorites" Page)
|
||||
- [ ] History Page and Logic
|
||||
|
|
File diff suppressed because it is too large
Load Diff
64
package.json
64
package.json
|
@ -11,18 +11,18 @@
|
|||
"@fortawesome/fontawesome-svg-core": "^1.2.28",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.13.0",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.9",
|
||||
"@silvermine/videojs-quality-selector": "^1.2.3",
|
||||
"@silvermine/videojs-quality-selector": "^1.2.4",
|
||||
"autolinker": "^3.14.1",
|
||||
"bulma-pro": "^0.1.8",
|
||||
"bulma-pro": "^0.2.0",
|
||||
"dateformat": "^3.0.3",
|
||||
"electron-context-menu": "^1.0.0",
|
||||
"jquery": "^3.5.0",
|
||||
"electron-context-menu": "^2.0.1",
|
||||
"jquery": "^3.5.1",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"material-design-icons": "^3.0.1",
|
||||
"mediaelement": "^4.2.16",
|
||||
"nedb": "^1.8.0",
|
||||
"opml-to-json": "0.0.3",
|
||||
"video.js": "^7.7.5",
|
||||
"video.js": "^7.7.6",
|
||||
"videojs-abloop": "^1.1.2",
|
||||
"videojs-contrib-quality-levels": "^2.0.9",
|
||||
"videojs-http-source-selector": "^1.1.6",
|
||||
|
@ -31,38 +31,38 @@
|
|||
"vue": "^2.6.11",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-router": "^3.1.6",
|
||||
"vuex": "^3.2.0",
|
||||
"vuex": "^3.4.0",
|
||||
"xml2json": "^0.12.0",
|
||||
"youtube-chat": "^1.0.2",
|
||||
"youtube-chat": "^1.1.0",
|
||||
"youtube-comments-fetch": "^1.0.1",
|
||||
"youtube-comments-task": "^1.3.14",
|
||||
"youtube-suggest": "^1.1.0",
|
||||
"yt-xml2vtt": "^1.0.1",
|
||||
"ytdl-core": "^2.1.0",
|
||||
"ytpl": "^0.1.20",
|
||||
"ytsr": "^0.1.12"
|
||||
"ytdl-core": "^2.1.2",
|
||||
"ytpl": "^0.1.21",
|
||||
"ytsr": "^0.1.13"
|
||||
},
|
||||
"description": "A private YouTube client",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/core": "^7.9.6",
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.9.5",
|
||||
"@babel/preset-env": "^7.9.5",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.9.6",
|
||||
"@babel/preset-env": "^7.9.6",
|
||||
"@babel/preset-typescript": "^7.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.29.0",
|
||||
"@typescript-eslint/parser": "^2.29.0",
|
||||
"acorn": "^7.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
"acorn": "^7.2.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^3.5.2",
|
||||
"copy-webpack-plugin": "^6.0.1",
|
||||
"css-loader": "^3.5.3",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^8.2.3",
|
||||
"electron-builder": "^22.5.1",
|
||||
"electron": "^8.3.0",
|
||||
"electron-builder": "^22.6.0",
|
||||
"electron-debug": "^3.0.1",
|
||||
"electron-rebuild": "^1.10.1",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.10.1",
|
||||
"electron-rebuild": "^1.11.0",
|
||||
"eslint": "^7.0.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
|
@ -72,26 +72,26 @@
|
|||
"eslint-plugin-vue": "^6.2.2",
|
||||
"fast-glob": "^3.2.2",
|
||||
"file-loader": "^6.0.0",
|
||||
"html-webpack-plugin": "^4.2.0",
|
||||
"jest": "^25.4.0",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"jest": "^26.0.1",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-loader": "^0.6.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.4",
|
||||
"sass": "^1.26.3",
|
||||
"prettier": "^2.0.5",
|
||||
"sass": "^1.26.5",
|
||||
"sass-loader": "^8.0.2",
|
||||
"style-loader": "^1.1.4",
|
||||
"style-loader": "^1.2.1",
|
||||
"tree-kill": "1.2.2",
|
||||
"typescript": "^3.8.3",
|
||||
"typescript": "^3.9.2",
|
||||
"url-loader": "^4.1.0",
|
||||
"vue-devtools": "^5.1.3",
|
||||
"vue-eslint-parser": "^7.0.0",
|
||||
"vue-loader": "^15.9.1",
|
||||
"vue-eslint-parser": "^7.1.0",
|
||||
"vue-loader": "^15.9.2",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.10.3"
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"main": "./dist/main.js",
|
||||
|
|
|
@ -7,6 +7,10 @@ export default Vue.extend({
|
|||
type: Object,
|
||||
required: true
|
||||
},
|
||||
playlistId: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
forceListType: {
|
||||
type: String,
|
||||
default: null
|
||||
|
@ -16,7 +20,6 @@ export default Vue.extend({
|
|||
return {
|
||||
id: '',
|
||||
title: '',
|
||||
thumbnail: '',
|
||||
channelName: '',
|
||||
channelId: '',
|
||||
viewCount: 0,
|
||||
|
@ -37,6 +40,19 @@ export default Vue.extend({
|
|||
|
||||
thumbnailPreference: function () {
|
||||
return this.$store.getters.getThumbnailPreference
|
||||
},
|
||||
|
||||
thumbnail: function () {
|
||||
switch (this.thumbnailPreference) {
|
||||
case 'start':
|
||||
return `https://i.ytimg.com/vi/${this.id}/mq1.jpg`
|
||||
case 'middle':
|
||||
return `https://i.ytimg.com/vi/${this.id}/mq2.jpg`
|
||||
case 'end':
|
||||
return `https://i.ytimg.com/vi/${this.id}/mq3.jpg`
|
||||
default:
|
||||
return `https://i.ytimg.com/vi/${this.id}/mqdefault.jpg`
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
|
@ -54,11 +70,27 @@ export default Vue.extend({
|
|||
},
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$router.push({ path: `/watch/${this.id}` })
|
||||
const playlistInfo = {
|
||||
playlistId: this.playlistId
|
||||
}
|
||||
console.log('playlist info')
|
||||
console.log(playlistInfo)
|
||||
|
||||
if (this.playlistId !== null) {
|
||||
console.log('Sending playlist info')
|
||||
this.$router.push(
|
||||
{
|
||||
path: `/watch/${this.id}`,
|
||||
query: playlistInfo
|
||||
}
|
||||
)
|
||||
} else {
|
||||
console.log('no playlist found')
|
||||
this.$router.push({ path: `/watch/${this.id}` })
|
||||
}
|
||||
},
|
||||
|
||||
goToChannel: function () {
|
||||
console.log(this.data)
|
||||
this.$router.push({ path: `/channel/${this.channelId}` })
|
||||
},
|
||||
|
||||
|
@ -101,20 +133,7 @@ export default Vue.extend({
|
|||
this.id = this.data.videoId
|
||||
this.title = this.data.title
|
||||
// this.thumbnail = this.data.videoThumbnails[4].url
|
||||
switch (this.thumbnailPreference) {
|
||||
case 'start':
|
||||
this.thumbnail = `https://i.ytimg.com/vi/${this.id}/mq1.jpg`
|
||||
break
|
||||
case 'middle':
|
||||
this.thumbnail = `https://i.ytimg.com/vi/${this.id}/mq2.jpg`
|
||||
break
|
||||
case 'end':
|
||||
this.thumbnail = `https://i.ytimg.com/vi/${this.id}/mq3.jpg`
|
||||
break
|
||||
default:
|
||||
this.thumbnail = `https://i.ytimg.com/vi/${this.id}/mqdefault.jpg`
|
||||
break
|
||||
}
|
||||
|
||||
this.channelName = this.data.author
|
||||
this.channelId = this.data.authorId
|
||||
this.duration = this.calculateVideoDuration(this.data.lengthSeconds)
|
||||
|
@ -142,22 +161,6 @@ export default Vue.extend({
|
|||
}
|
||||
|
||||
this.title = this.data.title
|
||||
// this.thumbnail = this.data.thumbnail
|
||||
|
||||
switch (this.thumbnailPreference) {
|
||||
case 'start':
|
||||
this.thumbnail = `https://i.ytimg.com/vi/${this.id}/mq1.jpg`
|
||||
break
|
||||
case 'middle':
|
||||
this.thumbnail = `https://i.ytimg.com/vi/${this.id}/mq2.jpg`
|
||||
break
|
||||
case 'end':
|
||||
this.thumbnail = `https://i.ytimg.com/vi/${this.id}/mq3.jpg`
|
||||
break
|
||||
default:
|
||||
this.thumbnail = `https://i.ytimg.com/vi/${this.id}/mqdefault.jpg`
|
||||
break
|
||||
}
|
||||
|
||||
if (typeof (this.data.author) === 'string') {
|
||||
this.channelName = this.data.author
|
||||
|
@ -188,7 +191,7 @@ export default Vue.extend({
|
|||
this.hideViews = true
|
||||
}
|
||||
|
||||
if (typeof (this.data.uploaded_at) !== 'undefined' && this.data.uploaded_at.includes('watching')) {
|
||||
if (typeof (this.data.uploaded_at) !== 'undefined' && this.data.uploaded_at !== null && this.data.uploaded_at.includes('watching')) {
|
||||
const uploadSplit = this.data.uploaded_at.split(' ')
|
||||
this.viewCount = parseInt(uploadSplit[0])
|
||||
this.isLive = true
|
||||
|
|
|
@ -37,7 +37,10 @@
|
|||
:style="{width: progressPercentage + '%'}"
|
||||
/>
|
||||
</div>
|
||||
<p class="videoTitle">
|
||||
<p
|
||||
class="videoTitle"
|
||||
@click="play(id)"
|
||||
>
|
||||
{{ title }}
|
||||
</p>
|
||||
<p
|
||||
|
@ -49,30 +52,35 @@
|
|||
<span
|
||||
v-if="!isLive && !hideViews"
|
||||
class="viewCount"
|
||||
@click="play(id)"
|
||||
>
|
||||
{{ viewCount }} views
|
||||
</span>
|
||||
<span
|
||||
v-if="uploadedTime !== '' && !isLive"
|
||||
class="uploadedTime"
|
||||
@click="play(id)"
|
||||
>
|
||||
- {{ uploadedTime }}
|
||||
</span>
|
||||
<span
|
||||
v-if="isLive"
|
||||
class="viewCount"
|
||||
@click="play(id)"
|
||||
>
|
||||
{{ viewCount }} watching
|
||||
</span>
|
||||
<p
|
||||
v-if="listType !== 'grid'"
|
||||
class="description"
|
||||
@click="play(id)"
|
||||
>
|
||||
{{ description }}
|
||||
</p>
|
||||
<span
|
||||
v-if="isLive"
|
||||
class="liveText"
|
||||
@click="play(id)"
|
||||
>
|
||||
LIVE NOW
|
||||
</span>
|
||||
|
|
|
@ -100,12 +100,80 @@ export default Vue.extend({
|
|||
return this.$store.getters.getDefaultPlayback
|
||||
},
|
||||
|
||||
defaultQuality: function () {
|
||||
return this.$store.getters.getDefaultQuality
|
||||
},
|
||||
|
||||
defaultVideoFormat: function () {
|
||||
return this.$store.getters.getDefaultVideoFormat
|
||||
},
|
||||
|
||||
autoplayVideos: function () {
|
||||
return this.$store.getters.getAutoplayVideos
|
||||
},
|
||||
|
||||
selectedDefaultQuality: function () {
|
||||
let selectedQuality = null
|
||||
|
||||
const maxAvailableQuality = parseInt(this.sourceList[this.sourceList.length - 1].qualityLabel.replace(/p|k/, ''))
|
||||
|
||||
switch (maxAvailableQuality) {
|
||||
case 4:
|
||||
if (this.defaultQuality >= 2160) {
|
||||
return '4k'
|
||||
}
|
||||
break
|
||||
case 8:
|
||||
if (this.defaultQuality >= 4320) {
|
||||
return '8k'
|
||||
}
|
||||
break
|
||||
case 144:
|
||||
if (this.defaultQuality >= 144) {
|
||||
return '144p'
|
||||
}
|
||||
break
|
||||
case 240:
|
||||
if (this.defaultQuality >= 240) {
|
||||
return '240p'
|
||||
}
|
||||
break
|
||||
case 360:
|
||||
if (this.defaultQuality >= 360) {
|
||||
return '360p'
|
||||
}
|
||||
break
|
||||
case 480:
|
||||
if (this.defaultQuality >= 480) {
|
||||
return '480p'
|
||||
}
|
||||
break
|
||||
case 720:
|
||||
if (this.defaultQuality >= 720) {
|
||||
return '720p'
|
||||
}
|
||||
break
|
||||
case 1080:
|
||||
if (this.defaultQuality >= 1080) {
|
||||
return '1080p'
|
||||
}
|
||||
break
|
||||
case 1440:
|
||||
if (this.defaultQuality >= 1440) {
|
||||
return '1440p'
|
||||
}
|
||||
break
|
||||
default:
|
||||
return maxAvailableQuality + 'p'
|
||||
}
|
||||
|
||||
this.activeSourceList.forEach((source) => {
|
||||
if (this.determineDefaultQuality(source.qualityLabel)) {
|
||||
selectedQuality = source.qualityLabel
|
||||
}
|
||||
})
|
||||
|
||||
return selectedQuality
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -172,6 +240,10 @@ export default Vue.extend({
|
|||
|
||||
const v = this
|
||||
|
||||
this.player.on('ended', function () {
|
||||
v.$emit('ended')
|
||||
})
|
||||
|
||||
this.player.on('error', function (error, message) {
|
||||
v.$emit('error', error.target.player.error_)
|
||||
})
|
||||
|
@ -191,6 +263,27 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
|
||||
determineDefaultQuality: function (label) {
|
||||
if (label.includes('p')) {
|
||||
const selectedQuality = parseInt(label.replace('p', ''))
|
||||
return this.defaultQuality === selectedQuality
|
||||
} else if (label.includes('k')) {
|
||||
const hdQuality = parseInt(label.replace('k', ''))
|
||||
|
||||
switch (hdQuality) {
|
||||
case 4:
|
||||
return this.defaultQuality === 2160
|
||||
case 8:
|
||||
return this.defaultQuality === 4320
|
||||
default:
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
console.log('Invalid label')
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
enableDashFormat: function () {
|
||||
if (this.dashSrc === null) {
|
||||
console.log('No dash format available.')
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
:src="source.url"
|
||||
:type="source.type || source.mimeType"
|
||||
:label="source.qualityLabel"
|
||||
:selected="source.qualityLabel === selectedDefaultQuality"
|
||||
/>
|
||||
<track
|
||||
v-for="(caption, index) in captionList"
|
||||
|
|
|
@ -42,15 +42,15 @@ export default Vue.extend({
|
|||
],
|
||||
qualityValues: [
|
||||
'auto',
|
||||
'144',
|
||||
'240',
|
||||
'360',
|
||||
'480',
|
||||
'720',
|
||||
'1080',
|
||||
'1440',
|
||||
'4k',
|
||||
'8k'
|
||||
144,
|
||||
240,
|
||||
360,
|
||||
480,
|
||||
720,
|
||||
1080,
|
||||
1440,
|
||||
2160,
|
||||
4320
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -16,8 +16,8 @@ export default Vue.extend({
|
|||
data: function () {
|
||||
return {
|
||||
id: '',
|
||||
randomVideoId: '',
|
||||
title: '',
|
||||
thumbnail: '',
|
||||
channelThumbnail: '',
|
||||
channelName: '',
|
||||
channelId: '',
|
||||
|
@ -25,6 +25,7 @@ export default Vue.extend({
|
|||
viewCount: 0,
|
||||
lastUpdated: '',
|
||||
description: '',
|
||||
infoSource: '',
|
||||
shareHeaders: [
|
||||
'Copy YouTube Link',
|
||||
'Open in YouTube',
|
||||
|
@ -42,17 +43,35 @@ export default Vue.extend({
|
|||
computed: {
|
||||
listType: function () {
|
||||
return this.$store.getters.getListType
|
||||
},
|
||||
|
||||
thumbnailPreference: function () {
|
||||
return this.$store.getters.getThumbnailPreference
|
||||
},
|
||||
|
||||
thumbnail: function () {
|
||||
switch (this.thumbnailPreference) {
|
||||
case 'start':
|
||||
return `https://i.ytimg.com/vi/${this.randomVideoId}/mq1.jpg`
|
||||
case 'middle':
|
||||
return `https://i.ytimg.com/vi/${this.randomVideoId}/mq2.jpg`
|
||||
case 'end':
|
||||
return `https://i.ytimg.com/vi/${this.randomVideoId}/mq3.jpg`
|
||||
default:
|
||||
return `https://i.ytimg.com/vi/${this.randomVideoId}/mqdefault.jpg`
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
console.log(this.data)
|
||||
this.id = this.data.id
|
||||
this.randomVideoId = this.data.randomVideoId
|
||||
this.title = this.data.title
|
||||
this.thumbnail = this.data.thumbnail
|
||||
this.channelName = this.data.channelName
|
||||
this.channelThumbnail = this.data.channelThumbnail
|
||||
this.uploadedTime = this.data.uploaded_at
|
||||
this.description = this.data.description
|
||||
this.infoSource = this.data.infoSource
|
||||
|
||||
// Causes errors if not put inside of a check
|
||||
if (typeof (this.data.viewCount) !== 'undefined') {
|
||||
|
|
|
@ -11,7 +11,11 @@
|
|||
{{ title }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ videoCount }} videos - {{ viewCount }} views - Last updated on {{ lastUpdated }}
|
||||
{{ videoCount }} videos - {{ viewCount }} views -
|
||||
<span v-if="infoSource !== 'local'">
|
||||
Last updated on
|
||||
</span>
|
||||
{{ lastUpdated }}
|
||||
</p>
|
||||
<p>
|
||||
{{ description }}
|
||||
|
|
|
@ -44,11 +44,11 @@ export default Vue.extend({
|
|||
},
|
||||
likeCount: {
|
||||
type: Number,
|
||||
required: true
|
||||
default: 0
|
||||
},
|
||||
dislikeCount: {
|
||||
type: Number,
|
||||
required: true
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
|
@ -88,6 +88,10 @@ export default Vue.extend({
|
|||
return this.$store.getters.getInvidiousInstance
|
||||
},
|
||||
|
||||
usingElectron: function () {
|
||||
return this.$store.getters.getUsingElectron
|
||||
},
|
||||
|
||||
invidiousUrl: function () {
|
||||
return `${this.invidiousInstance}/watch?v=${this.id}`
|
||||
},
|
||||
|
@ -118,7 +122,7 @@ export default Vue.extend({
|
|||
},
|
||||
methods: {
|
||||
goToChannel: function () {
|
||||
console.log('TODO: Handle goToChannel')
|
||||
this.$router.push({ path: `/channel/${this.channelId}` })
|
||||
},
|
||||
|
||||
handleSubscription: function () {
|
||||
|
@ -126,9 +130,6 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
handleFormatChange: function (format) {
|
||||
console.log('Handling share')
|
||||
console.log(this)
|
||||
|
||||
switch (format) {
|
||||
case 'dash':
|
||||
this.$parent.enableDashFormat()
|
||||
|
@ -147,19 +148,28 @@ export default Vue.extend({
|
|||
navigator.clipboard.writeText(this.youtubeUrl)
|
||||
break
|
||||
case 'openYoutube':
|
||||
// shell.openExternal(this.youtubeUrl)
|
||||
if (this.usingElectron) {
|
||||
const shell = require('electron').shell
|
||||
shell.openExternal(this.youtubeUrl)
|
||||
}
|
||||
break
|
||||
case 'copyYoutubeEmbed':
|
||||
navigator.clipboard.writeText(this.youtubeEmbedUrl)
|
||||
break
|
||||
case 'openYoutubeEmbed':
|
||||
// shell.openExternal(this.youtubeEmbedUrl)
|
||||
if (this.usingElectron) {
|
||||
const shell = require('electron').shell
|
||||
shell.openExternal(this.youtubeEmbedUrl)
|
||||
}
|
||||
break
|
||||
case 'copyInvidious':
|
||||
navigator.clipboard.writeText(this.invidiousUrl)
|
||||
break
|
||||
case 'openInvidious':
|
||||
// shell.openExternal(this.invidiousUrl)
|
||||
if (this.usingElectron) {
|
||||
const shell = require('electron').shell
|
||||
shell.openExternal(this.invidiousUrl)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
bottom: 15px;
|
||||
}
|
||||
|
||||
.playlistIndex {
|
||||
position: relative;
|
||||
bottom: 15px;
|
||||
color: var(--tertiary-text-color);
|
||||
}
|
||||
|
||||
.playlistIcon {
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
margin-top: -25px;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
color: var(--tertiary-text-color);
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
.playlistIcon:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.playlistIconActive {
|
||||
color: var(--accent-color)
|
||||
}
|
||||
|
||||
.playlistItems {
|
||||
overflow-y: auto;
|
||||
height: 395px;
|
||||
margin-top: -15px;
|
||||
margin-left: -16px;
|
||||
margin-right: -16px;
|
||||
}
|
||||
|
||||
.playlistItem {
|
||||
height: 75px;
|
||||
width: 100%;
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
.playlistItem:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.videoIndex {
|
||||
float: left;
|
||||
position: relative;
|
||||
top: 15px;
|
||||
left: 10px;
|
||||
color: var(--tertiary-text-color);
|
||||
}
|
||||
|
||||
.videoIndexIcon {
|
||||
float: left;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
top: 32px;
|
||||
left: 10px;
|
||||
color: var(--tertiary-text-color);
|
||||
}
|
||||
|
||||
.videoInfo {
|
||||
margin-left: 30px;
|
||||
position: relative;
|
||||
bottom: 7px;
|
||||
}
|
||||
|
||||
/deep/ .list {
|
||||
height: 60px;
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
|
||||
/deep/ .list .videoThumbnail {
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
/deep/ .list .videoThumbnail img {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
/deep/ .list .videoTitle {
|
||||
font-size: 12px;
|
||||
margin-left: 105px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
/deep/ .list .channelName {
|
||||
margin-left: 105px;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
/deep/ .list .viewCount {
|
||||
margin-left: 5px;
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
import Vue from 'vue'
|
||||
import FtLoader from '../ft-loader/ft-loader.vue'
|
||||
import FtCard from '../ft-card/ft-card.vue'
|
||||
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
|
||||
import FtListVideo from '../ft-list-video/ft-list-video.vue'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'WatchVideoPlaylist',
|
||||
components: {
|
||||
'ft-loader': FtLoader,
|
||||
'ft-card': FtCard,
|
||||
'ft-flex-box': FtFlexBox,
|
||||
'ft-list-video': FtListVideo
|
||||
},
|
||||
props: {
|
||||
playlistId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
videoId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
isLoading: false,
|
||||
shuffleEnabled: false,
|
||||
loopEnabled: false,
|
||||
channelName: '',
|
||||
channelId: '',
|
||||
channelThumbnail: '',
|
||||
playlistTitle: '',
|
||||
playlistItems: [],
|
||||
playlistWatchedVideoList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
usingElectron: function () {
|
||||
return this.$store.getters.getUsingElectron
|
||||
},
|
||||
|
||||
backendPreference: function () {
|
||||
return this.$store.getters.getBackendPreference
|
||||
},
|
||||
|
||||
backendFallback: function () {
|
||||
return this.$store.getters.getBackendFallback
|
||||
},
|
||||
|
||||
currentVideoIndex: function () {
|
||||
const index = this.playlistItems.findIndex((item) => {
|
||||
return item.videoId === this.videoId
|
||||
})
|
||||
|
||||
return index + 1
|
||||
},
|
||||
|
||||
playlistVideoCount: function () {
|
||||
return this.playlistItems.length
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
videoId () {
|
||||
this.playlistWatchedVideoList.push(this.videoId)
|
||||
},
|
||||
},
|
||||
mounted: function () {
|
||||
if (this.usingElectron) {
|
||||
this.getPlaylistInformationInvidious()
|
||||
} else {
|
||||
switch (this.backendPreference) {
|
||||
case 'local':
|
||||
this.getPlaylistInformationLocal()
|
||||
break
|
||||
case 'invidious':
|
||||
this.getPlaylistInformationInvidious()
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goToPlaylist: function () {
|
||||
this.$router.push({ path: `/playlist/${this.playlistId}` })
|
||||
},
|
||||
|
||||
goToChannel: function () {
|
||||
this.$router.push({ path: `/channel/${this.channelId}` })
|
||||
},
|
||||
|
||||
toggleLoop: function () {
|
||||
if (this.loopEnabled) {
|
||||
this.loopEnabled = false
|
||||
console.log('Disabling loop')
|
||||
} else {
|
||||
this.loopEnabled = true
|
||||
console.log('Enabling loop')
|
||||
}
|
||||
},
|
||||
|
||||
toggleShuffle: function () {
|
||||
if (this.shuffleEnabled) {
|
||||
this.shuffleEnabled = false
|
||||
console.log('Disabling shuffle')
|
||||
} else {
|
||||
this.shuffleEnabled = true
|
||||
console.log('Enabling shuffle')
|
||||
}
|
||||
},
|
||||
|
||||
playNextVideo: function () {
|
||||
const playlistInfo = {
|
||||
playlistId: this.playlistId
|
||||
}
|
||||
|
||||
const videoIndex = this.playlistItems.findIndex((item) => {
|
||||
return item.videoId === this.videoId
|
||||
})
|
||||
|
||||
const videosRemain = this.playlistWatchedVideoList.length < this.playlistItems.length
|
||||
|
||||
if (this.shuffleEnabled && videosRemain) {
|
||||
let runLoop = true
|
||||
while (runLoop) {
|
||||
const randomInt = Math.floor(Math.random() * this.playlistItems.length)
|
||||
const randomVideoId = this.playlistItems[randomInt].videoId
|
||||
|
||||
const watchedIndex = this.playlistWatchedVideoList.findIndex((watchedVideo) => {
|
||||
return watchedVideo === randomVideoId
|
||||
})
|
||||
|
||||
if (watchedIndex === -1) {
|
||||
runLoop = false
|
||||
this.$router.push(
|
||||
{
|
||||
path: `/watch/${randomVideoId}`,
|
||||
query: playlistInfo
|
||||
}
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if (this.shuffleEnabled && !videosRemain) {
|
||||
if (this.loopEnabled) {
|
||||
let runLoop = true
|
||||
while (runLoop) {
|
||||
const randomInt = Math.floor(Math.random() * this.playlistItems.length)
|
||||
const randomVideoId = this.playlistItems[randomInt].videoId
|
||||
|
||||
if (this.videoId !== randomVideoId) {
|
||||
this.playlistItems = []
|
||||
runLoop = false
|
||||
this.$router.push(
|
||||
{
|
||||
path: `/watch/${randomVideoId}`,
|
||||
query: playlistInfo
|
||||
}
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (this.loopEnabled && videoIndex === this.playlistItems.length - 1) {
|
||||
this.$router.push(
|
||||
{
|
||||
path: `/watch/${this.playlistItems[0].videoId}`,
|
||||
query: playlistInfo
|
||||
}
|
||||
)
|
||||
} else if (videoIndex < this.playlistItems.length - 1 && !videosRemain) {
|
||||
this.$router.push(
|
||||
{
|
||||
path: `/watch/${this.playlistItems[videoIndex + 1].videoId}`,
|
||||
query: playlistInfo
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
getPlaylistInformationLocal: function () {
|
||||
this.isLoading = true
|
||||
|
||||
this.$store.dispatch('ytGetPlaylistInfo', this.playlistId).then((result) => {
|
||||
console.log('done')
|
||||
console.log(result)
|
||||
|
||||
this.playlistTitle = result.title
|
||||
this.playlistItems = result.items
|
||||
this.videoCount = result.total_items
|
||||
this.channelName = result.author.name
|
||||
this.channelThumbnail = result.author.avatar
|
||||
this.channelId = result.author.id
|
||||
|
||||
this.playlistItems = result.items
|
||||
|
||||
this.playlistWatchedVideoList.push(this.videoId)
|
||||
this.isLoading = false
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'local' && this.backendFallback) {
|
||||
console.log('Falling back to Invidious API')
|
||||
this.getPlaylistInformationInvidious()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
// TODO: Show toast with error message
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getPlaylistInformationInvidious: function () {
|
||||
this.isLoading = true
|
||||
|
||||
const payload = {
|
||||
resource: 'playlists',
|
||||
id: this.playlistId,
|
||||
params: {
|
||||
page: this.playlistPage
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('invidiousGetPlaylistInfo', payload).then((result) => {
|
||||
console.log('done')
|
||||
console.log(result)
|
||||
|
||||
this.playlistTitle = result.title
|
||||
this.videoCount = result.videoCount
|
||||
this.channelName = result.author
|
||||
this.channelThumbnail = result.authorThumbnails[2].url
|
||||
this.channelId = result.authorId
|
||||
this.playlistItems = this.playlistItems.concat(result.videos)
|
||||
|
||||
if (this.playlistItems.length < result.videoCount) {
|
||||
console.log('getting next page')
|
||||
this.playlistPage++
|
||||
this.getPlaylistInformationInvidious()
|
||||
} else {
|
||||
this.playlistWatchedVideoList.push(this.videoId)
|
||||
this.isLoading = false
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'invidious' && this.backendFallback) {
|
||||
console.log('Error getting data with Invidious, falling back to local backend')
|
||||
this.getPlaylistInformationLocal()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
// TODO: Show toast with error message
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<ft-card class="relative">
|
||||
<ft-loader
|
||||
v-if="isLoading"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
<h3
|
||||
class="pointer"
|
||||
@click="goToPlaylist"
|
||||
>
|
||||
{{ playlistTitle }}
|
||||
</h3>
|
||||
<span
|
||||
class="channelName"
|
||||
@click="goToChannel"
|
||||
>
|
||||
{{ channelName }}
|
||||
</span>
|
||||
<span
|
||||
class="playlistIndex"
|
||||
>
|
||||
- {{ currentVideoIndex }} / {{ playlistVideoCount }}
|
||||
</span>
|
||||
<p>
|
||||
<font-awesome-icon
|
||||
class="playlistIcon"
|
||||
:class="{ playlistIconActive: loopEnabled }"
|
||||
icon="retweet"
|
||||
@click="toggleLoop"
|
||||
/>
|
||||
<font-awesome-icon
|
||||
class="playlistIcon"
|
||||
:class="{ playlistIconActive: shuffleEnabled }"
|
||||
icon="random"
|
||||
@click="toggleShuffle"
|
||||
/>
|
||||
</p>
|
||||
<ft-flex-box
|
||||
v-if="!isLoading"
|
||||
class="playlistItems"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in playlistItems"
|
||||
:key="index"
|
||||
class="playlistItem"
|
||||
>
|
||||
<font-awesome-icon
|
||||
v-if="item.videoId === videoId"
|
||||
class="videoIndexIcon"
|
||||
icon="play"
|
||||
/>
|
||||
<p
|
||||
v-else
|
||||
class="videoIndex"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</p>
|
||||
<ft-list-video
|
||||
:data="item"
|
||||
:playlist-id="playlistId"
|
||||
force-list-type="list"
|
||||
class="videoInfo"
|
||||
/>
|
||||
</div>
|
||||
</ft-flex-box>
|
||||
</div>
|
||||
</ft-card>
|
||||
</template>
|
||||
|
||||
<script src="./watch-video-playlist.js" />
|
||||
<style scoped src="./watch-video-playlist.css" />
|
|
@ -5,3 +5,29 @@
|
|||
.videoRecommendation {
|
||||
margin-bottom: -15px;
|
||||
}
|
||||
|
||||
/deep/ .list {
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
/deep/ .list .videoThumbnail {
|
||||
width: 180px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
/deep/ .list .videoThumbnail img {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
/deep/ .list .videoTitle {
|
||||
font-size: 12px;
|
||||
margin-left: 185px;
|
||||
}
|
||||
|
||||
/deep/ .list .channelName {
|
||||
margin-left: 185px;
|
||||
}
|
||||
|
||||
/deep/ .list .viewCount {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
|
|
@ -14,11 +14,6 @@ export default Vue.extend({
|
|||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
test: 'hello'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
listType: function () {
|
||||
return this.$store.getters.getListType
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import Datastore from 'nedb'
|
||||
|
||||
let dbLocation
|
||||
|
||||
if (window && window.process && window.process.type === 'renderer') {
|
||||
// Electron is being used
|
||||
let dbLocation = localStorage.getItem('dbLocation')
|
||||
|
||||
if (dbLocation === null) {
|
||||
const electron = require('electron')
|
||||
dbLocation = electron.remote.app.getPath('userData')
|
||||
}
|
||||
|
||||
dbLocation += '/playlists.db'
|
||||
} else {
|
||||
dbLocation = 'playlists.db'
|
||||
}
|
||||
|
||||
const subDb = new Datastore({
|
||||
filename: dbLocation,
|
||||
autoload: true
|
||||
})
|
||||
|
||||
const state = {
|
||||
activePlaylistId: '',
|
||||
activePlaylistVideoList: [],
|
||||
watchedVideosWithinPlaylist: []
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
addSubscription (state, payload) {
|
||||
state.subscriptions.push(payload)
|
||||
},
|
||||
setSubscriptions (state, payload) {
|
||||
state.subscriptions = payload
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
addSubscriptions ({ commit }, payload) {
|
||||
subDb.insert(payload, (err, payload) => {
|
||||
if (!err) {
|
||||
commit('addSubscription', payload)
|
||||
}
|
||||
})
|
||||
},
|
||||
getSubscriptions ({ commit }, payload) {
|
||||
subDb.find({}, (err, payload) => {
|
||||
if (!err) {
|
||||
commit('setSubscriptions', payload)
|
||||
}
|
||||
})
|
||||
},
|
||||
removeSubscription ({ commit }, channelId) {
|
||||
subDb.remove({ channelId: channelId }, {}, () => {
|
||||
commit('setSubscriptions', this.state.subscriptions.filter(sub => sub.channelId !== channelId))
|
||||
})
|
||||
}
|
||||
}
|
||||
const getters = {}
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
|
@ -118,7 +118,7 @@ const actions = {
|
|||
return new Promise((resolve, reject) => {
|
||||
console.log(playlistId)
|
||||
console.log('Getting playlist info please wait...')
|
||||
ytpl(playlistId, (err, result) => {
|
||||
ytpl(playlistId, { limit: 0 }, (err, result) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@charset "UTF-8";
|
||||
.vjs-modal-dialog .vjs-modal-dialog-content, .video-js .vjs-modal-dialog, .vjs-button > .vjs-icon-placeholder:before, .video-js .vjs-big-play-button .vjs-icon-placeholder:before {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
top: 0px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
@ -18,7 +18,7 @@ export default Vue.extend({
|
|||
data: function () {
|
||||
return {
|
||||
isLoading: false,
|
||||
playlistId: '',
|
||||
playlistId: null,
|
||||
nextPageRef: '',
|
||||
lastSearchQuery: '',
|
||||
playlistPage: 1,
|
||||
|
@ -82,13 +82,14 @@ export default Vue.extend({
|
|||
id: result.id,
|
||||
title: result.title,
|
||||
description: result.description,
|
||||
thumbnail: result.items[randomVideoIndex].thumbnail,
|
||||
randomVideoId: result.items[randomVideoIndex].id,
|
||||
viewCount: result.views,
|
||||
videoCount: result.total_items,
|
||||
lastUpdated: result.last_updated,
|
||||
channelName: result.author.name,
|
||||
channelThumbnail: result.author.avatar,
|
||||
channelId: result.author.id
|
||||
channelId: result.author.id,
|
||||
infoSource: 'local'
|
||||
}
|
||||
|
||||
this.playlistItems = result.items
|
||||
|
@ -127,12 +128,13 @@ export default Vue.extend({
|
|||
id: result.playlistId,
|
||||
title: result.title,
|
||||
description: result.description,
|
||||
thumbnail: result.videos[randomVideoIndex].videoThumbnails[0].url,
|
||||
randomVideoId: result.videos[randomVideoIndex].videoId,
|
||||
viewCount: result.viewCount,
|
||||
videoCount: result.videoCount,
|
||||
channelName: result.author,
|
||||
channelThumbnail: result.authorThumbnails[2].url,
|
||||
channelId: result.authorId
|
||||
channelId: result.authorId,
|
||||
infoSource: 'invidious'
|
||||
}
|
||||
|
||||
const dateString = new Date(result.updated * 1000)
|
||||
|
|
|
@ -9,18 +9,21 @@
|
|||
:data="infoData"
|
||||
class="playlistInfo"
|
||||
/>
|
||||
<ft-flex-box
|
||||
<ft-card
|
||||
v-if="!isLoading"
|
||||
class="playlistItems"
|
||||
>
|
||||
<ft-list-video
|
||||
v-for="(item, index) in playlistItems"
|
||||
:key="index"
|
||||
:data="item"
|
||||
force-list-type="list"
|
||||
class="playlistItem"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-flex-box>
|
||||
<ft-list-video
|
||||
v-for="(item, index) in playlistItems"
|
||||
:key="index"
|
||||
:data="item"
|
||||
:playlist-id="playlistId"
|
||||
force-list-type="list"
|
||||
class="playlistItem"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
</ft-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -27,14 +27,40 @@
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.watchVideoRecommendations {
|
||||
.watchVideoSideBar {
|
||||
width: 27%;
|
||||
max-width: 425px;
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
}
|
||||
|
||||
.watchVideoPlaylist {
|
||||
right: 10px;
|
||||
top: 70px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.theatrePlaylist {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
width: 85%;
|
||||
height: 500px;
|
||||
margin-bottom: 10px;
|
||||
max-width: none;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.watchVideoRecommendations {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.watchVideoRecommendationsNoCard {
|
||||
top: 70px;
|
||||
}
|
||||
|
||||
.watchVideoRecommendationsLowerCard {
|
||||
top: 600px;
|
||||
}
|
||||
|
||||
.theatreRecommendations {
|
||||
|
@ -78,6 +104,14 @@
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.watchVideoPlaylist {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
width: 85%;
|
||||
max-width: none;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.watchVideoRecommendations {
|
||||
float: none;
|
||||
margin: 0 auto;
|
||||
|
|
|
@ -8,6 +8,7 @@ import FtVideoPlayer from '../../components/ft-video-player/ft-video-player.vue'
|
|||
import WatchVideoInfo from '../../components/watch-video-info/watch-video-info.vue'
|
||||
import WatchVideoDescription from '../../components/watch-video-description/watch-video-description.vue'
|
||||
import WatchVideoComments from '../../components/watch-video-comments/watch-video-comments.vue'
|
||||
import WatchVideoPlaylist from '../../components/watch-video-playlist/watch-video-playlist.vue'
|
||||
import WatchVideoRecommendations from '../../components/watch-video-recommendations/watch-video-recommendations.vue'
|
||||
|
||||
export default Vue.extend({
|
||||
|
@ -20,6 +21,7 @@ export default Vue.extend({
|
|||
'watch-video-info': WatchVideoInfo,
|
||||
'watch-video-description': WatchVideoDescription,
|
||||
'watch-video-comments': WatchVideoComments,
|
||||
'watch-video-playlist': WatchVideoPlaylist,
|
||||
'watch-video-recommendations': WatchVideoRecommendations,
|
||||
},
|
||||
data: function() {
|
||||
|
@ -31,6 +33,7 @@ export default Vue.extend({
|
|||
showLegacyPlayer: false,
|
||||
showYouTubeNoCookieEmbed: false,
|
||||
hidePlayer: false,
|
||||
isLive: false,
|
||||
activeFormat: 'legacy',
|
||||
videoId: '',
|
||||
videoTitle: '',
|
||||
|
@ -49,6 +52,8 @@ export default Vue.extend({
|
|||
videoSourceList: [],
|
||||
captionSourceList: [],
|
||||
recommendedVideos: [],
|
||||
watchingPlaylist: false,
|
||||
playlistId: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -111,6 +116,8 @@ export default Vue.extend({
|
|||
|
||||
this.firstLoad = true
|
||||
|
||||
this.checkIfPlaylist()
|
||||
|
||||
switch (this.backendPreference) {
|
||||
case 'local':
|
||||
this.getVideoInformationLocal(this.videoId)
|
||||
|
@ -125,13 +132,15 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
},
|
||||
mounted: function() {
|
||||
mounted: function () {
|
||||
this.videoId = this.$route.params.id
|
||||
this.videoStoryboardSrc = `${this.invidiousInstance}/api/v1/storyboards/${this.videoId}?height=90`
|
||||
|
||||
this.activeFormat = this.defaultVideoFormat
|
||||
this.useTheatreMode = this.defaultTheatreMode
|
||||
|
||||
this.checkIfPlaylist()
|
||||
|
||||
if (!this.usingElectron) {
|
||||
this.getVideoInformationInvidious()
|
||||
} else {
|
||||
|
@ -158,6 +167,7 @@ export default Vue.extend({
|
|||
this.$store
|
||||
.dispatch('ytGetVideoInformation', this.videoId)
|
||||
.then(result => {
|
||||
console.log(result)
|
||||
this.videoTitle = result.title
|
||||
this.videoViewCount = parseInt(
|
||||
result.player_response.videoDetails.viewCount,
|
||||
|
@ -170,9 +180,24 @@ export default Vue.extend({
|
|||
this.videoDescription =
|
||||
result.player_response.videoDetails.shortDescription
|
||||
this.recommendedVideos = result.related_videos
|
||||
this.videoSourceList = result.player_response.streamingData.formats
|
||||
this.videoLikeCount = result.likes
|
||||
this.videoDislikeCount = result.dislikes
|
||||
this.isLive = result.player_response.videoDetails.isLive
|
||||
|
||||
if (this.isLive) {
|
||||
this.showLegacyPlayer = false
|
||||
this.showDashPlayer = true
|
||||
this.videoSourceList = [
|
||||
{
|
||||
url: 'https://invidious.snopyta.org/api/manifest/dash/id/EEIk7gwjgIM',
|
||||
type: 'application/dash+xml',
|
||||
label: 'Dash',
|
||||
qualityLabel: 'Auto'
|
||||
},
|
||||
]
|
||||
} else {
|
||||
this.videoSourceList = result.player_response.streamingData.formats
|
||||
}
|
||||
|
||||
// The response provides a storyboard, however it returns a 403 error.
|
||||
// Uncomment this line if that ever changes.
|
||||
|
@ -275,6 +300,22 @@ export default Vue.extend({
|
|||
})
|
||||
},
|
||||
|
||||
checkIfPlaylist: function () {
|
||||
if (typeof (this.$route.query) !== 'undefined') {
|
||||
console.log('defined')
|
||||
console.log(this.$route.query)
|
||||
this.playlistId = this.$route.query.playlistId
|
||||
|
||||
if (typeof (this.playlistId) !== 'undefined') {
|
||||
this.watchingPlaylist = true
|
||||
} else {
|
||||
this.watchingPlaylist = false
|
||||
}
|
||||
} else {
|
||||
this.watchingPlaylist = false
|
||||
}
|
||||
},
|
||||
|
||||
getLegacyFormats: function () {
|
||||
this.$store
|
||||
.dispatch('ytGetVideoInformation', this.videoId)
|
||||
|
@ -296,7 +337,7 @@ export default Vue.extend({
|
|||
}, 100)
|
||||
},
|
||||
|
||||
enableLegacyFormat: function() {
|
||||
enableLegacyFormat: function () {
|
||||
if (this.activeFormat === 'legacy') {
|
||||
return
|
||||
}
|
||||
|
@ -309,6 +350,15 @@ export default Vue.extend({
|
|||
}, 100)
|
||||
},
|
||||
|
||||
handleVideoEnded: function () {
|
||||
if (this.watchingPlaylist) {
|
||||
console.log('Playlist next video in 5 seconds')
|
||||
setTimeout(() => {
|
||||
this.$refs.watchVideoPlaylist.playNextVideo()
|
||||
}, 5000)
|
||||
}
|
||||
},
|
||||
|
||||
handleVideoError: function(error) {
|
||||
console.log(error)
|
||||
if (error.code === 4) {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
class="videoPlayer"
|
||||
:class="{ theatrePlayer: useTheatreMode }"
|
||||
ref="videoPlayer"
|
||||
@ended="handleVideoEnded"
|
||||
@error="handleVideoError"
|
||||
/>
|
||||
<watch-video-info
|
||||
|
@ -45,11 +46,24 @@
|
|||
class="watchVideo"
|
||||
:class="{ theatreWatchVideo: useTheatreMode }"
|
||||
/>
|
||||
<watch-video-playlist
|
||||
v-if="watchingPlaylist"
|
||||
v-show="!isLoading"
|
||||
:playlist-id="playlistId"
|
||||
:video-id="videoId"
|
||||
ref="watchVideoPlaylist"
|
||||
class="watchVideoSideBar watchVideoPlaylist"
|
||||
:class="{ theatrePlaylist: useTheatreMode }"
|
||||
/>
|
||||
<watch-video-recommendations
|
||||
v-if="!isLoading"
|
||||
:data="recommendedVideos"
|
||||
class="watchVideoRecommendations"
|
||||
:class="{ theatreRecommendations: useTheatreMode }"
|
||||
class="watchVideoSideBar watchVideoRecommendations"
|
||||
:class="{
|
||||
theatreRecommendations: useTheatreMode,
|
||||
watchVideoRecommendationsLowerCard: watchingPlaylist,
|
||||
watchVideoRecommendationsNoCard: !watchingPlaylist
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue