mirror of
https://github.com/mmjee/Piped-Material.git
synced 2024-12-06 19:26:36 +01:00
Match locales in a better way and port support from Piped for multilingual videos
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
"@fontsource/noto-sans-tc": "^4.5.11",
|
||||
"@fontsource/nunito-sans": "^4.5.9",
|
||||
"@fontsource/roboto": "^4.5.8",
|
||||
"@formatjs/intl-localematcher": "^0.2.31",
|
||||
"@mdi/js": "^6.9.96",
|
||||
"antar-hinted": "https://bafybeifmuaik6ozj65yoy443khijb6d5pqkmi4dwgykfzfvymtvc57mo6i.ipfs.cf-ipfs.com/?filename=hinted-antar-web.tar",
|
||||
"axios": "^0.27.2",
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import muxjs from 'mux.js'
|
||||
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 '@/components/Player.scss'
|
||||
@@ -28,7 +28,11 @@ import '@fontsource/roboto/latin.css'
|
||||
|
||||
import { setupKeybindings } from 'psychic-tiny-keys'
|
||||
|
||||
window.muxjs = muxjs
|
||||
if (!window.muxjs) {
|
||||
import('mux.js').then(m => {
|
||||
window.muxjs = m
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -142,8 +146,7 @@ export default {
|
||||
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.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
|
||||
@@ -215,8 +218,17 @@ export default {
|
||||
this.destroy()
|
||||
this.$ui = new shaka.ui.Overlay(localPlayer, this.$refs.container, videoEl)
|
||||
|
||||
const overflowMenuButtons = [
|
||||
'quality',
|
||||
'language',
|
||||
'captions',
|
||||
'picture_in_picture',
|
||||
'playback_rate',
|
||||
'airplay'
|
||||
]
|
||||
|
||||
const config = {
|
||||
overflowMenuButtons: ['quality', 'captions', 'picture_in_picture', 'playback_rate'],
|
||||
overflowMenuButtons,
|
||||
seekBarColors: {
|
||||
base: 'rgba(255, 255, 255, 0.3)',
|
||||
buffered: 'rgba(255, 255, 255, 0.54)',
|
||||
@@ -250,21 +262,31 @@ export default {
|
||||
if (qualityConds) this.$player.configure('abr.enabled', false)
|
||||
|
||||
player.load(uri, 0, mime).then(() => {
|
||||
let bestFitLanguage
|
||||
{
|
||||
const langs = player.getAudioLanguages()
|
||||
bestFitLanguage = LocaleMatcher.match([this.$i18n.locale], langs, 'en')
|
||||
player.selectAudioLanguage(bestFitLanguage)
|
||||
}
|
||||
|
||||
if (qualityConds) {
|
||||
let leastDiff = Number.MAX_VALUE
|
||||
let bestStream = null
|
||||
|
||||
let bestAudio = 0
|
||||
const tracks = player
|
||||
.getVariantTracks()
|
||||
.filter(track => track.language === bestFitLanguage || track.language === 'und')
|
||||
|
||||
// Choose the best audio stream
|
||||
if (quality >= 480) {
|
||||
player.getVariantTracks().forEach(track => {
|
||||
tracks.forEach(track => {
|
||||
const audioBandwidth = track.audioBandwidth
|
||||
if (audioBandwidth > bestAudio) bestAudio = audioBandwidth
|
||||
})
|
||||
}
|
||||
|
||||
player
|
||||
.getVariantTracks()
|
||||
tracks
|
||||
.sort((a, b) => a.bandwidth - b.bandwidth)
|
||||
.forEach(stream => {
|
||||
if (stream.audioBandwidth < bestAudio) return
|
||||
|
||||
@@ -44,6 +44,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async loadPlaylists () {
|
||||
if (!this.$store.getters['auth/isCurrentlyAuthenticated']) {
|
||||
return
|
||||
}
|
||||
this.playlistItems = (await this.$store.dispatch('auth/makeRequest', {
|
||||
method: 'GET',
|
||||
path: '/user/playlists'
|
||||
|
||||
+2
-9
@@ -1,6 +1,7 @@
|
||||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
import TimeAgo from 'javascript-time-ago'
|
||||
import * as LocaleMatcher from '@formatjs/intl-localematcher'
|
||||
|
||||
import store from '@/store'
|
||||
import ENTranslations from '@/translations/en.json'
|
||||
@@ -164,15 +165,7 @@ export async function changeLocale (lang) {
|
||||
function initializeLocalLocale () {
|
||||
let lang = window.localStorage.getItem('LOCALE')
|
||||
if (lang == null) {
|
||||
for (const possible of navigator.languages) {
|
||||
if (SUPPORTED_LANGUAGES.includes(possible)) {
|
||||
lang = possible
|
||||
break
|
||||
}
|
||||
}
|
||||
if (lang == null) {
|
||||
lang = 'en'
|
||||
}
|
||||
lang = LocaleMatcher.match(navigator.languages, SUPPORTED_LANGUAGES, 'en')
|
||||
}
|
||||
return changeLocale(lang)
|
||||
}
|
||||
|
||||
+42
-28
@@ -1,11 +1,11 @@
|
||||
// Based of https://github.com/GilgusMaximus/yt-dash-manifest-generator/blob/master/src/DashGenerator.js
|
||||
|
||||
import xml from 'xml-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 xml.json2xml(generatedJSON)
|
||||
return json2xml(generatedJSON)
|
||||
},
|
||||
generate_xmljs_json_from_data (VideoFormatArray, VideoLength) {
|
||||
return {
|
||||
@@ -39,55 +39,69 @@ const DashUtils = {
|
||||
},
|
||||
generate_adaptation_set (VideoFormatArray) {
|
||||
const adaptationSets = []
|
||||
const mimeTypes = []
|
||||
const mimeObjects = [[]]
|
||||
// sort the formats by mime types
|
||||
|
||||
const mimeAudioObjs = []
|
||||
|
||||
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 audioTrackId = videoFormat.audioTrackId
|
||||
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 (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]
|
||||
})
|
||||
})
|
||||
// for each MimeType generate a new Adaptation set with Representations as sub elements
|
||||
for (let i = 0; i < mimeTypes.length; i++) {
|
||||
let isVideoFormat = false
|
||||
|
||||
mimeAudioObjs.forEach(mimeAudioObj => {
|
||||
const adapSet = {
|
||||
type: 'element',
|
||||
name: 'AdaptationSet',
|
||||
attributes: {
|
||||
id: i,
|
||||
mimeType: mimeTypes[i],
|
||||
id: mimeAudioObj.audioTrackId,
|
||||
lang: mimeAudioObj.audioTrackId?.substr(0, 2),
|
||||
mimeType: mimeAudioObj.mimeType,
|
||||
startWithSAP: '1',
|
||||
subsegmentAlignment: 'true'
|
||||
},
|
||||
elements: []
|
||||
}
|
||||
if (!mimeTypes[i].includes('audio')) {
|
||||
adapSet.attributes.scanType = 'progressive'
|
||||
|
||||
let isVideoFormat = false
|
||||
|
||||
if (mimeAudioObj.mimeType.includes('video')) {
|
||||
isVideoFormat = true
|
||||
}
|
||||
mimeObjects[i].forEach(format => {
|
||||
|
||||
if (isVideoFormat) {
|
||||
adapSet.attributes.scanType = 'progressive'
|
||||
}
|
||||
|
||||
for (let i = 0; i < mimeAudioObj.videoFormats.length; i++) {
|
||||
const videoFormat = mimeAudioObj.videoFormats[i]
|
||||
if (isVideoFormat) {
|
||||
adapSet.elements.push(this.generate_representation_video(format))
|
||||
adapSet.elements.push(this.generate_representation_video(videoFormat))
|
||||
} else {
|
||||
adapSet.elements.push(this.generate_representation_audio(format))
|
||||
adapSet.elements.push(this.generate_representation_audio(videoFormat))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
adaptationSets.push(adapSet)
|
||||
}
|
||||
})
|
||||
return adaptationSets
|
||||
},
|
||||
generate_representation_audio (Format) {
|
||||
|
||||
@@ -1033,6 +1033,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-4.5.8.tgz#56347764786079838faf43f0eeda22dd7328437f"
|
||||
integrity sha512-CnD7zLItIzt86q4Sj3kZUiLcBk1dSk81qcqgMGaZe7SQ1P8hFNxhMl5AZthK1zrDM5m74VVhaOpuMGIL4gagaA==
|
||||
|
||||
"@formatjs/intl-localematcher@^0.2.31":
|
||||
version "0.2.31"
|
||||
resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.2.31.tgz#aada2b1e58211460cedba56889e3c489117eb6eb"
|
||||
integrity sha512-9QTjdSBpQ7wHShZgsNzNig5qT3rCPvmZogS/wXZzKotns5skbXgs0I7J8cuN0PPqXyynvNVuN+iOKhNS2eb+ZA==
|
||||
dependencies:
|
||||
tslib "2.4.0"
|
||||
|
||||
"@hapi/hoek@^9.0.0":
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
|
||||
@@ -6699,7 +6706,7 @@ tsconfig-paths@^3.14.1:
|
||||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@^2.0.3, tslib@^2.1.0:
|
||||
tslib@2.4.0, tslib@^2.0.3, tslib@^2.1.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||
|
||||
Reference in New Issue
Block a user