- smaller json structure for xml conv
- fix minor typos and bugs in scripts/
- parallel fetching upto 2 concurrent segments
This commit is contained in:
Shiny Nematoda 2023-09-02 11:21:25 +00:00
parent 62e7cffbc4
commit c7add3708e
15 changed files with 115 additions and 148 deletions

View file

@ -152,7 +152,7 @@ onMounted(() => {
v-if="data.state.art"
class="art"
loading="lazy"
:src="data.state.art.replaceAll('&', '&')" />
:src="data.state.art" />
<div class="wrapper">
<NowPlaying @get-artist="artist.getArtist" />
@ -207,6 +207,7 @@ onMounted(() => {
<style>
@import './assets/base.css';
@import 'bootstrap-icons/font/bootstrap-icons.css';
#app {
max-width: 1280px;

View file

@ -1,3 +1,3 @@
{
"date": "2023-08-30"
"date": "2023-09-02"
}

View file

@ -32,7 +32,9 @@ async function getCharts() {
console.log(json);
if (!id.value)
id.value = json.options.all.find(i => store.cc ? i.id == store.cc : i.title == json.options.default).id;
id.value = json.options.all.find(i =>
store.cc ? i.id == store.cc : i.title == json.options.default,
).id;
for (const country of json.options.all) {
const locId = `countries.${country.id}`,

View file

@ -169,7 +169,7 @@ const setProxy = async () => {
format: 'Hyperpipe',
version: 0,
app_version: 0,
subscriptions: JSON.parse( store.subs || '[]' ).map(id => ({
subscriptions: JSON.parse(store.subs || '[]').map(id => ({
url: 'https://www.youtube.com/channel/' + id,
service_id: 0,
})),

View file

@ -75,6 +75,9 @@ async function Stream() {
manifest: {
disableVideo: true,
},
streaming: {
segmentPrefetchLimit: 2,
},
});
window.audioPlayer = audioPlayer;
@ -109,7 +112,7 @@ async function Stream() {
})
.catch(err => {
console.error(err);
useAlert.add('error: ' + e.code)
useAlert.add('error: ' + e.code);
});
}
}

View file

@ -3,8 +3,6 @@ import { createPinia } from 'pinia';
import App from './App.vue';
import('bootstrap-icons/font/bootstrap-icons.css');
const pinia = createPinia(),
app = createApp(App);

View file

@ -12,64 +12,53 @@ export function useDash(streams, len) {
else {
mimeTypes.push(stream.mimeType);
mimes.push([]);
mimes[mimeTypes.length - 1].push(stream);
mimes.at(-1).push(stream);
}
});
for (let i in mimeTypes) {
const set = {
type: 'element',
name: 'AdaptationSet',
attributes: {
attr: {
id: i,
contentType: 'audio',
mimeType: mimeTypes[i],
startWithSAP: '1',
subsegmentAlignment: 'true',
},
elements: [],
child: [],
};
mimes[i].forEach(format => {
const audio = {
type: 'element',
name: 'Representation',
attributes: {
attr: {
id: format.itag,
codecs: format.codec,
bandwidth: format.bitrate,
},
elements: [
child: [
{
type: 'element',
name: 'AudioChannelConfiguration',
attributes: {
attr: {
schemeIdUri:
'urn:mpeg:dash:23003:3:audio_channel_configuration:2011',
value: '2',
},
},
{
type: 'element',
name: 'BaseURL',
elements: [
{
type: 'text',
text: format.url,
},
],
child: [format.url],
},
{
type: 'element',
name: 'SegmentBase',
attributes: {
attr: {
indexRange: `${format.indexStart}-${format.indexEnd}`,
},
elements: [
child: [
{
type: 'element',
name: 'Initialization',
attributes: {
attr: {
range: `${format.initStart}-${format.initEnd}`,
},
},
@ -78,43 +67,31 @@ export function useDash(streams, len) {
],
};
set.elements.push(audio);
set.child.push(audio);
});
sets.push(set);
}
const gen = {
declaration: {
attributes: {
version: '1.0',
encoding: 'utf-8',
const gen = [
{
name: 'MPD',
attr: {
xmlns: 'urn:mpeg:dash:schema:mpd:2011',
profiles: 'urn:mpeg:dash:profile:full:2011',
minBufferTime: 'PT1.5S',
type: 'static',
mediaPresentationDuration: `PT${len}S`,
},
},
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${len}S`,
child: [
{
name: 'Period',
attr: { id: 0 },
child: sets,
},
elements: [
{
type: 'element',
name: 'Period',
attributes: {
id: 0,
},
elements: sets,
},
],
},
],
};
],
},
];
return useXML(gen);
}

View file

@ -14,9 +14,7 @@ export function useSetupDB() {
};
req.onerror = e => {
console.log('Please let me use indexedDB!!');
console.log(e);
alert(e.target.error.message);
console.error(e);
};
req.onsuccess = e => {
@ -33,7 +31,7 @@ export function useUpdatePlaylist(key, obj, cb = () => null) {
req = store.get(key);
req.onerror = e => {
console.log('Error!!', e);
console.error(e);
};
req.onsuccess = e => {
@ -71,7 +69,7 @@ export function useCreatePlaylist(key, obj, cb = () => null) {
cb(res);
} else {
console.log(e.target.result);
console.error(e.target.result);
alert(`Error: Playlist with name ${key} exists`);
}
};
@ -101,7 +99,7 @@ export function useGetPlaylist(key, cb = () => null) {
req = store.get(key);
req.onerror = e => {
console.log('Error!!', e);
console.error(e);
};
req.onsuccess = e => {

View file

@ -56,7 +56,7 @@ export async function getJsonAuth(path, opts = {}) {
return await fetch('https://' + root + path, opts)
.then(res => res.json())
.catch(err => {
useAlert.add(err);
useAlert().add(err);
});
}

View file

@ -1,6 +1,10 @@
import DOMPurify from 'dompurify';
import { useI18n } from '@/stores/misc.js';
export const AMP = /&amp;/g;
export const PL_EXP =
/[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}/;
export function useSanitize(txt) {
return DOMPurify.sanitize(txt, {
ALLOWED_TAGS: ['br'],
@ -8,9 +12,7 @@ export function useSanitize(txt) {
}
export function useVerifyAuth(hash) {
return /[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}/.test(
hash,
);
return PL_EXP.test(hash);
}
export function useRoute(l) {
@ -45,7 +47,7 @@ export function useStore() {
export function useShare(data) {
if ('share' in navigator) {
navigator.share(data).catch(err => {
console.err(err);
console.error(err);
});
} else {
const { t } = useI18n();
@ -77,8 +79,6 @@ export function useMetadata(url, urls, data) {
type: 'image/webp',
}));
} else if (data.art) artwork = [{ src: data.art, type: 'image/webp' }];
console.log(album, artwork);
}
navigator.mediaSession.metadata = new MediaMetadata({
@ -90,22 +90,22 @@ export function useMetadata(url, urls, data) {
}
}
export async function useManifest(res) {
export async function useManifest({ streams, duration, hls }) {
let url, mime;
if (window.MediaSource !== undefined && res.streams.length > 0) {
if (window.MediaSource !== undefined && streams.length > 0) {
const { useDash } = await import('./dash.js');
const dash = useDash(res.streams, res.duration);
const dash = useDash(streams, duration);
url = 'data:application/dash+xml;charset=utf-8;base64,' + btoa(dash);
mime = 'application/dash+xml';
} else if (res.hls) {
url = res.hls;
} else if (hls) {
url = hls;
mime = 'application/x-mpegURL';
} else if (res.streams.length > 0) {
url = res.streams[0].url;
mime = res.streams[0].mimeType;
} else if (streams.length > 0) {
url = streams[0].url;
mime = streams[0].mimeType;
}
return { url, mime };

View file

@ -3,8 +3,7 @@ function useAttr(json) {
for (const attr in json) {
if (json[attr] != null) {
attrs += ' ';
attrs += attr;
attrs += ' ' + attr;
attrs += '="';
attrs += ('' + json[attr]).replace(/"/g, '&quote;');
attrs += '"';
@ -18,22 +17,18 @@ function useElems(json) {
let elems = '';
json.forEach(elem => {
switch (elem.type) {
case 'element':
elems += '<';
elems += elem.name;
elems += useAttr(elem.attributes);
switch (typeof elem) {
case 'object':
elems += '<' + elem.name + useAttr(elem.attr);
if (elem?.elements?.length > 0) {
elems += '>';
elems += useElems(elem.elements);
elems += '</';
elems += elem.name;
if (elem?.child?.length > 0) {
elems += '>';
elems += useElems(elem.child);
elems += '</' + elem.name + '>';
} else elems += '/>';
break;
case 'text':
elems += ('' + elem.text)
case 'string':
elems += ('' + elem)
.replace(/&amp;/g, '&')
.replace(/&/g, '&amp;')
.replace(/>/g, '&gt;')
@ -48,13 +43,8 @@ function useElems(json) {
export function useXML(json) {
json = JSON.parse(JSON.stringify(json));
let base = '';
let xml = '<?xml version="1.0" encoding="utf-8" ?>';
xml += useElems(json);
base += '<?xml';
base += useAttr(json.declaration.attributes);
base += '?>';
base += useElems(json.elements);
return base;
return xml;
}

View file

@ -78,7 +78,7 @@ export const SUPPORTED_LOCALES = [
{
code: 'nl',
name: 'Nederlands',
cc: 'NL'
cc: 'NL',
},
{
code: 'pt',
@ -162,10 +162,10 @@ export const useI18n = defineStore('i18n', () => {
locale.value = code;
localStorage.locale ??= code;
if (localStorage.to_cc == 'true') {
const cc = SUPPORTED_LOCALES.find(i => i.code == code).cc
if (cc) localStorage.cc = cc
else localStorage.cc = ''
} else localStorage.cc = ''
const cc = SUPPORTED_LOCALES.find(i => i.code == code).cc;
if (cc) localStorage.cc = cc;
else localStorage.cc = '';
} else localStorage.cc = '';
});
}
@ -182,7 +182,7 @@ export const useAlert = defineStore('alert', () => {
setTimeout(() => {
if (msg.value == m) msg.value = '';
}, 3000);
}, 4000);
}
return { msg, add };

View file

@ -2,7 +2,7 @@ import { reactive } from 'vue';
import { defineStore } from 'pinia';
import { getJsonPiped, getJsonHyp } from '@/scripts/fetch.js';
import { useStore, useMetadata } from '@/scripts/util.js';
import { useStore, useMetadata, AMP } from '@/scripts/util.js';
const store = useStore();
@ -24,12 +24,10 @@ export const useData = defineStore('data', () => {
const hash = new URLSearchParams(e.substring(e.indexOf('?'))).get('v'),
json = await getJsonPiped('/streams/' + hash);
state.art = json.thumbnailUrl.replaceAll('&amp;', '&');
state.art = json.thumbnailUrl.replace(AMP, '&');
state.description = json.description;
state.title = json.title.replaceAll('&amp;', '&');
state.artist = json.uploader
.replace(' - Topic', '')
.replaceAll('&amp;', '&');
state.title = json.title.replace(AMP, '&');
state.artist = json.uploader.replace(' - Topic', '').replace(AMP, '&');
state.artistUrl = json.uploaderUrl;
player.state.duration = json.duration;
player.state.hls = json.hls;