mirror of
https://codeberg.org/Hyperpipe/Hyperpipe
synced 2025-06-27 20:58:01 +02:00
Cleanup:
- smaller json structure for xml conv - fix minor typos and bugs in scripts/ - parallel fetching upto 2 concurrent segments
This commit is contained in:
parent
62e7cffbc4
commit
c7add3708e
15 changed files with 115 additions and 148 deletions
|
@ -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;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"date": "2023-08-30"
|
||||
"date": "2023-09-02"
|
||||
}
|
||||
|
|
|
@ -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}`,
|
||||
|
|
|
@ -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,
|
||||
})),
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import DOMPurify from 'dompurify';
|
||||
import { useI18n } from '@/stores/misc.js';
|
||||
|
||||
export const 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 };
|
||||
|
|
|
@ -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, '"e;');
|
||||
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(/&/g, '&')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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('&', '&');
|
||||
state.art = json.thumbnailUrl.replace(AMP, '&');
|
||||
state.description = json.description;
|
||||
state.title = json.title.replaceAll('&', '&');
|
||||
state.artist = json.uploader
|
||||
.replace(' - Topic', '')
|
||||
.replaceAll('&', '&');
|
||||
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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue