From affa225fae01891a6ae7d7769cd70945c9da68eb Mon Sep 17 00:00:00 2001 From: root Date: Wed, 15 Dec 2021 13:22:12 +0530 Subject: [PATCH] Reformat --- .editorconfig | 5 - .idea/codeStyles/Project.xml | 4 +- package.json | 50 ++- src/App.vue | 180 ++++---- src/components/AuthenticationModal.vue | 82 ++-- src/components/ErrorHandler.vue | 14 +- src/components/ExpandableDate.vue | 20 +- src/components/GenericDisplayItem.vue | 22 +- src/components/Player.vue | 542 ++++++++++++------------- src/components/SubscriptionButton.vue | 86 ++-- src/components/VideoComment.vue | 78 ++-- src/components/VideoItem.vue | 104 ++--- src/components/VideoRedirect.vue | 18 +- src/main.js | 10 +- src/plugins/i18n.js | 140 +++---- src/plugins/keybindings.js | 18 +- src/plugins/vuetify.js | 70 ++-- src/registerServiceWorker.js | 52 +-- src/router/index.js | 110 ++--- src/routes/Channel.vue | 186 ++++----- src/routes/FeedPage.vue | 170 ++++---- src/routes/Playlist.vue | 68 ++-- src/routes/Preferences.vue | 374 ++++++++--------- src/routes/SearchMenu.vue | 114 +++--- src/routes/SearchResults.vue | 204 +++++----- src/routes/Subscriptions.vue | 56 +-- src/routes/WatchHistory.vue | 82 ++-- src/routes/WatchVideo.vue | 410 +++++++++---------- src/store/authentication-store.js | 218 +++++----- src/store/index.js | 8 +- src/store/prefs-store.js | 152 +++---- src/store/watched-videos-db.js | 34 +- src/tools/DashUtils.js | 356 ++++++++-------- src/tools/libpiped.js | 122 +++--- vue.config.js | 44 +- 35 files changed, 2129 insertions(+), 2074 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 7053c49..0000000 --- a/.editorconfig +++ /dev/null @@ -1,5 +0,0 @@ -[*.{js,jsx,ts,tsx,vue}] -indent_style = space -indent_size = 2 -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 7c550ac..bb1482f 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -35,6 +35,7 @@ + diff --git a/package.json b/package.json index 9c8d18f..6fd7f36 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,55 @@ "parserOptions": { "parser": "@babel/eslint-parser" }, - "rules": {} + "rules": { + "no-tabs": "off", + "indent": [ + "error", + "tab", + { + "SwitchCase": 1, + "VariableDeclarator": 1, + "outerIIFEBody": 1, + "MemberExpression": 1, + "FunctionDeclaration": { + "parameters": 1, + "body": 1 + }, + "FunctionExpression": { + "parameters": 1, + "body": 1 + }, + "CallExpression": { + "arguments": 1 + }, + "ArrayExpression": 1, + "ObjectExpression": 1, + "ImportDeclaration": 1, + "flatTernaryExpressions": false, + "ignoreComments": false, + "ignoredNodes": [ + "TemplateLiteral *", + "JSXElement", + "JSXElement > *", + "JSXAttribute", + "JSXIdentifier", + "JSXNamespacedName", + "JSXMemberExpression", + "JSXSpreadAttribute", + "JSXExpressionContainer", + "JSXOpeningElement", + "JSXClosingElement", + "JSXFragment", + "JSXOpeningFragment", + "JSXClosingFragment", + "JSXText", + "JSXEmptyExpression", + "JSXSpreadChild" + ], + "offsetTernaryExpressions": true + } + ] + } }, "browserslist": [ "> 1% in US", diff --git a/src/App.vue b/src/App.vue index c63d9e4..3753cbb 100644 --- a/src/App.vue +++ b/src/App.vue @@ -95,104 +95,104 @@ import { changeLocale } from '@/plugins/i18n' import AuthenticationModal from '@/components/AuthenticationModal' export default { - name: 'App', - components: { - AuthenticationModal, - SearchMenu - }, - metaInfo () { - return { - title: this.$t('titles.homepage'), - // all titles will be injected into this template - titleTemplate: '%s - Piped' - } - }, + name: 'App', + components: { + AuthenticationModal, + SearchMenu + }, + metaInfo () { + return { + title: this.$t('titles.homepage'), + // all titles will be injected into this template + titleTemplate: '%s - Piped' + } + }, - data: () => ({ - languageOptions: [ - { value: 'zh_Hant', text: 'Chinese (Traditional)' }, - { value: 'en', text: 'English' }, - { value: 'fr', text: 'French' }, - { value: 'de', text: 'German' }, - { value: 'el', text: 'Greek' }, - { value: 'zh_Hans', text: 'Chinese (Simplified, only loads the font)' }, - { value: 'jp', text: 'Japanese (only loads fonts)' }, - { value: 'kr', text: 'Korean (only loads fonts)' }, - { value: 'cyrl', text: 'Cyrillic Languages (fonts)' }, - { value: 'lt', text: 'Lithuanian' }, - { value: 'ml', text: 'Malayalam' }, - { value: 'nb_NO', text: 'Norwegian Bokmål' }, - { value: 'tr', text: 'Turkish' }, - { value: 'bn_beng', text: 'Bengali (বাংলা)' } - // Incomplete, DO NOT USE - /* { value: 'bn_latn', text: 'Bengali (Latin)' }, */ - ].sort((a, b) => { - return a.text.localeCompare(b.text) - }), - drawer: false - }), + data: () => ({ + languageOptions: [ + { value: 'zh_Hant', text: 'Chinese (Traditional)' }, + { value: 'en', text: 'English' }, + { value: 'fr', text: 'French' }, + { value: 'de', text: 'German' }, + { value: 'el', text: 'Greek' }, + { value: 'zh_Hans', text: 'Chinese (Simplified, only loads the font)' }, + { value: 'jp', text: 'Japanese (only loads fonts)' }, + { value: 'kr', text: 'Korean (only loads fonts)' }, + { value: 'cyrl', text: 'Cyrillic Languages (fonts)' }, + { value: 'lt', text: 'Lithuanian' }, + { value: 'ml', text: 'Malayalam' }, + { value: 'nb_NO', text: 'Norwegian Bokmål' }, + { value: 'tr', text: 'Turkish' }, + { value: 'bn_beng', text: 'Bengali (বাংলা)' } + // Incomplete, DO NOT USE + /* { value: 'bn_latn', text: 'Bengali (Latin)' }, */ + ].sort((a, b) => { + return a.text.localeCompare(b.text) + }), + drawer: false + }), - computed: { - bgStyles () { - return { - backgroundColor: this.$vuetify.theme.dark ? '#282828' : '#fbf1c7' - } - }, + computed: { + bgStyles () { + return { + backgroundColor: this.$vuetify.theme.dark ? '#282828' : '#fbf1c7' + } + }, - links () { - const links = [ - { - id: 'trending', - name: 'titles.trending', - to: '/trending' - }, - { - id: 'prefs', - name: 'titles.preferences', - to: '/preferences' - }, - { - id: 'watch-history', - name: 'titles.history', - to: '/watch-history' - } - ] + links () { + const links = [ + { + id: 'trending', + name: 'titles.trending', + to: '/trending' + }, + { + id: 'prefs', + name: 'titles.preferences', + to: '/preferences' + }, + { + id: 'watch-history', + name: 'titles.history', + to: '/watch-history' + } + ] - if (this.$store.getters['auth/isCurrentlyAuthenticated']) { - links.splice(0, 0, { - id: 'feed', - name: 'titles.feed', - to: '/feed' - }) - } + if (this.$store.getters['auth/isCurrentlyAuthenticated']) { + links.splice(0, 0, { + id: 'feed', + name: 'titles.feed', + to: '/feed' + }) + } - return links - } - }, + return links + } + }, - watch: { - '$store.state.prefs.prefs.darkMode' (newVal) { - this.$vuetify.theme.dark = newVal - } - }, + watch: { + '$store.state.prefs.prefs.darkMode' (newVal) { + this.$vuetify.theme.dark = newVal + } + }, - methods: { - changeLocale (lang) { - return changeLocale(lang) - }, + methods: { + changeLocale (lang) { + return changeLocale(lang) + }, - toggleDarkMode () { - this.$vuetify.theme.dark = !this.$vuetify.theme.dark - this.$store.commit('prefs/setPrefs', { - id: 'darkMode', - value: this.$vuetify.theme.dark - }) - } - }, + toggleDarkMode () { + this.$vuetify.theme.dark = !this.$vuetify.theme.dark + this.$store.commit('prefs/setPrefs', { + id: 'darkMode', + value: this.$vuetify.theme.dark + }) + } + }, - created () { - this.$store.dispatch('prefs/loadState') - this.$store.dispatch('auth/initializeAuth') - } + created () { + this.$store.dispatch('prefs/loadState') + this.$store.dispatch('auth/initializeAuth') + } } diff --git a/src/components/AuthenticationModal.vue b/src/components/AuthenticationModal.vue index 88b294b..1e5cc95 100644 --- a/src/components/AuthenticationModal.vue +++ b/src/components/AuthenticationModal.vue @@ -47,50 +47,50 @@ import { AuthenticationError } from '@/store/authentication-store' export default { - name: 'AuthenticationModal', - props: ['listMode'], - data: () => ({ - error: null, - requestInProgress: false, - dialogOpen: false, - username: '', - password: '' - }), - methods: { - async doCall (path) { - this.error = null - try { - this.requestInProgress = true - await this.$store.dispatch('auth/loginOrRegister', { - path, - username: this.username, - password: this.password - }) - this.dialogOpen = false - } catch (e) { - if (!(e instanceof AuthenticationError)) { - throw e - } + name: 'AuthenticationModal', + props: ['listMode'], + data: () => ({ + error: null, + requestInProgress: false, + dialogOpen: false, + username: '', + password: '' + }), + methods: { + async doCall (path) { + this.error = null + try { + this.requestInProgress = true + await this.$store.dispatch('auth/loginOrRegister', { + path, + username: this.username, + password: this.password + }) + this.dialogOpen = false + } catch (e) { + if (!(e instanceof AuthenticationError)) { + throw e + } - this.error = e.message - } finally { - this.requestInProgress = false - } - }, + this.error = e.message + } finally { + this.requestInProgress = false + } + }, - logOut () { - this.$store.commit('auth/deleteAuthToken', { - apiURL: this.$store.getters['prefs/apiUrl'] - }) - }, + logOut () { + this.$store.commit('auth/deleteAuthToken', { + apiURL: this.$store.getters['prefs/apiUrl'] + }) + }, - login () { - return this.doCall('login') - }, + login () { + return this.doCall('login') + }, - register () { - return this.doCall('register') - } - } + register () { + return this.doCall('register') + } + } } diff --git a/src/components/ErrorHandler.vue b/src/components/ErrorHandler.vue index 2f798fd..cc7fbf5 100644 --- a/src/components/ErrorHandler.vue +++ b/src/components/ErrorHandler.vue @@ -8,12 +8,12 @@ diff --git a/src/components/ExpandableDate.vue b/src/components/ExpandableDate.vue index c03442e..eeb61ba 100644 --- a/src/components/ExpandableDate.vue +++ b/src/components/ExpandableDate.vue @@ -13,16 +13,16 @@ import { LibPiped } from '@/tools/libpiped' export default { - name: 'ExpandableDate', - props: ['date'], - computed: { - timeAgo () { - return LibPiped.timeAgo(this.date) - }, + name: 'ExpandableDate', + props: ['date'], + computed: { + timeAgo () { + return LibPiped.timeAgo(this.date) + }, - formattedDate () { - return LibPiped.formatFullDate(this.date) - } - } + formattedDate () { + return LibPiped.formatFullDate(this.date) + } + } } diff --git a/src/components/GenericDisplayItem.vue b/src/components/GenericDisplayItem.vue index 649f155..c1e188d 100644 --- a/src/components/GenericDisplayItem.vue +++ b/src/components/GenericDisplayItem.vue @@ -22,16 +22,16 @@ import { isNumber } from 'lodash-es' export default { - props: { - item: Object, - height: Number, - width: Number, - hideChannel: Boolean - }, - methods: { - isNumber (...args) { - return isNumber(...args) - } - } + props: { + item: Object, + height: Number, + width: Number, + hideChannel: Boolean + }, + methods: { + isNumber (...args) { + return isNumber(...args) + } + } } diff --git a/src/components/Player.vue b/src/components/Player.vue index c9e6f16..fcb5c35 100644 --- a/src/components/Player.vue +++ b/src/components/Player.vue @@ -43,310 +43,310 @@ import { setupKeybindings } from '@/plugins/keybindings' window.muxjs = muxjs export default { - props: { - video: Object, - sponsors: Object, - skipToTime: Number, - selectedAutoLoop: Boolean - }, - data () { - return { - $player: null, - $ui: null - } - }, - computed: { - shouldAutoPlay () { - return this.$store.getters['prefs/getPreferenceBoolean']('playerAutoPlay', true) - }, + props: { + video: Object, + sponsors: Object, + skipToTime: Number, + selectedAutoLoop: Boolean + }, + data () { + return { + $player: null, + $ui: null + } + }, + computed: { + shouldAutoPlay () { + return this.$store.getters['prefs/getPreferenceBoolean']('playerAutoPlay', true) + }, - preferredVideoCodecs () { - const preferredVideoCodecs = [] - if (this.$refs.videoEl.canPlayType('video/mp4; codecs="av01.0.08M.08"') !== '') { preferredVideoCodecs.push('av01') } - if (this.$refs.videoEl.canPlayType('video/webm; codecs="vp9"') !== '') preferredVideoCodecs.push('vp9') - if (this.$refs.videoEl.canPlayType('video/mp4; codecs="avc1.4d401f"') !== '') { preferredVideoCodecs.push('avc1') } - return preferredVideoCodecs - } - }, - methods: { - getCurrentTime () { - // UNCLEAN - return this.$refs.videoEl ? this.$refs.videoEl.currentTime : undefined - }, + preferredVideoCodecs () { + const preferredVideoCodecs = [] + if (this.$refs.videoEl.canPlayType('video/mp4; codecs="av01.0.08M.08"') !== '') { preferredVideoCodecs.push('av01') } + if (this.$refs.videoEl.canPlayType('video/webm; codecs="vp9"') !== '') preferredVideoCodecs.push('vp9') + if (this.$refs.videoEl.canPlayType('video/mp4; codecs="avc1.4d401f"') !== '') { preferredVideoCodecs.push('avc1') } + return preferredVideoCodecs + } + }, + methods: { + getCurrentTime () { + // UNCLEAN + return this.$refs.videoEl ? this.$refs.videoEl.currentTime : undefined + }, - async loadVideo () { - console.log('PIPED | LOADING VIDEO') - const component = this - const videoEl = this.$refs.videoEl + async loadVideo () { + console.log('PIPED | LOADING VIDEO') + const component = this + const videoEl = this.$refs.videoEl - videoEl.setAttribute('poster', this.video.thumbnailUrl) + videoEl.setAttribute('poster', this.video.thumbnailUrl) - if (this.skipToTime != null) videoEl.currentTime = this.skipToTime + if (this.skipToTime != null) videoEl.currentTime = this.skipToTime - const noPrevPlayer = !this.$player + const noPrevPlayer = !this.$player - const streams = [] + const streams = [] - streams.push(...this.video.audioStreams) - streams.push(...this.video.videoStreams) + streams.push(...this.video.audioStreams) + streams.push(...this.video.videoStreams) - const MseSupport = window.MediaSource !== undefined - const lbry = this.$store.getters['prefs/getPreferenceBoolean']('disableLBRY', true) - ? null - : this.video.videoStreams.filter(stream => stream.quality === 'LBRY')[0] - let uri, mime + const MseSupport = window.MediaSource !== undefined + const lbry = this.$store.getters['prefs/getPreferenceBoolean']('disableLBRY', true) + ? null + : this.video.videoStreams.filter(stream => stream.quality === 'LBRY')[0] + let uri, mime - if (this.video.livestream) { - uri = this.video.hls - mime = 'application/x-mpegURL' - } else if (this.video.audioStreams.length > 0 && !lbry && MseSupport) { - if (!this.video.dash) { - const dash = DashUtils.generate_dash_file_from_formats( - streams, - this.video.duration - ) - uri = 'data:application/dash+xml;charset=utf-8;base64,' + btoa(dash) - } else { - uri = this.video.dash - } - mime = 'application/dash+xml' - } else if (lbry) { - uri = lbry.url - if (this.$store.getters['prefs/getPreferenceBoolean']('proxyLBRY', true)) { - const url = new URL(uri) - url.searchParams.set('host', url.host) - url.host = new URL(this.video.proxyUrl).host - uri = url.toString() - } - mime = await fetch(uri, { - method: 'HEAD' - }).then(response => response.headers.get('Content-Type')) - } else if (this.video.hls) { - uri = this.video.hls - mime = 'application/x-mpegURL' - } else { - uri = this.video.videoStreams.filter(stream => stream.codec == null).slice(-1)[0].url - } + if (this.video.livestream) { + uri = this.video.hls + mime = 'application/x-mpegURL' + } else if (this.video.audioStreams.length > 0 && !lbry && MseSupport) { + if (!this.video.dash) { + const dash = DashUtils.generate_dash_file_from_formats( + streams, + this.video.duration + ) + uri = 'data:application/dash+xml;charset=utf-8;base64,' + btoa(dash) + } else { + uri = this.video.dash + } + mime = 'application/dash+xml' + } else if (lbry) { + uri = lbry.url + if (this.$store.getters['prefs/getPreferenceBoolean']('proxyLBRY', true)) { + const url = new URL(uri) + url.searchParams.set('host', url.host) + url.host = new URL(this.video.proxyUrl).host + uri = url.toString() + } + mime = await fetch(uri, { + method: 'HEAD' + }).then(response => response.headers.get('Content-Type')) + } else if (this.video.hls) { + uri = this.video.hls + mime = 'application/x-mpegURL' + } else { + uri = this.video.videoStreams.filter(stream => stream.codec == null).slice(-1)[0].url + } - if (noPrevPlayer) { - shaka.polyfill.installAll() + if (noPrevPlayer) { + shaka.polyfill.installAll() - const localPlayer = new shaka.Player(videoEl) - const proxyHost = new URL(component.video.proxyUrl).host + const localPlayer = new shaka.Player(videoEl) + const proxyHost = new URL(component.video.proxyUrl).host - localPlayer.getNetworkingEngine().registerRequestFilter((_type, request) => { - const uri = request.uris[0] - const url = new URL(uri) - const headers = request.headers - if ( - url.host.endsWith('.googlevideo.com') || + localPlayer.getNetworkingEngine().registerRequestFilter((_type, request) => { + const uri = request.uris[0] + const url = new URL(uri) + const headers = request.headers + if ( + url.host.endsWith('.googlevideo.com') || (url.host.endsWith('.lbryplayer.xyz') && (this.$store.getters['prefs/getPreferenceBoolean']('proxyLBRY', true) || headers.Range)) - ) { - url.searchParams.set('host', url.host) - url.host = proxyHost - request.uris[0] = url.toString() - } + ) { + url.searchParams.set('host', url.host) + url.host = proxyHost + request.uris[0] = url.toString() + } - if (url.pathname === '/videoplayback') { - if (headers.Range) { - url.searchParams.set('range', headers.Range.split('=')[1]) - request.headers = {} - request.uris[0] = url.toString() - } - } - }) + if (url.pathname === '/videoplayback') { + if (headers.Range) { + url.searchParams.set('range', headers.Range.split('=')[1]) + request.headers = {} + request.uris[0] = url.toString() + } + } + }) - localPlayer.configure( - 'streaming.bufferingGoal', - this.$store.getters['prefs/getPreferenceNumber']('bufferGoal', 10) - ) + localPlayer.configure( + 'streaming.bufferingGoal', + this.$store.getters['prefs/getPreferenceNumber']('bufferGoal', 10) + ) - this.setPlayerAttrs(localPlayer, videoEl, uri, mime, shaka) - } else this.setPlayerAttrs(this.$player, videoEl, uri, mime, shaka) + this.setPlayerAttrs(localPlayer, videoEl, uri, mime, shaka) + } else this.setPlayerAttrs(this.$player, videoEl, uri, mime, shaka) - if (noPrevPlayer) { - videoEl.addEventListener('timeupdate', (ev) => { - if (this.sponsors && this.sponsors.segments) { - const time = videoEl.currentTime - this.sponsors.segments.forEach(segment => { - if (!segment.skipped || this.selectedAutoLoop) { - const end = segment.segment[1] - if (time >= segment.segment[0] && time < end) { - console.log('Skipped segment at ' + time) - videoEl.currentTime = end - segment.skipped = true - } - } - }) - } - this.$emit('timeupdate', ev) - }) + if (noPrevPlayer) { + videoEl.addEventListener('timeupdate', (ev) => { + if (this.sponsors && this.sponsors.segments) { + const time = videoEl.currentTime + this.sponsors.segments.forEach(segment => { + if (!segment.skipped || this.selectedAutoLoop) { + const end = segment.segment[1] + if (time >= segment.segment[0] && time < end) { + console.log('Skipped segment at ' + time) + videoEl.currentTime = end + segment.skipped = true + } + } + }) + } + this.$emit('timeupdate', ev) + }) - videoEl.addEventListener('volumechange', () => { - this.$store.commit('prefs/setPrefs', { - id: 'volume', - value: videoEl.volume - }) - }) + videoEl.addEventListener('volumechange', () => { + this.$store.commit('prefs/setPrefs', { + id: 'volume', + value: videoEl.volume + }) + }) - videoEl.addEventListener('ratechange', () => { - this.$store.commit('prefs/setPrefs', { - id: 'rate', - value: videoEl.playbackRate - }) - }) + videoEl.addEventListener('ratechange', () => { + this.$store.commit('prefs/setPrefs', { + id: 'rate', + value: videoEl.playbackRate + }) + }) - videoEl.addEventListener('ended', () => { - this.$emit('videoEnded') - }) - } + videoEl.addEventListener('ended', () => { + this.$emit('videoEnded') + }) + } - // TODO: Add sponsors on seekbar: https://github.com/ajayyy/SponsorBlock/blob/e39de9fd852adb9196e0358ed827ad38d9933e29/src/js-components/previewBar.ts#L12 - }, - setPlayerAttrs (localPlayer, videoEl, uri, mime, shaka) { - if (!this.$ui) { - this.$ui = new shaka.ui.Overlay(localPlayer, this.$refs.container, videoEl) + // TODO: Add sponsors on seekbar: https://github.com/ajayyy/SponsorBlock/blob/e39de9fd852adb9196e0358ed827ad38d9933e29/src/js-components/previewBar.ts#L12 + }, + setPlayerAttrs (localPlayer, videoEl, uri, mime, shaka) { + if (!this.$ui) { + this.$ui = new shaka.ui.Overlay(localPlayer, this.$refs.container, videoEl) - const config = { - overflowMenuButtons: ['quality', 'captions', 'picture_in_picture', 'playback_rate'], - seekBarColors: { - base: 'rgba(255, 255, 255, 0.3)', - buffered: 'rgba(255, 255, 255, 0.54)', - played: 'rgb(255, 0, 0)' - } - } + const config = { + overflowMenuButtons: ['quality', 'captions', 'picture_in_picture', 'playback_rate'], + seekBarColors: { + base: 'rgba(255, 255, 255, 0.3)', + buffered: 'rgba(255, 255, 255, 0.54)', + played: 'rgb(255, 0, 0)' + } + } - this.$ui.configure(config) - } + this.$ui.configure(config) + } - const player = this.$ui.getControls().getPlayer() + const player = this.$ui.getControls().getPlayer() - this.$player = player + this.$player = player - const disableVideo = this.$store.getters['prefs/getPreferenceBoolean']('listen', false) && !this.video.livestream + const disableVideo = this.$store.getters['prefs/getPreferenceBoolean']('listen', false) && !this.video.livestream - this.$player.configure({ - preferredVideoCodecs: this.preferredVideoCodecs, - preferredAudioCodecs: ['opus', 'mp4a'], - manifest: { - disableVideo: disableVideo, - hls: { - useFullSegmentsForStartTime: true - } - }, - streaming: { - useNativeHlsOnSafari: false - } - }) + this.$player.configure({ + preferredVideoCodecs: this.preferredVideoCodecs, + preferredAudioCodecs: ['opus', 'mp4a'], + manifest: { + disableVideo: disableVideo, + hls: { + useFullSegmentsForStartTime: true + } + }, + streaming: { + useNativeHlsOnSafari: false + } + }) - const quality = this.$store.getters['prefs/getPreferenceNumber']('quality', 0) - const qualityConds = quality > 0 && (this.video.audioStreams.length > 0 || this.video.livestream) && !disableVideo - if (qualityConds) this.$player.configure('abr.enabled', false) + const quality = this.$store.getters['prefs/getPreferenceNumber']('quality', 0) + const qualityConds = quality > 0 && (this.video.audioStreams.length > 0 || this.video.livestream) && !disableVideo + if (qualityConds) this.$player.configure('abr.enabled', false) - player.load(uri, 0, mime).then(() => { - if (qualityConds) { - let leastDiff = Number.MAX_VALUE - let bestStream = null - player - .getVariantTracks() - .sort((a, b) => a.bandwidth - b.bandwidth) - .forEach(stream => { - const diff = Math.abs(quality - stream.height) - if (diff < leastDiff) { - leastDiff = diff - bestStream = stream - } - }) - player.selectVariantTrack(bestStream) - } + player.load(uri, 0, mime).then(() => { + if (qualityConds) { + let leastDiff = Number.MAX_VALUE + let bestStream = null + player + .getVariantTracks() + .sort((a, b) => a.bandwidth - b.bandwidth) + .forEach(stream => { + const diff = Math.abs(quality - stream.height) + if (diff < leastDiff) { + leastDiff = diff + bestStream = stream + } + }) + player.selectVariantTrack(bestStream) + } - this.video.subtitles.forEach(subtitle => { - player.addTextTrackAsync( - subtitle.url, - subtitle.code, - 'SUBTITLE', - subtitle.mimeType, - null, - subtitle.name - ) - }) - videoEl.volume = this.$store.getters['prefs/getPreferenceNumber']('volume', 1) - player.trickPlay(this.$store.getters['prefs/getPreferenceNumber']('rate', 1)) - }) - } - }, + this.video.subtitles.forEach(subtitle => { + player.addTextTrackAsync( + subtitle.url, + subtitle.code, + 'SUBTITLE', + subtitle.mimeType, + null, + subtitle.name + ) + }) + videoEl.volume = this.$store.getters['prefs/getPreferenceNumber']('volume', 1) + player.trickPlay(this.$store.getters['prefs/getPreferenceNumber']('rate', 1)) + }) + } + }, - watch: { - 'video.videoId' () { - this.loadVideo() - } - }, + watch: { + 'video.videoId' () { + this.loadVideo() + } + }, - mounted () { - this.loadVideo() - const self = this - const videoEl = self.$refs.videoEl - const onSpace = (e) => { - if (videoEl.paused) videoEl.play() - else videoEl.pause() - e.preventDefault() - } + mounted () { + this.loadVideo() + const self = this + const videoEl = self.$refs.videoEl + const onSpace = (e) => { + if (videoEl.paused) videoEl.play() + else videoEl.pause() + e.preventDefault() + } - this.unsubToKeybindings = setupKeybindings(window, { - f (e) { - if (document.fullscreenElement) document.exitFullscreen() - else self.$refs.container.requestFullscreen() - e.preventDefault() - }, - m (e) { - videoEl.muted = !videoEl.muted - e.preventDefault() - }, - j (e) { - videoEl.currentTime = Math.max(videoEl.currentTime - 15, 0) - e.preventDefault() - }, - l (e) { - videoEl.currentTime = videoEl.currentTime + 15 - e.preventDefault() - }, - c (e) { - self.$player.setTextTrackVisibility(!self.$player.isTextTrackVisible()) - e.preventDefault() - }, - Space: onSpace, - k: onSpace, - ArrowUp (e) { - videoEl.volume = Math.min(videoEl.volume + 0.05, 1) - e.preventDefault() - }, - ArrowDown (e) { - videoEl.volume = Math.max(videoEl.volume - 0.05, 0) - e.preventDefault() - }, - ArrowLeft (e) { - videoEl.currentTime = Math.max(videoEl.currentTime - 5, 0) - e.preventDefault() - }, - ArrowRight (e) { - videoEl.currentTime = videoEl.currentTime + 5 - e.preventDefault() - } - }) - }, + this.unsubToKeybindings = setupKeybindings(window, { + f (e) { + if (document.fullscreenElement) document.exitFullscreen() + else self.$refs.container.requestFullscreen() + e.preventDefault() + }, + m (e) { + videoEl.muted = !videoEl.muted + e.preventDefault() + }, + j (e) { + videoEl.currentTime = Math.max(videoEl.currentTime - 15, 0) + e.preventDefault() + }, + l (e) { + videoEl.currentTime = videoEl.currentTime + 15 + e.preventDefault() + }, + c (e) { + self.$player.setTextTrackVisibility(!self.$player.isTextTrackVisible()) + e.preventDefault() + }, + Space: onSpace, + k: onSpace, + ArrowUp (e) { + videoEl.volume = Math.min(videoEl.volume + 0.05, 1) + e.preventDefault() + }, + ArrowDown (e) { + videoEl.volume = Math.max(videoEl.volume - 0.05, 0) + e.preventDefault() + }, + ArrowLeft (e) { + videoEl.currentTime = Math.max(videoEl.currentTime - 5, 0) + e.preventDefault() + }, + ArrowRight (e) { + videoEl.currentTime = videoEl.currentTime + 5 + e.preventDefault() + } + }) + }, - beforeDestroy () { - if (this.$ui) { - this.$ui.destroy() - this.$ui = undefined - this.$player = undefined - } - if (this.$player) { - this.$player.destroy() - this.$player = undefined - } - this.unsubToKeybindings() - this.$refs.container.querySelectorAll('div').forEach(node => node.remove()) - } + beforeDestroy () { + if (this.$ui) { + this.$ui.destroy() + this.$ui = undefined + this.$player = undefined + } + if (this.$player) { + this.$player.destroy() + this.$player = undefined + } + this.unsubToKeybindings() + this.$refs.container.querySelectorAll('div').forEach(node => node.remove()) + } } diff --git a/src/components/SubscriptionButton.vue b/src/components/SubscriptionButton.vue index 0effb02..9f446db 100644 --- a/src/components/SubscriptionButton.vue +++ b/src/components/SubscriptionButton.vue @@ -9,52 +9,52 @@ diff --git a/src/components/VideoComment.vue b/src/components/VideoComment.vue index 5d8eb14..731a90b 100644 --- a/src/components/VideoComment.vue +++ b/src/components/VideoComment.vue @@ -37,47 +37,47 @@ import { LibPiped } from '@/tools/libpiped' import marked from 'marked' export default { - name: 'VideoComment', - props: ['comment', 'video', 'subComment'], - data: () => ({ - requestInProgress: false, - showChildComments: true, - childComments: [] - }), - methods: { - numberFormat (...args) { - return LibPiped.numberFormat(...args) - }, - async loadReplies () { - let nextpage = null - this.requestInProgress = true + name: 'VideoComment', + props: ['comment', 'video', 'subComment'], + data: () => ({ + requestInProgress: false, + showChildComments: true, + childComments: [] + }), + methods: { + numberFormat (...args) { + return LibPiped.numberFormat(...args) + }, + async loadReplies () { + let nextpage = null + this.requestInProgress = true - while (true) { - const replies = await this.$store.dispatch('auth/makeRequest', { - method: 'GET', - path: '/nextpage/comments/' + this.video.videoId, - params: { - nextpage: nextpage || this.comment.repliesPage - } - }) + while (true) { + const replies = await this.$store.dispatch('auth/makeRequest', { + method: 'GET', + path: '/nextpage/comments/' + this.video.videoId, + params: { + nextpage: nextpage || this.comment.repliesPage + } + }) - this.childComments = this.childComments.concat(replies.comments) - if (replies.nextpage) { - nextpage = replies.nextpage - } else { - break - } - } - this.requestInProgress = false - } - }, + this.childComments = this.childComments.concat(replies.comments) + if (replies.nextpage) { + nextpage = replies.nextpage + } else { + break + } + } + this.requestInProgress = false + } + }, - computed: { - renderedCommentTxt () { - return LibPiped.purifyHTML(marked.parseInline(this.comment.commentText, { - breaks: true - })) - } - } + computed: { + renderedCommentTxt () { + return LibPiped.purifyHTML(marked.parseInline(this.comment.commentText, { + breaks: true + })) + } + } } diff --git a/src/components/VideoItem.vue b/src/components/VideoItem.vue index 31c45dd..673cce2 100644 --- a/src/components/VideoItem.vue +++ b/src/components/VideoItem.vue @@ -26,62 +26,62 @@ import { LibPiped } from '@/tools/libpiped' import { findLastWatch } from '@/store/watched-videos-db' export default { - name: 'VideoItem', - props: { - video: Object, - height: Number, - width: Number, - hideChannel: Boolean, - maxHeight: Boolean, - srcProgress: Number - }, - data: () => ({ - alreadyWatched: false, - progress: 0 - }), - mounted () { - this.findIfVideoWatched() - }, - watch: { - 'video.videoId': 'findIfVideoWatched', - 'video.url': 'findIfVideoWatched', - srcProgress: 'findIfVideoWatched' - }, + name: 'VideoItem', + props: { + video: Object, + height: Number, + width: Number, + hideChannel: Boolean, + maxHeight: Boolean, + srcProgress: Number + }, + data: () => ({ + alreadyWatched: false, + progress: 0 + }), + mounted () { + this.findIfVideoWatched() + }, + watch: { + 'video.videoId': 'findIfVideoWatched', + 'video.url': 'findIfVideoWatched', + srcProgress: 'findIfVideoWatched' + }, - methods: { - async findIfVideoWatched () { - // if it has source progress, it's already seen - if (this.srcProgress) { - this.alreadyWatched = true - this.progress = this.srcProgress - return - } + methods: { + async findIfVideoWatched () { + // if it has source progress, it's already seen + if (this.srcProgress) { + this.alreadyWatched = true + this.progress = this.srcProgress + return + } - let videoId - if (this.video.videoId) { - videoId = this.video.videoId - } else { - videoId = LibPiped.determineVideoIdFromPath(this.video.url) - } + let videoId + if (this.video.videoId) { + videoId = this.video.videoId + } else { + videoId = LibPiped.determineVideoIdFromPath(this.video.url) + } - if (videoId) { - const lastVideo = await findLastWatch(videoId) - if (lastVideo != null) { - this.alreadyWatched = true - this.progress = lastVideo.progressPcnt - } else { - this.alreadyWatched = false - } - } - }, + if (videoId) { + const lastVideo = await findLastWatch(videoId) + if (lastVideo != null) { + this.alreadyWatched = true + this.progress = lastVideo.progressPcnt + } else { + this.alreadyWatched = false + } + } + }, - numberFormat (...args) { - return LibPiped.numberFormat(...args) - }, + numberFormat (...args) { + return LibPiped.numberFormat(...args) + }, - timeFormat (...args) { - return LibPiped.timeFormat(...args) - } - } + timeFormat (...args) { + return LibPiped.timeFormat(...args) + } + } } diff --git a/src/components/VideoRedirect.vue b/src/components/VideoRedirect.vue index 859714b..1080f6b 100644 --- a/src/components/VideoRedirect.vue +++ b/src/components/VideoRedirect.vue @@ -4,14 +4,14 @@ diff --git a/src/main.js b/src/main.js index eadb6be..76cc9de 100644 --- a/src/main.js +++ b/src/main.js @@ -14,9 +14,9 @@ import './registerServiceWorker' Vue.config.productionTip = false new Vue({ - vuetify, - i18n, - store, - router, - render: h => h(App) + vuetify, + i18n, + store, + router, + render: h => h(App) }).$mount('#app') diff --git a/src/plugins/i18n.js b/src/plugins/i18n.js index af25ae2..83febaf 100644 --- a/src/plugins/i18n.js +++ b/src/plugins/i18n.js @@ -6,93 +6,93 @@ import ENTranslations from '@/translations/en.json' Vue.use(VueI18n) const messages = { - en: ENTranslations + en: ENTranslations } export const i18n = new VueI18n({ - locale: 'en', // set default locale - fallbackLocale: 'en', - messages + locale: 'en', // set default locale + fallbackLocale: 'en', + messages }) async function syncStylesPerLanguage (locale) { - switch (locale) { - // All the latin languages - case 'de': - case 'en': - case 'el': - case 'fr': - case 'lt': - case 'ml': - case 'tr': - case 'bn_latn': - // Don't need to import fonts because Latin fonts are always loaded - document.body.classList.remove(...document.body.classList) - document.body.classList.add('latin') - break - // Bengali script - case 'bn_beng': - await import('@fontsource/hind-siliguri/bengali.css') - document.body.classList.remove(...document.body.classList) - document.body.classList.add('bengali') - break - // Other languages - // NOTE: if you are a native speaker & want to see a different font, just email me or join the channel - case 'zh_Hant': - await import('@fontsource/noto-sans-tc/chinese-traditional.css') - document.body.classList.remove(...document.body.classList) - document.body.classList.add('traditional-chinese') - break - case 'zh_Hans': - await import('@fontsource/noto-sans-sc/chinese-simplified.css') - document.body.classList.remove(...document.body.classList) - document.body.classList.add('simplified-chinese') - break - case 'jp': - await import('@fontsource/noto-sans-jp/japanese.css') - document.body.classList.remove(...document.body.classList) - document.body.classList.add('japanese') - break - case 'kr': - await import('@fontsource/noto-sans-kr/korean.css') - document.body.classList.remove(...document.body.classList) - document.body.classList.add('korean') - break - case 'cyrl': - await import('@fontsource/nunito-sans/cyrillic.css') - document.body.classList.remove(...document.body.classList) - document.body.classList.add('cyrillic') - break - } + switch (locale) { + // All the latin languages + case 'de': + case 'en': + case 'el': + case 'fr': + case 'lt': + case 'ml': + case 'tr': + case 'bn_latn': + // Don't need to import fonts because Latin fonts are always loaded + document.body.classList.remove(...document.body.classList) + document.body.classList.add('latin') + break + // Bengali script + case 'bn_beng': + await import('@fontsource/hind-siliguri/bengali.css') + document.body.classList.remove(...document.body.classList) + document.body.classList.add('bengali') + break + // Other languages + // NOTE: if you are a native speaker & want to see a different font, just email me or join the channel + case 'zh_Hant': + await import('@fontsource/noto-sans-tc/chinese-traditional.css') + document.body.classList.remove(...document.body.classList) + document.body.classList.add('traditional-chinese') + break + case 'zh_Hans': + await import('@fontsource/noto-sans-sc/chinese-simplified.css') + document.body.classList.remove(...document.body.classList) + document.body.classList.add('simplified-chinese') + break + case 'jp': + await import('@fontsource/noto-sans-jp/japanese.css') + document.body.classList.remove(...document.body.classList) + document.body.classList.add('japanese') + break + case 'kr': + await import('@fontsource/noto-sans-kr/korean.css') + document.body.classList.remove(...document.body.classList) + document.body.classList.add('korean') + break + case 'cyrl': + await import('@fontsource/nunito-sans/cyrillic.css') + document.body.classList.remove(...document.body.classList) + document.body.classList.add('cyrillic') + break + } } export async function loadLocale (locale) { - if (i18n.availableLocales.includes(locale)) { - return - } - // load locale messages with dynamic import - const messages = await import(/* webpackChunkName: "locale-[request]" */ `@/translations/${locale}.json`) + if (i18n.availableLocales.includes(locale)) { + return + } + // load locale messages with dynamic import + const messages = await import(/* webpackChunkName: "locale-[request]" */ `@/translations/${locale}.json`) - // set locale and locale message - i18n.setLocaleMessage(locale, messages.default) + // set locale and locale message + i18n.setLocaleMessage(locale, messages.default) - return Vue.nextTick() + return Vue.nextTick() } export async function changeLocale (lang) { - await loadLocale(lang) - await syncStylesPerLanguage(lang) - i18n.locale = lang - window.localStorage.setItem('LOCALE', lang) + await loadLocale(lang) + await syncStylesPerLanguage(lang) + i18n.locale = lang + window.localStorage.setItem('LOCALE', lang) } function initializeLocalLocale () { - let lang = window.localStorage.getItem('LOCALE') - if (lang == null) { - // Default language - lang = 'en' - } - changeLocale(lang).catch(e => console.error(e)) + let lang = window.localStorage.getItem('LOCALE') + if (lang == null) { + // Default language + lang = 'en' + } + changeLocale(lang).catch(e => console.error(e)) } initializeLocalLocale() diff --git a/src/plugins/keybindings.js b/src/plugins/keybindings.js index f7534d5..52b901d 100644 --- a/src/plugins/keybindings.js +++ b/src/plugins/keybindings.js @@ -1,19 +1,19 @@ import tinykeys from 'tinykeys' export function setupKeybindings (el, bindings, ...extra) { - const newBindings = {} - for (const [k, f] of Object.entries(bindings)) { - newBindings[k] = (ev, ...args) => { - const active = ev.target - const enteringText = active instanceof HTMLElement && + const newBindings = {} + for (const [k, f] of Object.entries(bindings)) { + newBindings[k] = (ev, ...args) => { + const active = ev.target + const enteringText = active instanceof HTMLElement && (active.isContentEditable || active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.tagName === 'SELECT' ) - if (!enteringText) return f(ev, ...args) - } - } + if (!enteringText) return f(ev, ...args) + } + } - return tinykeys(el, newBindings, ...extra) + return tinykeys(el, newBindings, ...extra) } diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.js index 9a8047a..fb06187 100644 --- a/src/plugins/vuetify.js +++ b/src/plugins/vuetify.js @@ -4,41 +4,41 @@ import Vuetify from 'vuetify/lib/framework' Vue.use(Vuetify) export default new Vuetify({ - theme: { - // There's apparently a race condition that causes components being rendered shortly before dark mode initialization unable to change to dark mode - dark: (() => { - try { - const preferences = JSON.parse(window.localStorage.getItem('PREFERENCES')) - return preferences.darkMode - } catch (e) { - return false - } - })(), - themes: { - light: { - primary: '#458588', - secondary: '#689d6a', - accent: '#af3a03', - error: '#cc241d', - warning: '#d79921', - info: '#458588', - success: '#98971a', + theme: { + // There's apparently a race condition that causes components being rendered shortly before dark mode initialization unable to change to dark mode + dark: (() => { + try { + const preferences = JSON.parse(window.localStorage.getItem('PREFERENCES')) + return preferences.darkMode + } catch (e) { + return false + } + })(), + themes: { + light: { + primary: '#458588', + secondary: '#689d6a', + accent: '#af3a03', + error: '#cc241d', + warning: '#d79921', + info: '#458588', + success: '#98971a', - bgOne: '#fbf1c7', - bgTwo: '#ebdbb2' - }, - dark: { - primary: '#458588', - secondary: '#689d6a', - accent: '#af3a03', - error: '#cc241d', - warning: '#d79921', - info: '#458588', - success: '#98971a', + bgOne: '#fbf1c7', + bgTwo: '#ebdbb2' + }, + dark: { + primary: '#458588', + secondary: '#689d6a', + accent: '#af3a03', + error: '#cc241d', + warning: '#d79921', + info: '#458588', + success: '#98971a', - bgOne: '#282828', - bgTwo: '#3c3836' - } - } - } + bgOne: '#282828', + bgTwo: '#3c3836' + } + } + } }) diff --git a/src/registerServiceWorker.js b/src/registerServiceWorker.js index 8c0c65d..f89ec91 100644 --- a/src/registerServiceWorker.js +++ b/src/registerServiceWorker.js @@ -3,31 +3,31 @@ import { register } from 'register-service-worker' if (process.env.NODE_ENV === 'production') { - register(`${process.env.BASE_URL}service-worker.js`, { - ready () { - console.log( - 'App is being served from cache by a service worker.\n' + + register(`${process.env.BASE_URL}service-worker.js`, { + ready () { + console.log( + 'App is being served from cache by a service worker.\n' + 'For more details, visit https://goo.gl/AFskqB' - ) - }, - registered () { - console.log('Service worker has been registered.') - }, - cached () { - console.log('Content has been cached for offline use.') - }, - updatefound () { - console.log('New content is downloading.') - }, - updated () { - console.log('New content is available; please refresh.') - window.location.reload() - }, - offline () { - console.log('No internet connection found. App is running in offline mode.') - }, - error (error) { - console.error('Error during service worker registration:', error) - } - }) + ) + }, + registered () { + console.log('Service worker has been registered.') + }, + cached () { + console.log('Content has been cached for offline use.') + }, + updatefound () { + console.log('New content is downloading.') + }, + updated () { + console.log('New content is available; please refresh.') + window.location.reload() + }, + offline () { + console.log('No internet connection found. App is running in offline mode.') + }, + error (error) { + console.error('Error during service worker registration:', error) + } + }) } diff --git a/src/router/index.js b/src/router/index.js index 9707080..efd1fc0 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -8,64 +8,64 @@ Vue.use(VueMeta) Vue.use(VueRouter) const routes = [ - { - path: '/', - name: 'Homepage', - component: FeedPage - }, - { - path: '/trending', - name: 'Trending', - component: FeedPage - }, - { - path: '/feed', - name: 'Feed', - component: FeedPage - }, - { - path: '/preferences', - name: 'Preferences', - component: () => import('@/routes/Preferences') - }, - { - path: '/subscriptions', - component: () => import('@/routes/Subscriptions') - }, - { - path: '/results', - name: 'SearchResults', - component: () => import('@/routes/SearchResults') - }, - { - path: '/playlist', - name: 'Playlist', - component: () => import('@/routes/Playlist') - }, - { - path: '/:path(v|w|embed|shorts|watch)/:v?', - name: 'WatchVideo', - component: () => import('@/routes/WatchVideo') - }, - { - path: '/:path(channel|user|c)/:channelId/:videos?', - name: 'Channel', - component: () => import('@/routes/Channel') - }, - { - path: '/:videoId([a-zA-Z0-9_-]{11})', - component: () => import('@/components/VideoRedirect') - }, - { - path: '/watch-history', - component: () => import('@/routes/WatchHistory') - } + { + path: '/', + name: 'Homepage', + component: FeedPage + }, + { + path: '/trending', + name: 'Trending', + component: FeedPage + }, + { + path: '/feed', + name: 'Feed', + component: FeedPage + }, + { + path: '/preferences', + name: 'Preferences', + component: () => import('@/routes/Preferences') + }, + { + path: '/subscriptions', + component: () => import('@/routes/Subscriptions') + }, + { + path: '/results', + name: 'SearchResults', + component: () => import('@/routes/SearchResults') + }, + { + path: '/playlist', + name: 'Playlist', + component: () => import('@/routes/Playlist') + }, + { + path: '/:path(v|w|embed|shorts|watch)/:v?', + name: 'WatchVideo', + component: () => import('@/routes/WatchVideo') + }, + { + path: '/:path(channel|user|c)/:channelId/:videos?', + name: 'Channel', + component: () => import('@/routes/Channel') + }, + { + path: '/:videoId([a-zA-Z0-9_-]{11})', + component: () => import('@/components/VideoRedirect') + }, + { + path: '/watch-history', + component: () => import('@/routes/WatchHistory') + } ] const router = new VueRouter({ - mode: 'history', - base: process.env.BASE_URL, - routes + mode: 'history', + base: process.env.BASE_URL, + routes }) export default router diff --git a/src/routes/Channel.vue b/src/routes/Channel.vue index 485127c..f2443a2 100644 --- a/src/routes/Channel.vue +++ b/src/routes/Channel.vue @@ -42,103 +42,103 @@ import SubscriptionButton from '@/components/SubscriptionButton' import { LibPiped } from '@/tools/libpiped' export default { - data () { - return { - channel: null - } - }, - metaInfo () { - const title = this.channel ? this.channel.name : 'Loading' + data () { + return { + channel: null + } + }, + metaInfo () { + const title = this.channel ? this.channel.name : 'Loading' - return { - title, - meta: this.channel && [ - { - name: 'twitter:title', - content: this.channel.name - }, - { - name: 'twitter:description', - content: this.channel.description - }, - { - property: 'og:type', - content: 'profile' - }, - { - property: 'og:title', - content: this.channel.name - }, - { - property: 'og:profile:username', - content: this.channel.name - }, - { - property: 'og:description', - content: this.channel.description - }, - { - name: 'description', - content: this.channel.description - }, - { - property: 'og:image', - content: this.channel.bannerUrl - }, - { - name: 'twitter:image', - content: this.channel.bannerUrl - } - ] - } - }, + return { + title, + meta: this.channel && [ + { + name: 'twitter:title', + content: this.channel.name + }, + { + name: 'twitter:description', + content: this.channel.description + }, + { + property: 'og:type', + content: 'profile' + }, + { + property: 'og:title', + content: this.channel.name + }, + { + property: 'og:profile:username', + content: this.channel.name + }, + { + property: 'og:description', + content: this.channel.description + }, + { + name: 'description', + content: this.channel.description + }, + { + property: 'og:image', + content: this.channel.bannerUrl + }, + { + name: 'twitter:image', + content: this.channel.bannerUrl + } + ] + } + }, - mounted () { - this.fetchChannel() - }, - watch: { - '$route.params.channelId': 'fetchChannel' - }, - methods: { - async fetchChannel () { - this.channel = await this.$store.dispatch('auth/makeRequest', { - path: '/' + this.$route.params.path + '/' + this.$route.params.channelId - }) - }, + mounted () { + this.fetchChannel() + }, + watch: { + '$route.params.channelId': 'fetchChannel' + }, + methods: { + async fetchChannel () { + this.channel = await this.$store.dispatch('auth/makeRequest', { + path: '/' + this.$route.params.path + '/' + this.$route.params.channelId + }) + }, - onRelatedStreamsEndIntersect (entries) { - if (entries[0].isIntersecting) { - this.fetchMoreVideos() - } - }, + onRelatedStreamsEndIntersect (entries) { + if (entries[0].isIntersecting) { + this.fetchMoreVideos() + } + }, - fetchMoreVideos () { - this.$store.dispatch('auth/makeRequest', { - path: '/nextpage/channel/' + this.channel.id, - params: { - nextpage: this.channel.nextpage - } - }).then(j => { - this.channel.relatedStreams = this.channel.relatedStreams.concat(j.relatedStreams) - this.channel.nextpage = j.nextpage - }) - } - }, - computed: { - renderedDescription () { - return LibPiped.purifyHTML(marked.parseInline(this.channel.description, { - breaks: true - })) - }, + fetchMoreVideos () { + this.$store.dispatch('auth/makeRequest', { + path: '/nextpage/channel/' + this.channel.id, + params: { + nextpage: this.channel.nextpage + } + }).then(j => { + this.channel.relatedStreams = this.channel.relatedStreams.concat(j.relatedStreams) + this.channel.nextpage = j.nextpage + }) + } + }, + computed: { + renderedDescription () { + return LibPiped.purifyHTML(marked.parseInline(this.channel.description, { + breaks: true + })) + }, - chunkedByFour () { - return _chunk(this.channel.relatedStreams, 4) - } - }, - components: { - SubscriptionButton, - ErrorHandler, - VideoItem - } + chunkedByFour () { + return _chunk(this.channel.relatedStreams, 4) + } + }, + components: { + SubscriptionButton, + ErrorHandler, + VideoItem + } } diff --git a/src/routes/FeedPage.vue b/src/routes/FeedPage.vue index 8e39db6..83b7de3 100644 --- a/src/routes/FeedPage.vue +++ b/src/routes/FeedPage.vue @@ -28,97 +28,97 @@ import { chunk as _chunk } from 'lodash-es' import VideoItem from '@/components/VideoItem.vue' export default { - data () { - return { - feedName: 'trending', - videos: [], - error: null, - errorIsJSON: false - } - }, + data () { + return { + feedName: 'trending', + videos: [], + error: null, + errorIsJSON: false + } + }, - metaInfo () { - return { - title: this.$t('titles.' + this.feedName) - } - }, + metaInfo () { + return { + title: this.$t('titles.' + this.feedName) + } + }, - watch: { - '$route.path': 'fetchData' - }, - mounted () { - this.fetchData() - }, - methods: { - async fetchData () { - const region = this.$store.getters['prefs/getPreference']('region', 'US') - const selectedHomepage = this.$store.getters['prefs/getPreference']('homepage', 'trending') - let path + watch: { + '$route.path': 'fetchData' + }, + mounted () { + this.fetchData() + }, + methods: { + async fetchData () { + const region = this.$store.getters['prefs/getPreference']('region', 'US') + const selectedHomepage = this.$store.getters['prefs/getPreference']('homepage', 'trending') + let path - switch (this.$route.path) { - case '/': - if (selectedHomepage === 'trending') { - path = '/trending' - this.feedName = 'trending' - } else { - path = '/feed' - this.feedName = 'feed' - } - break - case '/trending': - path = '/trending' - this.feedName = 'trending' - break - case '/feed': - path = '/feed' - this.feedName = 'feed' - break - } + switch (this.$route.path) { + case '/': + if (selectedHomepage === 'trending') { + path = '/trending' + this.feedName = 'trending' + } else { + path = '/feed' + this.feedName = 'feed' + } + break + case '/trending': + path = '/trending' + this.feedName = 'trending' + break + case '/feed': + path = '/feed' + this.feedName = 'feed' + break + } - try { - this.error = null - this.videos = await this.$store.dispatch('auth/makeRequest', { - path, - params: { - region: region - }, - tokenInParams: true - }) - } catch (e) { - if (e.isAxiosError) { - const rData = e.response.data - if (rData.message === 'Could not get Trending name') { - this.error = 'errors.trendingFetchError' - } else { - this.error = rData - this.errorIsJSON = true - } - } else { - throw e - } - } - } - }, - computed: { - rowSize () { - return this.$store.getters['prefs/getPreferenceNumber']('feedColumns', 4) - }, + try { + this.error = null + this.videos = await this.$store.dispatch('auth/makeRequest', { + path, + params: { + region: region + }, + tokenInParams: true + }) + } catch (e) { + if (e.isAxiosError) { + const rData = e.response.data + if (rData.message === 'Could not get Trending name') { + this.error = 'errors.trendingFetchError' + } else { + this.error = rData + this.errorIsJSON = true + } + } else { + throw e + } + } + } + }, + computed: { + rowSize () { + return this.$store.getters['prefs/getPreferenceNumber']('feedColumns', 4) + }, - columnClass () { - if (!this.$vuetify.breakpoint.mdAndUp) { - return - } - return 'span-col-' + (60 / this.rowSize) + ' mb-4' - }, + columnClass () { + if (!this.$vuetify.breakpoint.mdAndUp) { + return + } + return 'span-col-' + (60 / this.rowSize) + ' mb-4' + }, - splitIntoRows () { - return _chunk(this.videos, this.rowSize) - } - }, - components: { - VideoItem, - JSONViewer: () => import('vue-json-viewer') - } + splitIntoRows () { + return _chunk(this.videos, this.rowSize) + } + }, + components: { + VideoItem, + JSONViewer: () => import('vue-json-viewer') + } } diff --git a/src/routes/Playlist.vue b/src/routes/Playlist.vue index 3d63ba7..c486756 100644 --- a/src/routes/Playlist.vue +++ b/src/routes/Playlist.vue @@ -47,41 +47,41 @@ import VideoItem from '@/components/VideoItem.vue' import { chunk as _chunk } from 'lodash-es' export default { - data () { - return { - playlist: null - } - }, - metaInfo () { - return { title: this.playlist ? this.playlist.name : 'Loading' } - }, + data () { + return { + playlist: null + } + }, + metaInfo () { + return { title: this.playlist ? this.playlist.name : 'Loading' } + }, - mounted () { - this.getPlaylistData() - }, - computed: { - getRssUrl () { - return this.$store.getters['prefs/apiUrl'] + '/rss/playlists/' + this.$route.query.list - }, + mounted () { + this.getPlaylistData() + }, + computed: { + getRssUrl () { + return this.$store.getters['prefs/apiUrl'] + '/rss/playlists/' + this.$route.query.list + }, - chunkedByFour () { - return _chunk(this.playlist.relatedStreams, 4) - } - }, - methods: { - async fetchPlaylist () { - return this.$store.dispatch('auth/makeRequest', { - path: '/playlists/' + this.$route.query.list - }) - }, - async getPlaylistData () { - this.fetchPlaylist() - .then(data => (this.playlist = data)) - } - }, - components: { - ErrorHandler, - VideoItem - } + chunkedByFour () { + return _chunk(this.playlist.relatedStreams, 4) + } + }, + methods: { + async fetchPlaylist () { + return this.$store.dispatch('auth/makeRequest', { + path: '/playlists/' + this.$route.query.list + }) + }, + async getPlaylistData () { + this.fetchPlaylist() + .then(data => (this.playlist = data)) + } + }, + components: { + ErrorHandler, + VideoItem + } } diff --git a/src/routes/Preferences.vue b/src/routes/Preferences.vue index 8a20029..823bd48 100644 --- a/src/routes/Preferences.vue +++ b/src/routes/Preferences.vue @@ -38,196 +38,196 @@ import EnglishNames from 'i18n-iso-countries/langs/en.json' Countries.registerLocale(EnglishNames) export default { - data () { - return { - instances: [], + data () { + return { + instances: [], - options: [ - { - id: 'playerAutoplay', - type: 'bool', - default: true - }, - { - id: 'listen', - type: 'bool', - default: false - }, - { - id: 'disableLBRY', - type: 'bool', - default: true - }, - { - id: 'proxyLBRY', - type: 'bool', - default: true - }, - { - id: 'skipToLastPoint', - type: 'bool', - default: true - }, - { - id: 'homepage', - type: 'select', - label: 'Default Homepage', - default: 'trending', - options: [ - { - text: 'Trending', - value: 'trending' - }, - { - text: 'Feed', - value: 'feed' - } - ] - }, - { - id: 'feedColumns', - type: 'select', - label: 'Amount of Columns on Feed Pages', - default: 4, - options: [ - { - text: '4 columns', - value: 4 - }, - { - text: '5 columns', - value: 5 - }, - { - text: '6 columns', - value: 6 - } - ] - }, - { - id: 'quality', - type: 'select', - default: 0, - options: [ - { - text: 'Auto', - value: 0 - }, - ...([144, 240, 360, 480, 720, 1080, 1440, 2160, 4320].map(i => ({ - text: i.toString() + 'p', - value: i - }))) - ] - }, - { - id: 'bufferGoal', - type: 'number', - default: 10 - }, - { - id: 'sponsorblock', - type: 'bool', - default: true - }, - { - id: 'selectedSkip', - type: 'select', - label: 'Selected Segments to Skip', - multi: true, - default: ['sponsor', 'interaction', 'selfpromo', 'music_offtopic'], - options: [ - { text: 'Sponsor Segments ', value: 'sponsors' }, - { text: 'Intermission/Intro Animation Segments', value: 'intro' }, - { text: 'Endcards/Credits Segments', value: 'outro' }, - { text: 'Preview/Recap Segments', value: 'preview' }, - { text: 'Interaction Reminder (Subscribe) Segments', value: 'interaction' }, - { text: 'Unpaid/Self Promotion Segments', value: 'selfpromo' }, - { text: 'Music: Non-Music Segments', value: 'music_offtopic' } - ] - }, - { - id: 'region', - type: 'select', - label: 'Country', - default: 'US', - options: Object.entries(Countries.getNames('en', { select: 'official' })).map(([code, name]) => ({ - text: name, - value: code - })) - } - ], - tableHeaders: [ - { - text: 'Name', - value: 'name' - }, - { - text: 'API URL', - value: 'apiurl' - }, - { - text: 'Locations', - value: 'locations' - }, - { - text: 'CDN enabled?', - value: 'cdn' - } - ] - } - }, - metaInfo () { - return { - title: this.$t('titles.preferences') - } - }, + options: [ + { + id: 'playerAutoplay', + type: 'bool', + default: true + }, + { + id: 'listen', + type: 'bool', + default: false + }, + { + id: 'disableLBRY', + type: 'bool', + default: true + }, + { + id: 'proxyLBRY', + type: 'bool', + default: true + }, + { + id: 'skipToLastPoint', + type: 'bool', + default: true + }, + { + id: 'homepage', + type: 'select', + label: 'Default Homepage', + default: 'trending', + options: [ + { + text: 'Trending', + value: 'trending' + }, + { + text: 'Feed', + value: 'feed' + } + ] + }, + { + id: 'feedColumns', + type: 'select', + label: 'Amount of Columns on Feed Pages', + default: 4, + options: [ + { + text: '4 columns', + value: 4 + }, + { + text: '5 columns', + value: 5 + }, + { + text: '6 columns', + value: 6 + } + ] + }, + { + id: 'quality', + type: 'select', + default: 0, + options: [ + { + text: 'Auto', + value: 0 + }, + ...([144, 240, 360, 480, 720, 1080, 1440, 2160, 4320].map(i => ({ + text: i.toString() + 'p', + value: i + }))) + ] + }, + { + id: 'bufferGoal', + type: 'number', + default: 10 + }, + { + id: 'sponsorblock', + type: 'bool', + default: true + }, + { + id: 'selectedSkip', + type: 'select', + label: 'Selected Segments to Skip', + multi: true, + default: ['sponsor', 'interaction', 'selfpromo', 'music_offtopic'], + options: [ + { text: 'Sponsor Segments ', value: 'sponsors' }, + { text: 'Intermission/Intro Animation Segments', value: 'intro' }, + { text: 'Endcards/Credits Segments', value: 'outro' }, + { text: 'Preview/Recap Segments', value: 'preview' }, + { text: 'Interaction Reminder (Subscribe) Segments', value: 'interaction' }, + { text: 'Unpaid/Self Promotion Segments', value: 'selfpromo' }, + { text: 'Music: Non-Music Segments', value: 'music_offtopic' } + ] + }, + { + id: 'region', + type: 'select', + label: 'Country', + default: 'US', + options: Object.entries(Countries.getNames('en', { select: 'official' })).map(([code, name]) => ({ + text: name, + value: code + })) + } + ], + tableHeaders: [ + { + text: 'Name', + value: 'name' + }, + { + text: 'API URL', + value: 'apiurl' + }, + { + text: 'Locations', + value: 'locations' + }, + { + text: 'CDN enabled?', + value: 'cdn' + } + ] + } + }, + metaInfo () { + return { + title: this.$t('titles.preferences') + } + }, - mounted () { - if (Object.keys(this.$route.query).length > 0) this.$router.replace({ query: {} }) + mounted () { + if (Object.keys(this.$route.query).length > 0) this.$router.replace({ query: {} }) - fetch('https://raw.githubusercontent.com/wiki/TeamPiped/Piped-Frontend/Instances.md') - .then(resp => resp.text()) - .then(body => { - let skipped = 0 - const lines = body.split('\n') - lines.forEach(line => { - const split = line.split('|') - if (split.length === 5) { - if (skipped < 2) { - skipped++ - return - } - this.instances.push({ - name: split[0].trim(), - apiurl: split[1].trim(), - locations: split[2].trim(), - cdn: split[3].trim() - }) - } - }) - this.options.push({ - id: 'instance', - type: 'select', - default: this.$store.getters['prefs/apiUrl'], - label: 'Instance', - options: this.instances.map(i => ({ - text: i.name, - value: i.apiurl - })) - }) - }) - }, - methods: { - setValue (k, v) { - this.$store.commit('prefs/setPrefs', { - id: k, - value: v - }) - }, + fetch('https://raw.githubusercontent.com/wiki/TeamPiped/Piped-Frontend/Instances.md') + .then(resp => resp.text()) + .then(body => { + let skipped = 0 + const lines = body.split('\n') + lines.forEach(line => { + const split = line.split('|') + if (split.length === 5) { + if (skipped < 2) { + skipped++ + return + } + this.instances.push({ + name: split[0].trim(), + apiurl: split[1].trim(), + locations: split[2].trim(), + cdn: split[3].trim() + }) + } + }) + this.options.push({ + id: 'instance', + type: 'select', + default: this.$store.getters['prefs/apiUrl'], + label: 'Instance', + options: this.instances.map(i => ({ + text: i.name, + value: i.apiurl + })) + }) + }) + }, + methods: { + setValue (k, v) { + this.$store.commit('prefs/setPrefs', { + id: k, + value: v + }) + }, - sslScore (url) { - return 'https://www.ssllabs.com/ssltest/analyze.html?d=' + new URL(url).host + '&latest' - } - } + sslScore (url) { + return 'https://www.ssllabs.com/ssltest/analyze.html?d=' + new URL(url).host + '&latest' + } + } } diff --git a/src/routes/SearchMenu.vue b/src/routes/SearchMenu.vue index 9a4cd4b..e5e15e3 100644 --- a/src/routes/SearchMenu.vue +++ b/src/routes/SearchMenu.vue @@ -20,66 +20,66 @@ import { setupKeybindings } from '@/plugins/keybindings' export default { - name: 'SearchMenu', - data () { - return { - selected: 0, - select: '', - searchText: '', - requestInProgress: false, - searchSuggestions: [] - } - }, - watch: { - searchText (val) { - if (val && val !== this.select) { - this.refreshSuggestions() - } - }, + name: 'SearchMenu', + data () { + return { + selected: 0, + select: '', + searchText: '', + requestInProgress: false, + searchSuggestions: [] + } + }, + watch: { + searchText (val) { + if (val && val !== this.select) { + this.refreshSuggestions() + } + }, - async select (val) { - if (val === '') { - return - } - try { - await this.$router.push({ - name: 'SearchResults', - query: { search_query: val } - }) - } catch (e) { - console.log('???', e) - } finally { - this.select = '' - this.searchText = '' - } - } - }, + async select (val) { + if (val === '') { + return + } + try { + await this.$router.push({ + name: 'SearchResults', + query: { search_query: val } + }) + } catch (e) { + console.log('???', e) + } finally { + this.select = '' + this.searchText = '' + } + } + }, - methods: { - async refreshSuggestions () { - this.requestInProgress = true - this.searchSuggestions = await this.$store.dispatch('auth/makeRequest', { - path: '/suggestions', - params: { - query: this.searchText - } - }) - this.searchSuggestions.unshift(this.searchText) - this.requestInProgress = false - } - }, + methods: { + async refreshSuggestions () { + this.requestInProgress = true + this.searchSuggestions = await this.$store.dispatch('auth/makeRequest', { + path: '/suggestions', + params: { + query: this.searchText + } + }) + this.searchSuggestions.unshift(this.searchText) + this.requestInProgress = false + } + }, - mounted () { - this.unsubToKeybindings = setupKeybindings(window, { - '/': (e) => { - this.$refs.searchMenu.focus() - e.preventDefault() - } - }) - }, + mounted () { + this.unsubToKeybindings = setupKeybindings(window, { + '/': (e) => { + this.$refs.searchMenu.focus() + e.preventDefault() + } + }) + }, - beforeDestroy () { - this.unsubToKeybindings() - } + beforeDestroy () { + this.unsubToKeybindings() + } } diff --git a/src/routes/SearchResults.vue b/src/routes/SearchResults.vue index cd3a230..a39d2b6 100644 --- a/src/routes/SearchResults.vue +++ b/src/routes/SearchResults.vue @@ -31,115 +31,115 @@ import VideoItem from '@/components/VideoItem' import GenericDisplayItem from '@/components/GenericDisplayItem' export default { - components: { GenericDisplayItem, VideoItem }, - data () { - return { - results: null, - availableFilters: [ - 'all', - 'videos', - 'channels', - 'playlists', - 'music_songs', - 'music_videos', - 'music_albums', - 'music_playlists' - ], - selectedFilter: 'all' - } - }, - metaInfo () { - return { - title: this.$route.query.search_query - } - }, + components: { GenericDisplayItem, VideoItem }, + data () { + return { + results: null, + availableFilters: [ + 'all', + 'videos', + 'channels', + 'playlists', + 'music_songs', + 'music_videos', + 'music_albums', + 'music_playlists' + ], + selectedFilter: 'all' + } + }, + metaInfo () { + return { + title: this.$route.query.search_query + } + }, - mounted () { - this.updateResults() - }, - computed: { - chunkedByFour () { - return _chunk(this.results.items, 4) - } - }, - watch: { - selectedFilter () { - this.updateResults() - }, + mounted () { + this.updateResults() + }, + computed: { + chunkedByFour () { + return _chunk(this.results.items, 4) + } + }, + watch: { + selectedFilter () { + this.updateResults() + }, - // For history navigation - '$route.query.search_query' () { - this.updateResults() - } - }, + // For history navigation + '$route.query.search_query' () { + this.updateResults() + } + }, - methods: { - rationalizeSearchResult (sr) { - let type - // This seriously can't be the best solution - if (sr.url.startsWith('/watch')) { - type = 'VIDEO' - } else if (sr.url.startsWith('/playlist')) { - type = 'PLAYLIST' - } else if (sr.url.startsWith('/channel')) { - type = 'CHANNEL' - } else { - console.warn('WARNING: UNKNOWN VIDEO URL TYPE FOUND:', sr.url) - type = 'VIDEO' - } + methods: { + rationalizeSearchResult (sr) { + let type + // This seriously can't be the best solution + if (sr.url.startsWith('/watch')) { + type = 'VIDEO' + } else if (sr.url.startsWith('/playlist')) { + type = 'PLAYLIST' + } else if (sr.url.startsWith('/channel')) { + type = 'CHANNEL' + } else { + console.warn('WARNING: UNKNOWN VIDEO URL TYPE FOUND:', sr.url) + type = 'VIDEO' + } - return { - title: sr.name, - type, - uploaderName: sr.uploader, - uploadedDate: sr.uploadDate, - ...sr - } - }, + return { + title: sr.name, + type, + uploaderName: sr.uploader, + uploadedDate: sr.uploadDate, + ...sr + } + }, - async fetchResults () { - return this.$store.dispatch('auth/makeRequest', { - path: 'search', - params: { - q: this.$route.query.search_query, - filter: this.selectedFilter - } - }) - }, + async fetchResults () { + return this.$store.dispatch('auth/makeRequest', { + path: 'search', + params: { + q: this.$route.query.search_query, + filter: this.selectedFilter + } + }) + }, - numberFormat (...args) { - return LibPiped.numberFormat(...args) - }, + numberFormat (...args) { + return LibPiped.numberFormat(...args) + }, - timeFormat (...args) { - return LibPiped.timeFormat(...args) - }, - async updateResults () { - this.results = this.fetchResults().then(json => { - json.items = json.items.map(this.rationalizeSearchResult) - this.results = json - }) - }, - onSearchResultsEndIntersect (entries) { - if (entries[0].isIntersecting) { - this.fetchMoreResults() - } - }, + timeFormat (...args) { + return LibPiped.timeFormat(...args) + }, + async updateResults () { + this.results = this.fetchResults().then(json => { + json.items = json.items.map(this.rationalizeSearchResult) + this.results = json + }) + }, + onSearchResultsEndIntersect (entries) { + if (entries[0].isIntersecting) { + this.fetchMoreResults() + } + }, - fetchMoreResults () { - this.$store.dispatch('auth/makeRequest', { - path: '/nextpage/search', - params: { - nextpage: this.results.nextpage, - q: this.$route.query.search_query, - filter: this.selectedFilter - } - }).then(json => { - this.results.nextpage = json.nextpage - this.results.id = json.id - this.results.items = this.results.items.concat(json.items.map(this.rationalizeSearchResult)) - }) - } - } + fetchMoreResults () { + this.$store.dispatch('auth/makeRequest', { + path: '/nextpage/search', + params: { + nextpage: this.results.nextpage, + q: this.$route.query.search_query, + filter: this.selectedFilter + } + }).then(json => { + this.results.nextpage = json.nextpage + this.results.id = json.id + this.results.items = this.results.items.concat(json.items.map(this.rationalizeSearchResult)) + }) + } + } } diff --git a/src/routes/Subscriptions.vue b/src/routes/Subscriptions.vue index fdce3a3..d7e1ea7 100644 --- a/src/routes/Subscriptions.vue +++ b/src/routes/Subscriptions.vue @@ -23,33 +23,33 @@ import SubscriptionButton from '@/components/SubscriptionButton' import { LibPiped } from '@/tools/libpiped' export default { - name: 'Subscriptions', - components: { SubscriptionButton }, - data: () => ({ - loaded: false, - data: null - }), - methods: { - async loadData () { - if (!this.$store.getters['auth/isCurrentlyAuthenticated']) { - await this.$router.replace({ - path: '/' - }) - return - } - const resp = await this.$store.dispatch('auth/makeRequest', { - method: 'GET', - path: '/subscriptions' - }) - this.loaded = true - this.data = _chunk(resp.map(r => { - r.id = LibPiped.determineVideoIdFromChannelURL(r.url) - return r - }), 6) - } - }, - mounted () { - this.loadData() - } + name: 'Subscriptions', + components: { SubscriptionButton }, + data: () => ({ + loaded: false, + data: null + }), + methods: { + async loadData () { + if (!this.$store.getters['auth/isCurrentlyAuthenticated']) { + await this.$router.replace({ + path: '/' + }) + return + } + const resp = await this.$store.dispatch('auth/makeRequest', { + method: 'GET', + path: '/subscriptions' + }) + this.loaded = true + this.data = _chunk(resp.map(r => { + r.id = LibPiped.determineVideoIdFromChannelURL(r.url) + return r + }), 6) + } + }, + mounted () { + this.loadData() + } } diff --git a/src/routes/WatchHistory.vue b/src/routes/WatchHistory.vue index 7186a1e..fe8e83e 100644 --- a/src/routes/WatchHistory.vue +++ b/src/routes/WatchHistory.vue @@ -30,52 +30,52 @@ import VideoItem from '@/components/VideoItem' import ExpandableDate from '@/components/ExpandableDate' export default { - components: { - ExpandableDate, - VideoItem - }, - data: () => ({ - loaded: false, - data: null, - unfinishedVideosSwitch: false - }), + components: { + ExpandableDate, + VideoItem + }, + data: () => ({ + loaded: false, + data: null, + unfinishedVideosSwitch: false + }), - metaInfo () { - return { - title: this.$t('titles.history') - } - }, + metaInfo () { + return { + title: this.$t('titles.history') + } + }, - methods: { - async loadData (onlyUnfinished = false) { - if (!onlyUnfinished) { - this.data = await getWatchedVideos() - } else { - this.data = await getUnfinishedVideos() - } - this.loaded = true - }, + methods: { + async loadData (onlyUnfinished = false) { + if (!onlyUnfinished) { + this.data = await getWatchedVideos() + } else { + this.data = await getUnfinishedVideos() + } + this.loaded = true + }, - async deleteWatchHistory () { - await deleteWatchedVideos() - await this.loadData() - } - }, + async deleteWatchHistory () { + await deleteWatchedVideos() + await this.loadData() + } + }, - watch: { - unfinishedVideosSwitch () { - this.loadData(this.unfinishedVideosSwitch) - } - }, + watch: { + unfinishedVideosSwitch () { + this.loadData(this.unfinishedVideosSwitch) + } + }, - computed: { - chunkedByFour () { - return _chunk(this.data, 4) - } - }, + computed: { + chunkedByFour () { + return _chunk(this.data, 4) + } + }, - mounted () { - this.loadData().catch(e => console.error(e)) - } + mounted () { + this.loadData().catch(e => console.error(e)) + } } diff --git a/src/routes/WatchVideo.vue b/src/routes/WatchVideo.vue index 403dca7..b0ae970 100644 --- a/src/routes/WatchVideo.vue +++ b/src/routes/WatchVideo.vue @@ -105,228 +105,228 @@ import SubscriptionButton from '@/components/SubscriptionButton' import ExpandableDate from '@/components/ExpandableDate' export default { - name: 'WatchVideo', - data () { - return { - loaded: false, - video: { - title: 'Loading ...' - }, - sponsors: null, - selectedAutoLoop: false, - showDesc: true, - comments: null, - channelId: null, + name: 'WatchVideo', + data () { + return { + loaded: false, + video: { + title: 'Loading ...' + }, + sponsors: null, + selectedAutoLoop: false, + showDesc: true, + comments: null, + channelId: null, - dbID: null, - lastWatch: null - } - }, - metaInfo () { - return { - title: this.video.title, - meta: [ - { - name: 'twitter:title', - content: this.video.title - }, - { - name: 'twitter:description', - content: this.video.description - }, - { - property: 'og:type', - content: 'video' - }, - { - property: 'og:title', - content: this.video.title - }, - { - property: 'og:description', - content: this.video.description - }, - { - name: 'description', - content: this.video.description - }, - { - property: 'og:image', - content: this.video.thumbnailUrl - }, - { - name: 'twitter:image', - content: this.video.thumbnailUrl - } - ] - } - }, + dbID: null, + lastWatch: null + } + }, + metaInfo () { + return { + title: this.video.title, + meta: [ + { + name: 'twitter:title', + content: this.video.title + }, + { + name: 'twitter:description', + content: this.video.description + }, + { + property: 'og:type', + content: 'video' + }, + { + property: 'og:title', + content: this.video.title + }, + { + property: 'og:description', + content: this.video.description + }, + { + name: 'description', + content: this.video.description + }, + { + property: 'og:image', + content: this.video.thumbnailUrl + }, + { + name: 'twitter:image', + content: this.video.thumbnailUrl + } + ] + } + }, - mounted () { - this.initialize() - }, - watch: { - '$route.query.v': function (v) { - if (v) { - window.scrollTo(0, 0) - } - this.initialize() - } - }, - methods: { - initialize () { - this.getVideoData() - this.getSponsors() - if (this.$store.getters['prefs/getPreferenceBoolean']('comments', true)) this.getComments() - }, + mounted () { + this.initialize() + }, + watch: { + '$route.query.v': function (v) { + if (v) { + window.scrollTo(0, 0) + } + this.initialize() + } + }, + methods: { + initialize () { + this.getVideoData() + this.getSponsors() + if (this.$store.getters['prefs/getPreferenceBoolean']('comments', true)) this.getComments() + }, - videoEnded () { - if (!this.selectedAutoLoop && this.isAutoplayEnabled && this.video.relatedStreams[0]) { - this.$router.push({ - name: 'WatchVideo', - query: { - v: LibPiped.determineVideoIdFromPath(this.video.relatedStreams[0].url) - } - }) - } - }, + videoEnded () { + if (!this.selectedAutoLoop && this.isAutoplayEnabled && this.video.relatedStreams[0]) { + this.$router.push({ + name: 'WatchVideo', + query: { + v: LibPiped.determineVideoIdFromPath(this.video.relatedStreams[0].url) + } + }) + } + }, - onYTClick () { - const time = this.$refs.player.getCurrentTime() + onYTClick () { + const time = this.$refs.player.getCurrentTime() - const url = new URL('https://youtube.com/watch') - url.searchParams.set('v', this.videoId) - if (Number.isFinite(time)) { - url.searchParams.set('t', time.toFixed(0)) - } - window.location.href = url.href - }, + const url = new URL('https://youtube.com/watch') + url.searchParams.set('v', this.videoId) + if (Number.isFinite(time)) { + url.searchParams.set('t', time.toFixed(0)) + } + window.location.href = url.href + }, - numberFormat (...args) { - return LibPiped.numberFormat(...args) - }, + numberFormat (...args) { + return LibPiped.numberFormat(...args) + }, - addCommas (...args) { - return LibPiped.addCommas(...args) - }, + addCommas (...args) { + return LibPiped.addCommas(...args) + }, - fetchVideo () { - return this.$store.dispatch('auth/makeRequest', { - method: 'GET', - path: '/streams/' + this.videoId - }) - }, + fetchVideo () { + return this.$store.dispatch('auth/makeRequest', { + method: 'GET', + path: '/streams/' + this.videoId + }) + }, - async getSponsors () { - if (!this.$store.getters['prefs/getPreference']('sponsorblock', true)) { - return - } - this.sponsors = await this.$store.dispatch('auth/makeRequest', { - path: '/sponsors/' + this.videoId, - params: { - category: JSON.stringify(this.$store.getters['prefs/getPreference']('selectedSkip', ['sponsor', 'interaction', 'selfpromo', 'music_offtopic'])) - } - }) - }, - fetchComments () { - return this.$store.dispatch('auth/makeRequest', { - path: '/comments/' + this.videoId - }) - }, + async getSponsors () { + if (!this.$store.getters['prefs/getPreference']('sponsorblock', true)) { + return + } + this.sponsors = await this.$store.dispatch('auth/makeRequest', { + path: '/sponsors/' + this.videoId, + params: { + category: JSON.stringify(this.$store.getters['prefs/getPreference']('selectedSkip', ['sponsor', 'interaction', 'selfpromo', 'music_offtopic'])) + } + }) + }, + fetchComments () { + return this.$store.dispatch('auth/makeRequest', { + path: '/comments/' + this.videoId + }) + }, - onCommentsProgressIntersect (entries) { - if (entries[0].isIntersecting) { - this.fetchMoreComments() - } - }, + onCommentsProgressIntersect (entries) { + if (entries[0].isIntersecting) { + this.fetchMoreComments() + } + }, - fetchMoreComments () { - this.$store.dispatch('auth/makeRequest', { - path: '/nextpage/comments/' + this.videoId, - params: { - nextpage: this.comments.nextpage - } - }).then(json => { - this.comments.nextpage = json.nextpage - this.comments.comments = this.comments.comments.concat(json.comments) - }) - }, + fetchMoreComments () { + this.$store.dispatch('auth/makeRequest', { + path: '/nextpage/comments/' + this.videoId, + params: { + nextpage: this.comments.nextpage + } + }).then(json => { + this.comments.nextpage = json.nextpage + this.comments.comments = this.comments.comments.concat(json.comments) + }) + }, - onAutoplayChg (ev) { - this.$store.commit('prefs/setPrefs', { - id: 'autoplay', - value: ev - }) - }, + onAutoplayChg (ev) { + this.$store.commit('prefs/setPrefs', { + id: 'autoplay', + value: ev + }) + }, - async getVideoData () { - try { - this.lastWatch = await findLastWatch(this.videoId) - } catch (e) { - console.error('Errored while finding last watched', e) - } + async getVideoData () { + try { + this.lastWatch = await findLastWatch(this.videoId) + } catch (e) { + console.error('Errored while finding last watched', e) + } - const video = await this.fetchVideo() - video.videoId = this.videoId - video.url = this.$route.fullPath - this.video = video - this.loaded = true + const video = await this.fetchVideo() + video.videoId = this.videoId + video.url = this.$route.fullPath + this.video = video + this.loaded = true - if (this.video.error) { - return - } - this.channelId = this.video.uploaderUrl.split('/')[2] + if (this.video.error) { + return + } + this.channelId = this.video.uploaderUrl.split('/')[2] - this.video.description = LibPiped.purifyHTML( - this.video.description - .replaceAll('http://www.youtube.com', '') - .replaceAll('https://www.youtube.com', '') - .replaceAll('\n', '
') - ) - this.dbID = await addWatchedVideo(video) - }, - getComments () { - this.fetchComments().then(data => (this.comments = data)) - }, + this.video.description = LibPiped.purifyHTML( + this.video.description + .replaceAll('http://www.youtube.com', '') + .replaceAll('https://www.youtube.com', '') + .replaceAll('\n', '
') + ) + this.dbID = await addWatchedVideo(video) + }, + getComments () { + this.fetchComments().then(data => (this.comments = data)) + }, - onTimeUpdate: debounce(function onTimeUpdate (e) { - if (this.dbID == null || !this.$refs.player) { - return - } - return updateWatchedVideoProgress(this.dbID, this.$refs.player.getCurrentTime(), this.video.duration) - }, 500) - }, - computed: { - isAutoplayEnabled () { - return this.$store.getters['prefs/getPreferenceBoolean']('autoplay', false) - }, - videoId () { - return this.$route.query.v || this.$route.params.v - }, - skipToTime () { - // 't' in $route.query ? Number($route.query.t) : (lastWatch.progress ? lastWatch.progress : undefined) - // 1st Priority - t in query - // 2nd Priority - Last Watched Progress, if enabled - if ('t' in this.$route.query) { - return Number(this.$route.query.t) - } else if (this.lastWatch && this.lastWatch.progress != null && this.lastWatch.progress !== 0 && this.$store.getters['prefs/getPreferenceBoolean']('skipToLastPoint', true)) { - return this.lastWatch.progress - } else { - return undefined - } - }, + onTimeUpdate: debounce(function onTimeUpdate (e) { + if (this.dbID == null || !this.$refs.player) { + return + } + return updateWatchedVideoProgress(this.dbID, this.$refs.player.getCurrentTime(), this.video.duration) + }, 500) + }, + computed: { + isAutoplayEnabled () { + return this.$store.getters['prefs/getPreferenceBoolean']('autoplay', false) + }, + videoId () { + return this.$route.query.v || this.$route.params.v + }, + skipToTime () { + // 't' in $route.query ? Number($route.query.t) : (lastWatch.progress ? lastWatch.progress : undefined) + // 1st Priority - t in query + // 2nd Priority - Last Watched Progress, if enabled + if ('t' in this.$route.query) { + return Number(this.$route.query.t) + } else if (this.lastWatch && this.lastWatch.progress != null && this.lastWatch.progress !== 0 && this.$store.getters['prefs/getPreferenceBoolean']('skipToLastPoint', true)) { + return this.lastWatch.progress + } else { + return undefined + } + }, - lastWatchDurationH () { - return LibPiped.timeFormat(this.lastWatch.progress) - } - }, - components: { - ExpandableDate, - SubscriptionButton, - VideoComment, - Player, - VideoItem, - ErrorHandler - } + lastWatchDurationH () { + return LibPiped.timeFormat(this.lastWatch.progress) + } + }, + components: { + ExpandableDate, + SubscriptionButton, + VideoComment, + Player, + VideoItem, + ErrorHandler + } } diff --git a/src/store/authentication-store.js b/src/store/authentication-store.js index 0bb5a91..92a48c5 100644 --- a/src/store/authentication-store.js +++ b/src/store/authentication-store.js @@ -2,136 +2,136 @@ import axios from 'axios' import { isPlainObject as _isPlainObject, set as _set } from 'lodash-es' export class AuthenticationError extends Error { - constructor (message) { - super() - this.message = message - } + constructor (message) { + super() + this.message = message + } } const AuthenticationStore = { - namespaced: true, + namespaced: true, - state: () => ({ - authStateByInstance: { - // isAuthenticated: Boolean - // authToken: String? - } - }), + state: () => ({ + authStateByInstance: { + // isAuthenticated: Boolean + // authToken: String? + } + }), - mutations: { - replaceAuth (state, data) { - state.authStateByInstance = data - }, + mutations: { + replaceAuth (state, data) { + state.authStateByInstance = data + }, - setAuthToken (state, { apiURL, token }) { - _set(state.authStateByInstance, [apiURL], { - isAuthenticated: true, - authToken: token - }) - window.localStorage.setItem('AUTH', JSON.stringify(state.authStateByInstance)) - }, + setAuthToken (state, { apiURL, token }) { + _set(state.authStateByInstance, [apiURL], { + isAuthenticated: true, + authToken: token + }) + window.localStorage.setItem('AUTH', JSON.stringify(state.authStateByInstance)) + }, - deleteAuthToken (state, { apiURL }) { - _set(state.authStateByInstance, [apiURL], { - isAuthenticated: false - }) - window.localStorage.setItem('AUTH', JSON.stringify(state.authStateByInstance)) - } - }, + deleteAuthToken (state, { apiURL }) { + _set(state.authStateByInstance, [apiURL], { + isAuthenticated: false + }) + window.localStorage.setItem('AUTH', JSON.stringify(state.authStateByInstance)) + } + }, - getters: { - isCurrentlyAuthenticated (state, getters, rootState, rootGetters) { - const s = state.authStateByInstance[rootGetters['prefs/apiUrl']] - return s ? (s.isAuthenticated === true) : false - }, + getters: { + isCurrentlyAuthenticated (state, getters, rootState, rootGetters) { + const s = state.authStateByInstance[rootGetters['prefs/apiUrl']] + return s ? (s.isAuthenticated === true) : false + }, - authToken (state, getters, rootState, rootGetters) { - const authState = state.authStateByInstance[rootGetters['prefs/apiUrl']] || {} - return authState.authToken - } - }, + authToken (state, getters, rootState, rootGetters) { + const authState = state.authStateByInstance[rootGetters['prefs/apiUrl']] || {} + return authState.authToken + } + }, - actions: { - initializeAuth ({ commit }) { - const data = window.localStorage.getItem('AUTH') - if (data != null) { - commit('replaceAuth', JSON.parse(data)) - } - }, + actions: { + initializeAuth ({ commit }) { + const data = window.localStorage.getItem('AUTH') + if (data != null) { + commit('replaceAuth', JSON.parse(data)) + } + }, - async loginOrRegister ({ commit, rootGetters }, { path, username, password }) { - const apiURL = rootGetters['prefs/apiUrl'] - const { data: resp } = await axios({ - method: 'POST', - baseURL: apiURL, - url: '/' + path, - data: { - username, - password - } - }) + async loginOrRegister ({ commit, rootGetters }, { path, username, password }) { + const apiURL = rootGetters['prefs/apiUrl'] + const { data: resp } = await axios({ + method: 'POST', + baseURL: apiURL, + url: '/' + path, + data: { + username, + password + } + }) - if ('error' in resp) { - throw new AuthenticationError(resp.error) - } + if ('error' in resp) { + throw new AuthenticationError(resp.error) + } - commit('setAuthToken', { - apiURL, - token: resp.token - }) - }, + commit('setAuthToken', { + apiURL, + token: resp.token + }) + }, - async makeRequest ({ - commit, - state, - rootGetters - }, { - path, - method, - data, - params, - tokenInParams = false - }) { - const APIURL = rootGetters['prefs/apiUrl'] - const AuthState = state.authStateByInstance[APIURL] || {} + async makeRequest ({ + commit, + state, + rootGetters + }, { + path, + method, + data, + params, + tokenInParams = false + }) { + const APIURL = rootGetters['prefs/apiUrl'] + const AuthState = state.authStateByInstance[APIURL] || {} - if (AuthState.isAuthenticated && tokenInParams) { - if (_isPlainObject(params)) { - params.authToken = AuthState.authToken - } else { - params = { - authToken: AuthState.authToken - } - } - } + if (AuthState.isAuthenticated && tokenInParams) { + if (_isPlainObject(params)) { + params.authToken = AuthState.authToken + } else { + params = { + authToken: AuthState.authToken + } + } + } - const { data: resp } = await axios({ - baseURL: APIURL, - method, - url: path, - params, - data, - headers: AuthState.isAuthenticated - ? { - Authorization: AuthState.authToken - } - : undefined - }) + const { data: resp } = await axios({ + baseURL: APIURL, + method, + url: path, + params, + data, + headers: AuthState.isAuthenticated + ? { + Authorization: AuthState.authToken + } + : undefined + }) - return resp - } - } + return resp + } + } } function initializeAuthEvents (store) { - window.addEventListener('storage', (storageEv) => { - if (storageEv.key === 'AUTH') { - store.commit('auth/replaceAuth', JSON.parse(storageEv.newValue)) - } - }) + window.addEventListener('storage', (storageEv) => { + if (storageEv.key === 'AUTH') { + store.commit('auth/replaceAuth', JSON.parse(storageEv.newValue)) + } + }) } export { - AuthenticationStore, - initializeAuthEvents + AuthenticationStore, + initializeAuthEvents } diff --git a/src/store/index.js b/src/store/index.js index ead76ad..ca76c8d 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -7,10 +7,10 @@ import { AuthenticationStore, initializeAuthEvents } from '@/store/authenticatio Vue.use(Vuex) const store = new Vuex.Store({ - modules: { - prefs: PrefsStore, - auth: AuthenticationStore - } + modules: { + prefs: PrefsStore, + auth: AuthenticationStore + } }) initializePrefEvents(store) diff --git a/src/store/prefs-store.js b/src/store/prefs-store.js index 8c943ed..6f4ed2e 100644 --- a/src/store/prefs-store.js +++ b/src/store/prefs-store.js @@ -1,93 +1,93 @@ import { get as _get, isString, set as _set } from 'lodash-es' const PrefsStore = { - namespaced: true, - state: () => ({ - // Declaring boolean values here because v-simple-checkbox has a bug (???) which renders it unable to have internal state - prefs: { - darkMode: false, - playerAutoplay: true, - autoplay: false, - listen: false, - disableLBRY: true, - proxyLBRY: true, - sponsorblock: true, - skipToLastPoint: true - } - }), - mutations: { - setPrefs (state, { - id, - value - }) { - _set(state.prefs, id, value) - window.localStorage.setItem('PREFERENCES', JSON.stringify(state.prefs)) - }, + namespaced: true, + state: () => ({ + // Declaring boolean values here because v-simple-checkbox has a bug (???) which renders it unable to have internal state + prefs: { + darkMode: false, + playerAutoplay: true, + autoplay: false, + listen: false, + disableLBRY: true, + proxyLBRY: true, + sponsorblock: true, + skipToLastPoint: true + } + }), + mutations: { + setPrefs (state, { + id, + value + }) { + _set(state.prefs, id, value) + window.localStorage.setItem('PREFERENCES', JSON.stringify(state.prefs)) + }, - replacePrefs (state, nextPrefs) { - state.prefs = nextPrefs - } - }, - actions: { - loadState ({ commit }) { - try { - const jv = window.localStorage.getItem('PREFERENCES') - if (isString(jv) && jv.length !== 0) { - const p = JSON.parse(jv) - commit('replacePrefs', p) - } - } catch (e) { - console.log('Error:', e) - } - } - }, + replacePrefs (state, nextPrefs) { + state.prefs = nextPrefs + } + }, + actions: { + loadState ({ commit }) { + try { + const jv = window.localStorage.getItem('PREFERENCES') + if (isString(jv) && jv.length !== 0) { + const p = JSON.parse(jv) + commit('replacePrefs', p) + } + } catch (e) { + console.log('Error:', e) + } + } + }, - getters: { - getPreference: state => (id, default_) => { - return _get(state.prefs, id, default_) - }, + getters: { + getPreference: state => (id, default_) => { + return _get(state.prefs, id, default_) + }, - getPreferenceBoolean: (_, getters) => (...args) => { - const v = getters.getPreference(...args) + getPreferenceBoolean: (_, getters) => (...args) => { + const v = getters.getPreference(...args) - switch (v) { - case 'true': - case true: - case '1': - case 'on': - case 'yes': - return true - default: - return false - } - }, + switch (v) { + case 'true': + case true: + case '1': + case 'on': + case 'yes': + return true + default: + return false + } + }, - getPreferenceNumber: (_, getters) => (id, default_) => { - const v = getters.getPreference(id, default_) + getPreferenceNumber: (_, getters) => (id, default_) => { + const v = getters.getPreference(id, default_) - const n = Number(v) - if (!Number.isFinite(n)) { - return default_ - } else { - return n - } - }, + const n = Number(v) + if (!Number.isFinite(n)) { + return default_ + } else { + return n + } + }, - apiUrl (state, getters) { - return getters.getPreference('instance', process.env.VUE_APP_PIPED_URL || 'https://pipedapi.kavin.rocks') - } - } + apiUrl (state, getters) { + return getters.getPreference('instance', process.env.VUE_APP_PIPED_URL || 'https://pipedapi.kavin.rocks') + } + } } function initializePrefEvents (store) { - window.addEventListener('storage', (storageEv) => { - if (storageEv.key === 'PREFERENCES') { - store.commit('prefs/replacePrefs', JSON.parse(storageEv.newValue)) - } - }) + window.addEventListener('storage', (storageEv) => { + if (storageEv.key === 'PREFERENCES') { + store.commit('prefs/replacePrefs', JSON.parse(storageEv.newValue)) + } + }) } export { - PrefsStore, - initializePrefEvents + PrefsStore, + initializePrefEvents } diff --git a/src/store/watched-videos-db.js b/src/store/watched-videos-db.js index b3db21f..1b432ec 100644 --- a/src/store/watched-videos-db.js +++ b/src/store/watched-videos-db.js @@ -3,39 +3,39 @@ import Dexie from 'dexie' export const PMDB = new Dexie('PipedMaterialDB') PMDB.version(3).stores({ - watchedVideos: '++id,videoId,progressPcnt,timestamp' + watchedVideos: '++id,videoId,progressPcnt,timestamp' }) export async function addWatchedVideo (videoObj) { - return PMDB.watchedVideos.add({ - videoId: videoObj.videoId, - video: videoObj, - progress: 0, - progressPcnt: 0, - timestamp: new Date() - }) + return PMDB.watchedVideos.add({ + videoId: videoObj.videoId, + video: videoObj, + progress: 0, + progressPcnt: 0, + timestamp: new Date() + }) } export function updateWatchedVideoProgress (videoID, prog, dur) { - return PMDB.watchedVideos.update(videoID, { - progress: prog, - progressPcnt: Math.min((prog / dur) * 100, 100) - }) + return PMDB.watchedVideos.update(videoID, { + progress: prog, + progressPcnt: Math.min((prog / dur) * 100, 100) + }) } export function findLastWatch (videoId) { - return PMDB.watchedVideos.where('videoId').equals(videoId).last() + return PMDB.watchedVideos.where('videoId').equals(videoId).last() } export function getWatchedVideos () { - return PMDB.watchedVideos.orderBy('timestamp').reverse().toArray() + return PMDB.watchedVideos.orderBy('timestamp').reverse().toArray() } export function getUnfinishedVideos () { - // Shaka never fires the last event, thus the last percent turns out to be around 99.95 or 99.98 - return PMDB.watchedVideos.where('progressPcnt').below(99.9).reverse().sortBy('timestamp') + // Shaka never fires the last event, thus the last percent turns out to be around 99.95 or 99.98 + return PMDB.watchedVideos.where('progressPcnt').below(99.9).reverse().sortBy('timestamp') } export function deleteWatchedVideos () { - return PMDB.watchedVideos.clear() + return PMDB.watchedVideos.clear() } diff --git a/src/tools/DashUtils.js b/src/tools/DashUtils.js index a225684..471c6cc 100644 --- a/src/tools/DashUtils.js +++ b/src/tools/DashUtils.js @@ -3,185 +3,185 @@ import xml from 'xml-js' const DashUtils = { - generate_dash_file_from_formats (VideoFormats, VideoLength) { - const generatedJSON = this.generate_xmljs_json_from_data(VideoFormats, VideoLength) - return xml.json2xml(generatedJSON) - }, - generate_xmljs_json_from_data (VideoFormatArray, VideoLength) { - return { - declaration: { - attributes: { - version: '1.0', - encoding: 'utf-8' - } - }, - elements: [ - { - type: 'element', - name: 'MPD', - attributes: { - xmlns: 'urn:mpeg:dash:schema:mpd:2011', - profiles: 'urn:mpeg:dash:profile:full:2011', - minBufferTime: 'PT1.5S', - type: 'static', - mediaPresentationDuration: `PT${VideoLength}S` - }, - elements: [ - { - type: 'element', - name: 'Period', - elements: this.generate_adaptation_set(VideoFormatArray) - } - ] - } - ] - } - }, - generate_adaptation_set (VideoFormatArray) { - const adaptationSets = [] - const mimeTypes = [] - const mimeObjects = [[]] - // sort the formats by mime types - VideoFormatArray.forEach(videoFormat => { - // the dual formats should not be used - if (videoFormat.mimeType.indexOf('video') !== -1 && !videoFormat.videoOnly) { - return - } - // if these properties are not available, then we skip it because we cannot set these properties - // if (!(videoFormat.hasOwnProperty('initRange') && videoFormat.hasOwnProperty('indexRange'))) { - // return - // } - const mimeType = videoFormat.mimeType - const mimeTypeIndex = mimeTypes.indexOf(mimeType) - if (mimeTypeIndex > -1) { - mimeObjects[mimeTypeIndex].push(videoFormat) - } else { - mimeTypes.push(mimeType) - mimeObjects.push([]) - mimeObjects[mimeTypes.length - 1].push(videoFormat) - } - }) - // for each MimeType generate a new Adaptation set with Representations as sub elements - for (let i = 0; i < mimeTypes.length; i++) { - let isVideoFormat = false - const adapSet = { - type: 'element', - name: 'AdaptationSet', - attributes: { - id: i, - mimeType: mimeTypes[i], - startWithSAP: '1', - subsegmentAlignment: 'true' - }, - elements: [] - } - if (!mimeTypes[i].includes('audio')) { - adapSet.attributes.scanType = 'progressive' - isVideoFormat = true - } - mimeObjects[i].forEach(format => { - if (isVideoFormat) { - adapSet.elements.push(this.generate_representation_video(format)) - } else { - adapSet.elements.push(this.generate_representation_audio(format)) - } - }) - adaptationSets.push(adapSet) - } - return adaptationSets - }, - generate_representation_audio (Format) { - return { - type: 'element', - name: 'Representation', - attributes: { - id: Format.itag, - codecs: Format.codec, - bandwidth: Format.bitrate - }, - elements: [ - { - type: 'element', - name: 'AudioChannelConfiguration', - attributes: { - schemeIdUri: 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011', - value: '2' - } - }, - { - type: 'element', - name: 'BaseURL', - elements: [ - { - type: 'text', - text: Format.url - } - ] - }, - { - type: 'element', - name: 'SegmentBase', - attributes: { - indexRange: `${Format.indexStart}-${Format.indexEnd}` - }, - elements: [ - { - type: 'element', - name: 'Initialization', - attributes: { - range: `${Format.initStart}-${Format.initEnd}` - } - } - ] - } - ] - } - }, - generate_representation_video (Format) { - return { - type: 'element', - name: 'Representation', - attributes: { - id: Format.itag, - codecs: Format.codec, - bandwidth: Format.bitrate, - width: Format.width, - height: Format.height, - maxPlayoutRate: '1', - frameRate: Format.fps - }, - elements: [ - { - type: 'element', - name: 'BaseURL', - elements: [ - { - type: 'text', - text: Format.url - } - ] - }, - { - type: 'element', - name: 'SegmentBase', - attributes: { - indexRange: `${Format.indexStart}-${Format.indexEnd}` - }, - elements: [ - { - type: 'element', - name: 'Initialization', - attributes: { - range: `${Format.initStart}-${Format.initEnd}` - } - } - ] - } - ] - } - } + generate_dash_file_from_formats (VideoFormats, VideoLength) { + const generatedJSON = this.generate_xmljs_json_from_data(VideoFormats, VideoLength) + return xml.json2xml(generatedJSON) + }, + generate_xmljs_json_from_data (VideoFormatArray, VideoLength) { + return { + declaration: { + attributes: { + version: '1.0', + encoding: 'utf-8' + } + }, + elements: [ + { + type: 'element', + name: 'MPD', + attributes: { + xmlns: 'urn:mpeg:dash:schema:mpd:2011', + profiles: 'urn:mpeg:dash:profile:full:2011', + minBufferTime: 'PT1.5S', + type: 'static', + mediaPresentationDuration: `PT${VideoLength}S` + }, + elements: [ + { + type: 'element', + name: 'Period', + elements: this.generate_adaptation_set(VideoFormatArray) + } + ] + } + ] + } + }, + generate_adaptation_set (VideoFormatArray) { + const adaptationSets = [] + const mimeTypes = [] + const mimeObjects = [[]] + // sort the formats by mime types + VideoFormatArray.forEach(videoFormat => { + // the dual formats should not be used + if (videoFormat.mimeType.indexOf('video') !== -1 && !videoFormat.videoOnly) { + return + } + // if these properties are not available, then we skip it because we cannot set these properties + // if (!(videoFormat.hasOwnProperty('initRange') && videoFormat.hasOwnProperty('indexRange'))) { + // return + // } + const mimeType = videoFormat.mimeType + const mimeTypeIndex = mimeTypes.indexOf(mimeType) + if (mimeTypeIndex > -1) { + mimeObjects[mimeTypeIndex].push(videoFormat) + } else { + mimeTypes.push(mimeType) + mimeObjects.push([]) + mimeObjects[mimeTypes.length - 1].push(videoFormat) + } + }) + // for each MimeType generate a new Adaptation set with Representations as sub elements + for (let i = 0; i < mimeTypes.length; i++) { + let isVideoFormat = false + const adapSet = { + type: 'element', + name: 'AdaptationSet', + attributes: { + id: i, + mimeType: mimeTypes[i], + startWithSAP: '1', + subsegmentAlignment: 'true' + }, + elements: [] + } + if (!mimeTypes[i].includes('audio')) { + adapSet.attributes.scanType = 'progressive' + isVideoFormat = true + } + mimeObjects[i].forEach(format => { + if (isVideoFormat) { + adapSet.elements.push(this.generate_representation_video(format)) + } else { + adapSet.elements.push(this.generate_representation_audio(format)) + } + }) + adaptationSets.push(adapSet) + } + return adaptationSets + }, + generate_representation_audio (Format) { + return { + type: 'element', + name: 'Representation', + attributes: { + id: Format.itag, + codecs: Format.codec, + bandwidth: Format.bitrate + }, + elements: [ + { + type: 'element', + name: 'AudioChannelConfiguration', + attributes: { + schemeIdUri: 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011', + value: '2' + } + }, + { + type: 'element', + name: 'BaseURL', + elements: [ + { + type: 'text', + text: Format.url + } + ] + }, + { + type: 'element', + name: 'SegmentBase', + attributes: { + indexRange: `${Format.indexStart}-${Format.indexEnd}` + }, + elements: [ + { + type: 'element', + name: 'Initialization', + attributes: { + range: `${Format.initStart}-${Format.initEnd}` + } + } + ] + } + ] + } + }, + generate_representation_video (Format) { + return { + type: 'element', + name: 'Representation', + attributes: { + id: Format.itag, + codecs: Format.codec, + bandwidth: Format.bitrate, + width: Format.width, + height: Format.height, + maxPlayoutRate: '1', + frameRate: Format.fps + }, + elements: [ + { + type: 'element', + name: 'BaseURL', + elements: [ + { + type: 'text', + text: Format.url + } + ] + }, + { + type: 'element', + name: 'SegmentBase', + attributes: { + indexRange: `${Format.indexStart}-${Format.indexEnd}` + }, + elements: [ + { + type: 'element', + name: 'Initialization', + attributes: { + range: `${Format.initStart}-${Format.initEnd}` + } + } + ] + } + ] + } + } } export { - DashUtils + DashUtils } diff --git a/src/tools/libpiped.js b/src/tools/libpiped.js index 88d27d9..a1a81ae 100644 --- a/src/tools/libpiped.js +++ b/src/tools/libpiped.js @@ -7,74 +7,86 @@ TimeAgo.addDefaultLocale(en) const timeAgo = new TimeAgo('en-US') class LibPiped { - intlDTF = new Intl.DateTimeFormat([], { - dateStyle: 'full', - timeStyle: 'full' - }) + intlDTF = new Intl.DateTimeFormat([], { + dateStyle: 'full', + timeStyle: 'full' + }) - pad (num, size) { - return ('000' + num).slice(size * -1) - } + pad (num, size) { + return ('000' + num).slice(size * -1) + } - timeFormat (duration) { - const time = parseFloat(duration).toFixed(3) - const hours = Math.floor(time / 60 / 60) - const minutes = Math.floor(time / 60) % 60 - const seconds = Math.floor(time - minutes * 60) + timeFormat (duration) { + const time = parseFloat(duration).toFixed(3) + const hours = Math.floor(time / 60 / 60) + const minutes = Math.floor(time / 60) % 60 + const seconds = Math.floor(time - minutes * 60) - let str = '' + let str = '' - if (hours > 0) str += hours + ':' + if (hours > 0) str += hours + ':' - str += this.pad(minutes, 2) + ':' + this.pad(seconds, 2) + str += this.pad(minutes, 2) + ':' + this.pad(seconds, 2) - return str - } + return str + } - formatFullDate (date) { - return this.intlDTF.format(date) - } + formatFullDate (date) { + return this.intlDTF.format(date) + } - numberFormat (num) { - const digits = 2 - const si = [ - { value: 1, symbol: '' }, - { value: 1e3, symbol: 'K' }, - { value: 1e6, symbol: 'M' }, - { value: 1e9, symbol: 'B' } - ] - const rx = /\.0+$|(\.[0-9]*[1-9])0+$/ - let i - for (i = si.length - 1; i > 0; i--) { - if (num >= si[i].value) { - break - } - } - return (num / si[i].value).toFixed(digits).replace(rx, '$1') + si[i].symbol - } + numberFormat (num) { + const digits = 2 + const si = [ + { + value: 1, + symbol: '' + }, + { + value: 1e3, + symbol: 'K' + }, + { + value: 1e6, + symbol: 'M' + }, + { + value: 1e9, + symbol: 'B' + } + ] + const rx = /\.0+$|(\.[0-9]*[1-9])0+$/ + let i + for (i = si.length - 1; i > 0; i--) { + if (num >= si[i].value) { + break + } + } + return (num / si[i].value).toFixed(digits).replace(rx, '$1') + si[i].symbol + } - addCommas (num) { - num = parseInt(num) - return num.toLocaleString('en-US') - } + addCommas (num) { + num = parseInt(num) + return num.toLocaleString('en-US') + } - purifyHTML (original) { - return DOMPurify.sanitize(original) - } + purifyHTML (original) { + return DOMPurify.sanitize(original) + } - timeAgo (time) { - return timeAgo.format(time) - } + timeAgo (time) { + return timeAgo.format(time) + } - determineVideoIdFromPath (path) { - const loc = new URL(path, 'http://localhost') - return loc.searchParams.get('v') - } + determineVideoIdFromPath (path) { + const loc = new URL(path, 'http://localhost') + return loc.searchParams.get('v') + } - determineVideoIdFromChannelURL (path) { - const pathParts = path.split('/') - return pathParts[2] - } + determineVideoIdFromChannelURL (path) { + const pathParts = path.split('/') + return pathParts[2] + } } const lp = new LibPiped() diff --git a/vue.config.js b/vue.config.js index 89de98f..6f8637d 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,26 +1,26 @@ module.exports = { - transpileDependencies: [ - 'vuetify' - ], - pwa: { - name: 'Piped Material', - themeColor: '#458588', - msTileColor: '', - appleMobileWebAppCapable: 'yes', - appleMobileWebAppStatusBarStyle: 'black', + transpileDependencies: [ + 'vuetify' + ], + pwa: { + name: 'Piped Material', + themeColor: '#458588', + msTileColor: '', + appleMobileWebAppCapable: 'yes', + appleMobileWebAppStatusBarStyle: 'black', - workboxOptions: { - navigateFallback: 'index.html', - skipWaiting: true, - importWorkboxFrom: 'local', - runtimeCaching: [ - { - urlPattern: /\.(?:png|svg|ico)$/, - handler: 'CacheFirst' - } - ] - } - }, + workboxOptions: { + navigateFallback: 'index.html', + skipWaiting: true, + importWorkboxFrom: 'local', + runtimeCaching: [ + { + urlPattern: /\.(?:png|svg|ico)$/, + handler: 'CacheFirst' + } + ] + } + }, - lintOnSave: false + lintOnSave: false }