diff --git a/package.json b/package.json index 272dccd..ba6dd39 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "lint": "vue-cli-service lint" }, "dependencies": { - "@babel/eslint-parser": "^7.19.1", + "@babel/eslint-parser": "^7.21.3", "@fontsource/baloo-da-2": "^4.5.12", "@fontsource/material-icons-rounded": "^4.5.4", "@fontsource/noto-naskh-arabic": "^4.5.12", @@ -21,9 +21,9 @@ "@mdi/js": "^6.9.96", "antar-hinted": "https://bafybeifmuaik6ozj65yoy443khijb6d5pqkmi4dwgykfzfvymtvc57mo6i.ipfs.cf-ipfs.com/?filename=hinted-antar-web.tar", "axios": "^0.27.2", - "core-js": "^3.27.2", + "core-js": "^3.29.1", "dexie": "^3.2.3", - "dompurify": "^2.4.3", + "dompurify": "^2.4.5", "format-duration": "^1.4.0", "i18n-iso-countries": "^6.8.0", "javascript-time-ago": "https://bafybeicafhqxxnrmlt5fipy7zf46je4u6iwady7fiqrwcszhpk7ztno6pe.ipfs.cf-ipfs.com/?filename=jta.tar.gz", @@ -60,7 +60,7 @@ "eslint-plugin-standard": "^5.0.0", "eslint-plugin-vue": "^7.20.0", "lint-staged": "^11.2.6", - "sass": "~1.58.0", + "sass": "~1.59.3", "stream-browserify": "^3.0.0", "vue-cli-plugin-vuetify": "~2.5.8", "vue-template-compiler": "^2.7.14", diff --git a/src/components/Player.vue b/src/components/Player.vue index 506a005..4e27e71 100644 --- a/src/components/Player.vue +++ b/src/components/Player.vue @@ -19,7 +19,7 @@ import shaka from 'shaka-player/dist/shaka-player.ui.js' import 'shaka-player/dist/controls.css' import * as LocaleMatcher from '@formatjs/intl-localematcher' -import { DashUtils } from '@/tools/DashUtils' +import * as DashUtils from '@/tools/DashUtils' import '@/components/Player.scss' // Fonts for Shaka @@ -107,7 +107,7 @@ export default { 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( + const dash = DashUtils.generateDashFileFromFormats( streams, this.video.duration ) diff --git a/src/router/index.js b/src/router/index.js index cd5552a..36ac85f 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -44,7 +44,7 @@ const routes = [ component: () => import('@/routes/PlaylistPage') }, { - path: '/:path(v|w|embed|shorts|watch)/:v?', + path: '/:path(v|w|embed|live|shorts|watch)/:v?', name: 'WatchVideo', component: () => import('@/routes/WatchVideo') }, diff --git a/src/tools/DashUtils.js b/src/tools/DashUtils.js index e322581..8e71a11 100644 --- a/src/tools/DashUtils.js +++ b/src/tools/DashUtils.js @@ -1,201 +1,204 @@ // Based of https://github.com/GilgusMaximus/yt-dash-manifest-generator/blob/master/src/DashGenerator.js - import { json2xml } from 'xml-js' -const DashUtils = { - generate_dash_file_from_formats (VideoFormats, VideoLength) { - const generatedJSON = this.generate_xmljs_json_from_data(VideoFormats, VideoLength) - return json2xml(generatedJSON) - }, - generate_xmljs_json_from_data (VideoFormatArray, VideoLength) { - return { - declaration: { +export function generateDashFileFromFormats (VideoFormats, VideoLength) { + const generatedJSON = generateJSONFromData(VideoFormats, VideoLength) + return json2xml(generatedJSON) +} + +function generateJSONFromData (VideoFormatArray, VideoLength) { + const convertJSON = { + declaration: { + attributes: { + version: '1.0', + encoding: 'utf-8' + } + }, + elements: [ + { + type: 'element', + name: 'MPD', 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) - } - ] - } - ] + 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: generateAdaptationSet(VideoFormatArray) + } + ] + } + ] + } + return convertJSON +} + +function generateAdaptationSet (VideoFormatArray) { + const adaptationSets = [] + + const mimeAudioObjs = [] + + VideoFormatArray.forEach(videoFormat => { + // the dual formats should not be used + if ( + (videoFormat.mimeType.includes('video') && !videoFormat.videoOnly) || + videoFormat.mimeType.includes('application') + ) { + return } - }, - generate_adaptation_set (VideoFormatArray) { - const adaptationSets = [] - const mimeAudioObjs = [] + const audioTrackId = videoFormat.audioTrackId + const mimeType = videoFormat.mimeType - VideoFormatArray.forEach(videoFormat => { - // the dual formats should not be used - if (videoFormat.mimeType.indexOf('video') !== -1 && !videoFormat.videoOnly) { + for (let i = 0; i < mimeAudioObjs.length; i++) { + const mimeAudioObj = mimeAudioObjs[i] + + if (mimeAudioObj.audioTrackId === audioTrackId && mimeAudioObj.mimeType === mimeType) { + mimeAudioObj.videoFormats.push(videoFormat) return } + } - const audioTrackId = videoFormat.audioTrackId - const mimeType = videoFormat.mimeType - - for (let i = 0; i < mimeAudioObjs.length; i++) { - const mimeAudioObj = mimeAudioObjs[i] - - if (mimeAudioObj.audioTrackId === audioTrackId && mimeAudioObj.mimeType === mimeType) { - mimeAudioObj.videoFormats.push(videoFormat) - return - } - } - - mimeAudioObjs.push({ - audioTrackId, - mimeType, - videoFormats: [videoFormat] - }) + mimeAudioObjs.push({ + audioTrackId, + mimeType, + videoFormats: [videoFormat] }) + }) - mimeAudioObjs.forEach(mimeAudioObj => { - const adapSet = { - type: 'element', - name: 'AdaptationSet', - attributes: { - id: mimeAudioObj.audioTrackId, - lang: mimeAudioObj.audioTrackId?.substr(0, 2), - mimeType: mimeAudioObj.mimeType, - startWithSAP: '1', - subsegmentAlignment: 'true' - }, - elements: [] - } + mimeAudioObjs.forEach(mimeAudioObj => { + const adapSet = { + type: 'element', + name: 'AdaptationSet', + attributes: { + id: mimeAudioObj.audioTrackId, + lang: mimeAudioObj.audioTrackId?.substr(0, 2), + mimeType: mimeAudioObj.mimeType, + startWithSAP: '1', + subsegmentAlignment: 'true' + }, + elements: [] + } - let isVideoFormat = false + let isVideoFormat = false - if (mimeAudioObj.mimeType.includes('video')) { - isVideoFormat = true - } + if (mimeAudioObj.mimeType.includes('video')) { + isVideoFormat = true + } + if (isVideoFormat) { + adapSet.attributes.scanType = 'progressive' + } + + for (let i = 0; i < mimeAudioObj.videoFormats.length; i++) { + const videoFormat = mimeAudioObj.videoFormats[i] if (isVideoFormat) { - adapSet.attributes.scanType = 'progressive' + adapSet.elements.push(generateRepresentationVideo(videoFormat)) + } else { + adapSet.elements.push(generateRepresentationAudio(videoFormat)) } + } - for (let i = 0; i < mimeAudioObj.videoFormats.length; i++) { - const videoFormat = mimeAudioObj.videoFormats[i] - if (isVideoFormat) { - adapSet.elements.push(this.generate_representation_video(videoFormat)) - } else { - adapSet.elements.push(this.generate_representation_audio(videoFormat)) + adaptationSets.push(adapSet) + }) + return adaptationSets +} + +function generateRepresentationAudio (Format) { + const representation = { + 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' } - } - - 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: '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}` }, - { - type: 'element', - name: 'SegmentBase', - attributes: { - indexRange: `${Format.indexStart}-${Format.indexEnd}` - }, - elements: [ - { - type: 'element', - name: 'Initialization', - attributes: { - range: `${Format.initStart}-${Format.initEnd}` - } + elements: [ + { + type: 'element', + name: 'Initialization', + attributes: { + range: `${Format.initStart}-${Format.initEnd}` } - ] - } - ] - } + } + ] + } + ] } + return representation } -export { - DashUtils +function generateRepresentationVideo (Format) { + const representation = { + 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}` + } + } + ] + } + ] + } + return representation } diff --git a/yarn.lock b/yarn.lock index 206bb1f..8649182 100644 --- a/yarn.lock +++ b/yarn.lock @@ -68,10 +68,10 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/eslint-parser@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz#4f68f6b0825489e00a24b41b6a1ae35414ecd2f4" - integrity sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ== +"@babel/eslint-parser@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.21.3.tgz#d79e822050f2de65d7f368a076846e7184234af7" + integrity sha512-kfhmPimwo6k4P8zxNs8+T7yR44q1LdpsZdE1NkCsVlfiuTPRfnGgjaF8Qgug9q9Pou17u6wneYF0lDCZJATMFg== dependencies: "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" eslint-visitor-keys "^2.1.0" @@ -2667,10 +2667,10 @@ core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.8.3: browserslist "^4.21.3" semver "7.0.0" -core-js@^3.27.2: - version "3.27.2" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.27.2.tgz#85b35453a424abdcacb97474797815f4d62ebbf7" - integrity sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w== +core-js@^3.29.1: + version "3.29.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.29.1.tgz#40ff3b41588b091aaed19ca1aa5cb111803fa9a6" + integrity sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw== core-js@^3.8.3: version "3.24.1" @@ -3024,10 +3024,10 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -dompurify@^2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.3.tgz#f4133af0e6a50297fc8874e2eaedc13a3c308c03" - integrity sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ== +dompurify@^2.4.5: + version "2.4.5" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.5.tgz#0e89a27601f0bad978f9a924e7a05d5d2cccdd87" + integrity sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA== domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" @@ -6141,10 +6141,10 @@ sass-loader@^10: schema-utils "^3.0.0" semver "^7.3.2" -sass@~1.58.0: - version "1.58.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.58.0.tgz#ee8aea3ad5ea5c485c26b3096e2df6087d0bb1cc" - integrity sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg== +sass@~1.59.3: + version "1.59.3" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.59.3.tgz#a1ddf855d75c70c26b4555df4403e1bbf8e4403f" + integrity sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -6293,13 +6293,6 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -shaka-player@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-4.3.4.tgz#97c7cb73743884e55a4855f5efe4d116aff83848" - integrity sha512-fF90r/57Oxk+SV2cnoEpJxNujNj1gJkudKXVbXEBhgxh5qYu5gdFU+nT2N7jMN71KxQdtXo1NOqdNphudLEEug== - dependencies: - eme-encryption-scheme-polyfill "^2.1.1" - "shaka-player@https://f003.backblazeb2.com/file/Bronze8113/shaka-v4.3.4.tar": version "4.3.4" resolved "https://f003.backblazeb2.com/file/Bronze8113/shaka-v4.3.4.tar#a23a211ac79995bef1fe2b2c5726610276822181"