mirror of
https://codeberg.org/Hyperpipe/Hyperpipe
synced 2025-06-27 12:48:01 +02:00
Initial support for offline playback
This commit is contained in:
parent
1372d87beb
commit
4353cacead
9 changed files with 116 additions and 50 deletions
60
package-lock.json
generated
60
package-lock.json
generated
|
@ -12,7 +12,7 @@
|
|||
"dompurify": "^3.0.3",
|
||||
"mux.js": "^6.3.0",
|
||||
"peerjs": "^1.4.7",
|
||||
"pinia": "^2.1.3",
|
||||
"pinia": "^2.1.4",
|
||||
"shaka-player": "^4.3.6",
|
||||
"sortablejs": "^1.15.0",
|
||||
"vue": "^3.2.38"
|
||||
|
@ -2242,9 +2242,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.0.tgz",
|
||||
"integrity": "sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==",
|
||||
"version": "20.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
|
||||
"integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
|
@ -2405,9 +2405,9 @@
|
|||
"integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz",
|
||||
"integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
|
@ -2567,9 +2567,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.21.7",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz",
|
||||
"integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==",
|
||||
"version": "4.21.9",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz",
|
||||
"integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2586,8 +2586,8 @@
|
|||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001489",
|
||||
"electron-to-chromium": "^1.4.411",
|
||||
"caniuse-lite": "^1.0.30001503",
|
||||
"electron-to-chromium": "^1.4.431",
|
||||
"node-releases": "^2.0.12",
|
||||
"update-browserslist-db": "^1.0.11"
|
||||
},
|
||||
|
@ -2630,9 +2630,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001499",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001499.tgz",
|
||||
"integrity": "sha512-IhoQqRrW6WiecFcfZgoJS1YLEN1/HR1vHP5WNgjCARRW7KUNToHHTX3FrwCM+y4zkRa48D9rE90WFYc2IWhDWQ==",
|
||||
"version": "1.0.30001505",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001505.tgz",
|
||||
"integrity": "sha512-jaAOR5zVtxHfL0NjZyflVTtXm3D3J9P15zSJ7HmQF8dSKGA6tqzQq+0ZI3xkjyQj46I4/M0K2GbMpcAFOcbr3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2706,9 +2706,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.30.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz",
|
||||
"integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==",
|
||||
"version": "3.31.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.0.tgz",
|
||||
"integrity": "sha512-hM7YCu1cU6Opx7MXNu0NuumM0ezNeAeRKadixyiQELWY3vT3De9S4J5ZBMraWV2vZnrE1Cirl0GtFtDtMUXzPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"browserslist": "^4.21.5"
|
||||
|
@ -2800,9 +2800,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.427",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.427.tgz",
|
||||
"integrity": "sha512-HK3r9l+Jm8dYAm1ctXEWIC+hV60zfcjS9UA5BDlYvnI5S7PU/yytjpvSrTNrSSRRkuu3tDyZhdkwIczh+0DWaw==",
|
||||
"version": "1.4.434",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.434.tgz",
|
||||
"integrity": "sha512-5Gvm09UZTQRaWrimRtWRO5rvaX6Kpk5WHAPKDa7A4Gj6NIPuJ8w8WNpnxCXdd+CJJt6RBU6tUw0KyULoW6XuHw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eme-encryption-scheme-polyfill": {
|
||||
|
@ -4056,9 +4056,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.3.tgz",
|
||||
"integrity": "sha512-XNA/z/ye4P5rU1pieVmh0g/hSuDO98/a5UC8oSP0DNdvt6YtetJNHTrXwpwsQuflkGT34qKxAEcp7lSxXNjf/A==",
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.4.tgz",
|
||||
"integrity": "sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.5.0",
|
||||
"vue-demi": ">=0.14.5"
|
||||
|
@ -4329,9 +4329,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "3.25.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.0.tgz",
|
||||
"integrity": "sha512-FnJkNRst2jEZGw7f+v4hFo6UTzpDKrAKcHZWcEfm5/GJQ5CK7wgb4moNLNAe7npKUev7yQn1AY/YbZRIxOv6Qg==",
|
||||
"version": "3.25.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.1.tgz",
|
||||
"integrity": "sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
|
@ -4651,9 +4651,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.17.7",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.17.7.tgz",
|
||||
"integrity": "sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==",
|
||||
"version": "5.18.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz",
|
||||
"integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"dompurify": "^3.0.3",
|
||||
"mux.js": "^6.3.0",
|
||||
"peerjs": "^1.4.7",
|
||||
"pinia": "^2.1.3",
|
||||
"pinia": "^2.1.4",
|
||||
"shaka-player": "^4.3.6",
|
||||
"sortablejs": "^1.15.0",
|
||||
"vue": "^3.2.38"
|
||||
|
|
|
@ -93,12 +93,12 @@ function parseUrl() {
|
|||
|
||||
function playThis(t) {
|
||||
const i = data.state.urls.indexOf(t);
|
||||
data.getSong(data.state.urls[i].url);
|
||||
data.play(data.state.urls[i]);
|
||||
}
|
||||
|
||||
function playList(a) {
|
||||
data.state.urls = a;
|
||||
data.getSong(data.state.urls[0].url);
|
||||
data.play(data.state.urls[0]);
|
||||
}
|
||||
|
||||
/* Lifestyle hooks */
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"date": "2023-06-13"
|
||||
"date": "2023-06-20"
|
||||
}
|
||||
|
|
|
@ -75,10 +75,8 @@ const setProxy = async () => {
|
|||
title: 'Local • ' + key,
|
||||
items: res.urls.map(i => ({
|
||||
...i,
|
||||
...{
|
||||
playlistId: key,
|
||||
thumbnail: parseThumb(i.url, proxy.value),
|
||||
},
|
||||
playlistId: key,
|
||||
thumbnail: parseThumb(i.url, proxy.value),
|
||||
})),
|
||||
});
|
||||
|
||||
|
@ -86,6 +84,23 @@ const setProxy = async () => {
|
|||
} else alert('No songs to play!');
|
||||
});
|
||||
},
|
||||
OpenOffline = async () => {
|
||||
if (window.offline) {
|
||||
const songs = await window.offline.list();
|
||||
console.log();
|
||||
results.resetItems();
|
||||
results.setItem('songs', {
|
||||
title: 'Hyp • Offline',
|
||||
items: songs.map(i => ({
|
||||
...i.appMetadata,
|
||||
offlineUri: i.offlineUri,
|
||||
thumbnail: parseThumb(i.appMetadata.url, proxy.value),
|
||||
duration: i.duration
|
||||
})),
|
||||
});
|
||||
nav.state.page = 'home';
|
||||
}
|
||||
},
|
||||
List = async () => {
|
||||
if (!proxy.value) await setProxy();
|
||||
|
||||
|
@ -421,6 +436,7 @@ onMounted(async () => {
|
|||
<h2 v-if="list.length > 0">{{ t('playlist.local') }}</h2>
|
||||
|
||||
<div class="grid-3">
|
||||
<AlbumItem name="Offline" :grad="useRand()" @open-album="OpenOffline()" />
|
||||
<AlbumItem
|
||||
v-for="i in list"
|
||||
:key="i.name"
|
||||
|
|
|
@ -60,6 +60,18 @@ async function Stream() {
|
|||
}
|
||||
});
|
||||
|
||||
window.offline = new shaka.offline.Storage(audioPlayer);
|
||||
window.offline.configure({
|
||||
offline: {
|
||||
progressCallback: (data, prog) => console.log(data, prog),
|
||||
trackSelectionCallback: tracks => [
|
||||
tracks
|
||||
.sort((a, b) => a.bandwidth - b.bandwidth)
|
||||
.find(i => i.type == 'variant'),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
audioPlayer.configure({
|
||||
preferredAudioCodecs: codecs ? codecs.split(':') : ['opus', 'mp4a'],
|
||||
manifest: {
|
||||
|
@ -171,7 +183,7 @@ onMounted(() => {
|
|||
if (data.state.urls.length > 2) {
|
||||
const i = data.state.urls.findIndex(s => s.url == data.state.url);
|
||||
|
||||
data.getSong(data.state.urls[i - 1].url);
|
||||
data.play(data.state.urls[i - 1]);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -179,7 +191,7 @@ onMounted(() => {
|
|||
if (data.state.urls.length > 2) {
|
||||
const i = data.state.urls.findIndex(s => s.url == data.state.url);
|
||||
|
||||
data.getSong(data.state.urls[i + 1].url);
|
||||
data.play(data.state.urls[i + 1]);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -34,6 +34,9 @@ const shuffleAdd = () => {
|
|||
url: i.url,
|
||||
title: i.title,
|
||||
thumbnails: [{ url: i.thumbnail }],
|
||||
thumbnail: i.thumbnail,
|
||||
offlineUri: i.offlineUri,
|
||||
duration: i.duration
|
||||
})),
|
||||
copy = [];
|
||||
|
||||
|
@ -56,9 +59,13 @@ const shuffleAdd = () => {
|
|||
url: i.url || '/watch?v=' + song.id,
|
||||
title: i.title,
|
||||
thumbnails: [{ url: i.thumbnail }],
|
||||
thumbnail: i.thumbnail,
|
||||
offlineUri: i.offlineUri,
|
||||
duration: i.duration
|
||||
}));
|
||||
|
||||
data.getSong(song.url || '/watch?v=' + song.id);
|
||||
song.url = song.url || '/watch?v=' + song.id;
|
||||
data.play(song);
|
||||
} else {
|
||||
emit('play-urls', [
|
||||
{
|
||||
|
@ -245,6 +252,9 @@ onDeactivated(() => {
|
|||
url: item.url,
|
||||
title: item.title,
|
||||
thumbnails: [{ url: item.thumbnail }],
|
||||
thumbnail: item.thumbnail,
|
||||
offlineUri: item.offlineUri,
|
||||
duration: item.duration
|
||||
})),
|
||||
)
|
||||
" />
|
||||
|
@ -313,9 +323,11 @@ onDeactivated(() => {
|
|||
:key="song.url || song.id"
|
||||
:index="index"
|
||||
:playlistId="song.playlistId"
|
||||
:author="song.uploaderName || song.subtitle"
|
||||
:author="song.uploaderName || song.artist || song.subtitle"
|
||||
:title="song.title || song.name"
|
||||
:channel="song.uploaderUrl || '/channel/' + song.subId"
|
||||
:channel="
|
||||
song.uploaderUrl || song.artistUrl || '/channel/' + song.subId
|
||||
"
|
||||
:play="song.url || '/watch?v=' + song.id"
|
||||
:art="
|
||||
song.thumbnail ||
|
||||
|
|
|
@ -75,6 +75,17 @@ function Save() {
|
|||
}
|
||||
}
|
||||
|
||||
async function Offline() {
|
||||
if (window.offline && data.state.url) {
|
||||
window.offline.store(window.audioPlayer.getAssetUri(), {
|
||||
title: data.state.title,
|
||||
url: data.state.url,
|
||||
artist: data.state.artist,
|
||||
artistUrl: data.state.artistUrl,
|
||||
});
|
||||
} else console.error('no offline storage found');
|
||||
}
|
||||
|
||||
async function Like() {
|
||||
liking.value = true;
|
||||
|
||||
|
@ -279,6 +290,12 @@ async function Like() {
|
|||
:data-loading="liking"
|
||||
@click="Like"></button>
|
||||
|
||||
<button
|
||||
id="dl-btn"
|
||||
title="Save for Offline Playback"
|
||||
class="bi bi-download clickable"
|
||||
@click="Offline"></button>
|
||||
|
||||
<button
|
||||
id="addToPlaylist"
|
||||
title="Add Current Song to a Playlist"
|
||||
|
|
|
@ -39,6 +39,15 @@ export const useData = defineStore('data', () => {
|
|||
await getNext(hash);
|
||||
}
|
||||
|
||||
async function play(song) {
|
||||
if (song.offlineUri) {
|
||||
state.art = song.thumbnail;
|
||||
player.state.duration = song.duration
|
||||
for (let i of ['title', 'artist', 'artistUrl', 'url']) state[i] = song[i];
|
||||
window.audioPlayer.load(song.offlineUri);
|
||||
} else await getSong(song.url);
|
||||
}
|
||||
|
||||
async function getNext(hash) {
|
||||
if (
|
||||
store.getItem('next') !== 'false' &&
|
||||
|
@ -97,34 +106,34 @@ export const useData = defineStore('data', () => {
|
|||
state.urls.length != 0 &&
|
||||
state.urls[i + 1]
|
||||
)
|
||||
getSong(state.urls[i + 1].url);
|
||||
play(state.urls[i + 1]);
|
||||
else if (player.state.loop == 1) {
|
||||
state.url = state.urls[0].url;
|
||||
getSong(state.urls[0].url);
|
||||
play(state.urls[0]);
|
||||
} else state.urls = [];
|
||||
}
|
||||
|
||||
function prevTrack() {
|
||||
const i = state.urls.findIndex(s => s.url === state.url);
|
||||
|
||||
if (state.urls[i - 1]) getSong(state.urls[i - 1].url);
|
||||
if (state.urls[i - 1]) play(state.urls[i - 1]);
|
||||
else if (player.state.loop == 1) {
|
||||
state.url = state.urls[state.urls.length - 1].url;
|
||||
getSong(state.urls[state.urls.length - 1].url);
|
||||
play(state.urls[state.urls.length - 1]);
|
||||
} else state.urls = [];
|
||||
}
|
||||
|
||||
function nextTrack() {
|
||||
const i = state.urls.findIndex(s => s.url === state.url);
|
||||
|
||||
if (state.urls[i + 1]) getSong(state.urls[i + 1].url);
|
||||
if (state.urls[i + 1]) play(state.urls[i + 1]);
|
||||
else if (player.state.loop == 1) {
|
||||
state.url = state.urls[0].url;
|
||||
getSong(state.urls[0].url);
|
||||
play(state.urls[0]);
|
||||
} else state.urls = [];
|
||||
}
|
||||
|
||||
return { state, getSong, playNext, prevTrack, nextTrack };
|
||||
return { state, getSong, play, playNext, prevTrack, nextTrack };
|
||||
});
|
||||
|
||||
export const usePlayer = defineStore('player', () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue