mirror of
https://codeberg.org/Hyperpipe/Hyperpipe
synced 2025-06-27 20:58:01 +02:00
Merge pull request 'Update branch' (#1) from Hyperpipe/Hyperpipe:main into main
Reviewed-on: https://codeberg.org/HexagonCDN/Hyperpipe/pulls/1
This commit is contained in:
commit
098af5026d
49 changed files with 8355 additions and 813 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,6 +6,7 @@ yarn-debug.log*
|
|||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
log.txt
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
|
|
|
@ -5,8 +5,6 @@ pipeline:
|
|||
- npm install
|
||||
- npm run build
|
||||
when:
|
||||
path:
|
||||
exclude: ['*.md', '.gitea/*']
|
||||
event: ['push', 'pull_request']
|
||||
|
||||
surge:
|
||||
|
@ -18,8 +16,6 @@ pipeline:
|
|||
secrets: [surge_login, surge_token]
|
||||
when:
|
||||
branch: main
|
||||
path:
|
||||
exclude: ['*.md', '.gitea/*']
|
||||
event: ['push']
|
||||
|
||||
docker:
|
||||
|
@ -29,11 +25,10 @@ pipeline:
|
|||
repo: codeberg.org/hyperpipe/hyperpipe
|
||||
registry: codeberg.org
|
||||
tags: latest
|
||||
username: snematoda
|
||||
username:
|
||||
from_secret: cb_user
|
||||
password:
|
||||
from_secret: cb_token
|
||||
when:
|
||||
branch: main
|
||||
path:
|
||||
exclude: ['*.md', '.gitea/*']
|
||||
event: ['push']
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM --platform=$BUILDPLATFORM node:lts AS build
|
||||
FROM --platform=$BUILDPLATFORM node:lts-alpine AS build
|
||||
|
||||
WORKDIR /app/
|
||||
|
||||
|
@ -9,7 +9,7 @@ RUN --mount=type=cache,target=/root/.cache/node \
|
|||
npm install --prefer-offline && \
|
||||
npm run build
|
||||
|
||||
FROM --platform=$BUILDPLATFORM nginx:stable
|
||||
FROM --platform=$BUILDPLATFORM nginx:stable-alpine
|
||||
|
||||
COPY --from=build /app/dist/ /usr/share/nginx/html/
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
|
10
README.md
10
README.md
|
@ -1,5 +1,7 @@
|
|||
# Hyperpipe
|
||||
|
||||
<div align="center">
|
||||
|
||||

|
||||
|
||||
A Privacy Respecting Frontend for YouTube Music inspired and built with the help of [Piped][piped] and YouTube's InnerTube API.
|
||||
|
@ -7,6 +9,8 @@ A Privacy Respecting Frontend for YouTube Music inspired and built with the help
|
|||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## Disclaimer
|
||||
|
||||
**_HYPERPIPE IS VERY EXPIRIMENTAL, EXPECT_**
|
||||
|
@ -103,6 +107,7 @@ You can reach out to me personally on:
|
|||
- ViteJS -> [MIT][vite]
|
||||
- shaka-player -> [Apache-2.0][shaka]
|
||||
- PeerJS -> [MIT][peer]
|
||||
- DOMPurify -> [Apache-2.0][purify]
|
||||
- Bootstrap Icons -> [MIT][bi]
|
||||
- VueJS theme -> [MIT][vuetheme]
|
||||
- Dracula theme -> [MIT][dracula]
|
||||
|
@ -119,16 +124,17 @@ You can reach out to me personally on:
|
|||
|
||||
---
|
||||
|
||||
*Hyperpipe does not host any content. All content on Hyperpipe is from YouTube Music. YouTube and YouTube Music are trademarks of Google LLC. Hyperpipe is not affiliated with YouTube or YouTube Music.*
|
||||
*All content on Hyperpipe is from YouTube Music. YouTube and YouTube Music are trademarks of Google LLC. Hyperpipe is not affiliated with YouTube or YouTube Music.*
|
||||
|
||||
[hypipe]: https://hyperpipe.surge.sh
|
||||
[piped]: https://piped.kavin.rocks
|
||||
[piped]: https://github.com/TeamPiped/Piped
|
||||
[license]: https://codeberg.org/Hyperpipe/Hyperpipe/src/branch/main/LICENSE.md
|
||||
[vue]: https://github.com/vuejs/core/blob/main/LICENSE
|
||||
[vite]: https://github.com/vitejs/vite/blob/main/LICENSE
|
||||
[bi]: https://github.com/twbs/icons/blob/main/LICENSE.md
|
||||
[peer]: https://github.com/peers/peerjs/blob/master/LICENSE
|
||||
[shaka]: https://github.com/shaka-project/shaka-player/blob/main/LICENSE
|
||||
[purify]: https://github.com/cure53/DOMPurify/blob/main/LICENSE
|
||||
[nord]: https://github.com/arcticicestudio/nord/blob/develop/LICENSE.md
|
||||
[vuetheme]: https://github.com/vuejs/theme/blob/main/LICENSE
|
||||
[dracula]: https://github.com/dracula/dracula-theme/blob/master/LICENSE
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
<link rel="dns-prefetch" href="https://hyperpipe-proxy.onrender.com" />
|
||||
<link rel="dns-prefetch" href="https://piped-instances.kavin.rocks" />
|
||||
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
|
||||
|
|
8152
package-lock.json
generated
8152
package-lock.json
generated
File diff suppressed because it is too large
Load diff
20
package.json
20
package.json
|
@ -10,20 +10,18 @@
|
|||
"check": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap-icons": "^1.10.2",
|
||||
"buffer": "^6.0.3",
|
||||
"dompurify": "^2.4.1",
|
||||
"mux.js": "^6.2.0",
|
||||
"bootstrap-icons": "^1.10.3",
|
||||
"dompurify": "^2.4.4",
|
||||
"mux.js": "^6.3.0",
|
||||
"peerjs": "^1.4.7",
|
||||
"pinia": "^2.0.28",
|
||||
"shaka-player": "^4.3.1",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"vue": "^3.2.38",
|
||||
"xml-js": "^1.6.11"
|
||||
"pinia": "^2.0.32",
|
||||
"shaka-player": "^4.3.4",
|
||||
"vue": "^3.2.38"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"prettier": "^2.8.1",
|
||||
"vite": "^4.0.0"
|
||||
"prettier": "^2.8.4",
|
||||
"vite": "^4.1.4",
|
||||
"vite-plugin-pwa": "^0.14.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"name": "Hyperpipe",
|
||||
"short_name": "Hyperpipe",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#000",
|
||||
"description": "Privacy respecting YouTube Music Frontend.",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-maskable.png",
|
||||
"sizes": "1024x1024",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -109,9 +109,8 @@ function playList(a) {
|
|||
/* Lifestyle hooks */
|
||||
onBeforeMount(() => {
|
||||
/* Set the default theme if set */
|
||||
if (store.theme) {
|
||||
document.body.setAttribute('data-theme', store.theme);
|
||||
}
|
||||
if (store.theme) document.body.setAttribute('data-theme', store.theme);
|
||||
if (store.compact == 'true') document.body.setAttribute('data-compact', '');
|
||||
|
||||
/* Set the default locale if set */
|
||||
if (store.locale) setupLocale(store.locale);
|
||||
|
|
|
@ -94,10 +94,12 @@ body[data-theme*='blur'] .pl-modal,
|
|||
body[data-theme*='blur'] .modal-box,
|
||||
body[data-theme*='blur'] .popup input[type='text'] {
|
||||
background: var(--color-blur);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
body[data-theme*='blur'] .modal {
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
|
@ -327,15 +329,26 @@ img {
|
|||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@media (max-width: 530px) {
|
||||
[data-compact] .grid-3 {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
@media (min-width: 530px) {
|
||||
.grid-3 {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
[data-compact] .grid-3 {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
}
|
||||
@media (min-width: 780px) {
|
||||
.grid-3 {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
[data-compact] .grid-3 {
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
}
|
||||
}
|
||||
@media (min-width: 930px) {
|
||||
.grid {
|
||||
|
@ -347,6 +360,9 @@ img {
|
|||
.grid-3 {
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
}
|
||||
[data-compact] .grid-3 {
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"date": "2022-12-31"
|
||||
"date": "2023-03-05"
|
||||
}
|
||||
|
|
|
@ -45,5 +45,18 @@ defineEmits(['open-album']);
|
|||
}
|
||||
.card-text {
|
||||
margin-top: 0.5rem;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
[data-compact] .card {
|
||||
min-height: 12rem;
|
||||
width: 10rem;
|
||||
}
|
||||
[data-compact] .card-bg {
|
||||
width: 7rem;
|
||||
height: 7rem;
|
||||
}
|
||||
[data-compact] .card-text {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -14,19 +14,17 @@ const subs = JSON.parse(store.subs ? store.subs : '[]'),
|
|||
isSub = ref(subs.includes(hash));
|
||||
|
||||
function Sub() {
|
||||
if (artist.state.title) {
|
||||
if (isSub.value) {
|
||||
subs.splice(subs.indexOf(hash), 1);
|
||||
store.setItem('subs', JSON.stringify(subs));
|
||||
isSub.value = false;
|
||||
} else {
|
||||
subs.push(hash);
|
||||
store.setItem('subs', JSON.stringify(subs));
|
||||
isSub.value = true;
|
||||
}
|
||||
if (!artist.state.title) return;
|
||||
|
||||
alert(JSON.stringify(subs));
|
||||
if (isSub.value) {
|
||||
subs.splice(subs.indexOf(hash), 1);
|
||||
isSub.value = false;
|
||||
} else {
|
||||
subs.push(hash);
|
||||
isSub.value = true;
|
||||
}
|
||||
|
||||
store.setItem('subs', JSON.stringify(subs));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -30,9 +30,7 @@ async function getCharts() {
|
|||
console.log(json);
|
||||
|
||||
if (!id.value)
|
||||
id.value = json.options.all.filter(
|
||||
i => i.title == json.options.default,
|
||||
)[0].id;
|
||||
id.value = json.options.all.find(i => i.title == json.options.default).id;
|
||||
|
||||
data.options = json.options.all;
|
||||
data.songs = json.trending;
|
||||
|
|
|
@ -116,7 +116,9 @@ onMounted(get);
|
|||
}
|
||||
.btn-grid {
|
||||
display: grid;
|
||||
grid-template-columns: calc(100% / 3) calc(100% / 3) calc(100% / 3);
|
||||
align-self: center;
|
||||
max-width: 100%;
|
||||
grid-template-columns: repeat(auto-fill, 8rem);
|
||||
grid-auto-rows: 1fr;
|
||||
gap: 0.125rem;
|
||||
padding: 1rem 0;
|
||||
|
@ -143,20 +145,4 @@ onMounted(get);
|
|||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
@media (min-width: 760px) {
|
||||
.btn-grid {
|
||||
grid-template-columns: calc(100% / 4) calc(100% / 4) calc(100% / 4) calc(
|
||||
100% / 4
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.btn-grid {
|
||||
grid-template-columns:
|
||||
calc(100% / 5) calc(100% / 5) calc(100% / 5) calc(100% / 5)
|
||||
calc(100% / 5);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -36,8 +36,11 @@ const list = ref([]),
|
|||
show = reactive({
|
||||
new: false,
|
||||
sync: false,
|
||||
import: false,
|
||||
}),
|
||||
text = ref(''),
|
||||
isImport = ref(false),
|
||||
importFile = ref(''),
|
||||
sync = reactive({
|
||||
type: 'send',
|
||||
id: 'Please Wait...',
|
||||
|
@ -53,16 +56,27 @@ const list = ref([]),
|
|||
|
||||
const pathname = url => new URL(url).pathname;
|
||||
|
||||
const Open = key => {
|
||||
const Open = async key => {
|
||||
console.log(key);
|
||||
|
||||
const { imageProxyUrl } = await getJsonPiped('/config');
|
||||
|
||||
useGetPlaylist(key, res => {
|
||||
console.log(res);
|
||||
if (res.urls.length > 0) {
|
||||
results.resetItems();
|
||||
results.setItem('songs', {
|
||||
title: 'Local • ' + key,
|
||||
items: res.urls.map(i => ({ ...i, ...{ playlistId: key } })),
|
||||
items: res.urls.map(i => ({
|
||||
...i,
|
||||
...{
|
||||
playlistId: key,
|
||||
thumbnail: `${imageProxyUrl}/vi_webp/${i.url.replace(
|
||||
'/watch?v=',
|
||||
'',
|
||||
)}/maxresdefault.webp?host=i.ytimg.com`,
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
nav.state.page = 'home';
|
||||
|
@ -82,6 +96,80 @@ const Open = key => {
|
|||
});
|
||||
}
|
||||
},
|
||||
Import = async (data = importFile.value) => {
|
||||
if (data?.type == 'application/json')
|
||||
data = await data.text().then(json => {
|
||||
json = JSON.parse(json);
|
||||
|
||||
if (json?.subscriptions?.length > 0) {
|
||||
const subs = JSON.parse(store.subs || '[]');
|
||||
|
||||
for (const sub of json.subscriptions) {
|
||||
const id = sub.url.slice(-24);
|
||||
|
||||
if (subs.indexOf(id) < 0) subs.push(id);
|
||||
}
|
||||
|
||||
store.subs = JSON.stringify(subs);
|
||||
}
|
||||
|
||||
return json.local;
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
alert('No data to import');
|
||||
return;
|
||||
}
|
||||
|
||||
List();
|
||||
|
||||
for (let i of data) {
|
||||
const pl = list.value.find(p => p.name == i.name);
|
||||
|
||||
if (pl) {
|
||||
for (let u of i.urls) {
|
||||
if (pl.urls.findIndex(r => r.url === u.url) < 0) {
|
||||
useUpdatePlaylist(i.name, u, () => {
|
||||
console.log('Added: ' + u.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
useCreatePlaylist(i.name, i.urls);
|
||||
}
|
||||
|
||||
List();
|
||||
}
|
||||
|
||||
show.import = false;
|
||||
},
|
||||
Export = () => {
|
||||
List();
|
||||
|
||||
const base = JSON.stringify(
|
||||
{
|
||||
format: 'Hyperpipe',
|
||||
version: 0,
|
||||
app_version: 0,
|
||||
subscriptions: JSON.parse(store.subs).map(id => ({
|
||||
url: 'https://www.youtube.com/channel/' + id,
|
||||
service_id: 0,
|
||||
})),
|
||||
local: list.value,
|
||||
playlists: [], // TODO?
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
file = new Blob([base], { type: 'application/json' }),
|
||||
ele = document.createElement('a');
|
||||
|
||||
ele.href = URL.createObjectURL(file);
|
||||
ele.download = 'hyperpipe.json';
|
||||
|
||||
ele.click();
|
||||
ele.remove();
|
||||
},
|
||||
Send = () => {
|
||||
const conn = sync.peer.connect(sync.to);
|
||||
|
||||
|
@ -146,12 +234,13 @@ const Login = async () => {
|
|||
};
|
||||
|
||||
const getFeeds = async () => {
|
||||
const subs = JSON.parse(store.subs ? store.subs : '[]');
|
||||
const subs = store.subs;
|
||||
|
||||
if (subs.length > 0) {
|
||||
const json = await getJsonPiped(
|
||||
'/feed/unauthenticated?channels=' + subs.join(','),
|
||||
);
|
||||
if (subs) {
|
||||
const json = await getJsonPiped('/feed/unauthenticated', {
|
||||
method: 'POST',
|
||||
body: subs,
|
||||
});
|
||||
|
||||
results.resetItems();
|
||||
results.setItem('songs', {
|
||||
|
@ -183,29 +272,9 @@ watch(
|
|||
if (sync.type == 'rec') {
|
||||
console.log(data);
|
||||
|
||||
List();
|
||||
Import(data);
|
||||
|
||||
for (let i of data) {
|
||||
const pl = list.value.filter(p => p.name == i.name)[0];
|
||||
|
||||
if (pl) {
|
||||
for (let u of i.urls) {
|
||||
if (!pl.urls.filter(r => r.url === u.url)[0]) {
|
||||
useUpdatePlaylist(i.name, u, () => {
|
||||
console.log('Added: ' + u.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
useCreatePlaylist(i.name, i.urls);
|
||||
}
|
||||
|
||||
List();
|
||||
|
||||
if (data.indexOf(i) == data.length - 1) {
|
||||
show.sync = false;
|
||||
}
|
||||
}
|
||||
show.sync = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -300,6 +369,43 @@ onMounted(async () => {
|
|||
</template>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
n="2"
|
||||
:display="show.import"
|
||||
:title="t('action.import')"
|
||||
@show="
|
||||
e => {
|
||||
show.import = e;
|
||||
}
|
||||
">
|
||||
<template #content>
|
||||
<div class="tabs">
|
||||
<button :data-active="isImport" @click="isImport = true">
|
||||
{{ t('action.import') }}
|
||||
</button>
|
||||
<button :data-active="!isImport" @click="isImport = false">
|
||||
{{ t('action.export') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="isImport">
|
||||
<input
|
||||
type="file"
|
||||
class="textbox"
|
||||
name="import"
|
||||
accept="application/json"
|
||||
@change="importFile = $event.target.files[0]" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #buttons>
|
||||
<button @click="show.import = false">{{ t('action.cancel') }}</button>
|
||||
<button @click="isImport ? Import() : Export()">
|
||||
{{ isImport ? t('action.import') : t('action.export') }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
||||
<div class="grid">
|
||||
<div class="npl-box bi bi-plus-lg pop" @click="show.new = true"></div>
|
||||
|
||||
|
@ -308,6 +414,10 @@ onMounted(async () => {
|
|||
@click="show.sync = true"></div>
|
||||
|
||||
<div class="npl-box bi bi-tag pop" @click="getFeeds"></div>
|
||||
|
||||
<div
|
||||
class="npl-box bi bi-box-arrow-in-down pop"
|
||||
@click="show.import = true"></div>
|
||||
</div>
|
||||
|
||||
<h2 v-if="list.length > 0">{{ t('playlist.local') }}</h2>
|
||||
|
@ -329,6 +439,7 @@ onMounted(async () => {
|
|||
v-for="i in user.playlists"
|
||||
:key="i.id"
|
||||
:name="i.name.replace('Playlist - ', '')"
|
||||
:author="t('title.songs') + ' • ' + i.videos"
|
||||
:art="pathname(i.thumbnail) != '/' ? i.thumbnail : undefined"
|
||||
@open-album="$emit('open-playlist', '/playlists?list=' + i.id)" />
|
||||
</div>
|
||||
|
@ -419,6 +530,20 @@ button.logout {
|
|||
display: block;
|
||||
background: linear-gradient(135deg, indianred, #bf616a);
|
||||
}
|
||||
input[type='file'] {
|
||||
display: block;
|
||||
}
|
||||
input[type='file']::file-selector-button {
|
||||
font-weight: bold;
|
||||
appearance: inherit;
|
||||
outline: inherit;
|
||||
border: inherit;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
margin: 0.1rem 0.5rem 0.1rem 0.1rem;
|
||||
color: var(--color-background);
|
||||
background: linear-gradient(135deg, cornflowerblue, #88c0d0);
|
||||
}
|
||||
.tabs button:first-child {
|
||||
border-radius: 0.25rem 0 0 0.25rem;
|
||||
}
|
||||
|
|
|
@ -118,6 +118,11 @@ async function Stream() {
|
|||
}
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
window.audioPlayer.destroy();
|
||||
window.audioPlayer = undefined;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => player.state.play,
|
||||
() => {
|
||||
|
@ -150,8 +155,9 @@ onMounted(() => {
|
|||
if ('mediaSession' in navigator) {
|
||||
navigator.mediaSession.setActionHandler('play', () => {
|
||||
player.state.status = 'pause';
|
||||
|
||||
audio.value.play().catch(err => {
|
||||
alert(err);
|
||||
console.log(err);
|
||||
player.state.status = 'play';
|
||||
});
|
||||
});
|
||||
|
@ -163,7 +169,7 @@ onMounted(() => {
|
|||
|
||||
navigator.mediaSession.setActionHandler('previoustrack', () => {
|
||||
if (data.state.urls.length > 2) {
|
||||
const i = data.state.urls.map(s => s.url).indexOf(data.state.url);
|
||||
const i = data.state.urls.findIndex(s => s.url == data.state.url);
|
||||
|
||||
data.getSong(data.state.urls[i - 1].url);
|
||||
}
|
||||
|
@ -171,7 +177,7 @@ onMounted(() => {
|
|||
|
||||
navigator.mediaSession.setActionHandler('nexttrack', () => {
|
||||
if (data.state.urls.length > 2) {
|
||||
const i = data.state.urls.map(s => s.url).indexOf(data.state.url);
|
||||
const i = data.state.urls.findIndex(s => s.url == data.state.url);
|
||||
|
||||
data.getSong(data.state.urls[i + 1].url);
|
||||
}
|
||||
|
@ -188,16 +194,10 @@ onMounted(() => {
|
|||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (window.audioPlayer) {
|
||||
window.audioPlayer.destroy();
|
||||
window.audioPlayer = undefined;
|
||||
}
|
||||
if (window.audioPlayer) destroy();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (window.audioPlayer) {
|
||||
window.audioPlayer.destroy();
|
||||
window.audioPlayer = undefined;
|
||||
}
|
||||
if (window.audioPlayer) destroy();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ import('@/assets/version.json').then(v => {
|
|||
const { t, setupLocale } = useI18n(),
|
||||
instances = ref([]),
|
||||
hypInstances = ref([]),
|
||||
next = ref(false);
|
||||
next = ref(false),
|
||||
compact = ref(false);
|
||||
|
||||
getJson('https://piped-instances.kavin.rocks').then(i => {
|
||||
instances.value = i;
|
||||
|
@ -60,8 +61,14 @@ function setLang(locale) {
|
|||
setStore('locale', locale);
|
||||
}
|
||||
|
||||
function setCodec(codec) {
|
||||
setStore('codec', codec);
|
||||
if (window.audioPlayer)
|
||||
window.audioPlayer.configure('preferredAudioCodecs', codec.split(':'));
|
||||
}
|
||||
|
||||
function getStoreBool(key, ele) {
|
||||
ele.value = getStore(key) || true;
|
||||
ele.value = getStore(key) || ele.value;
|
||||
}
|
||||
|
||||
const verifyApi = computed(() =>
|
||||
|
@ -82,6 +89,7 @@ const verifyApi = computed(() =>
|
|||
|
||||
onMounted(() => {
|
||||
getStoreBool('next', next);
|
||||
getStoreBool('compact', compact);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -100,6 +108,17 @@ onMounted(() => {
|
|||
<option value="nord">{{ t('pref.nord') }}</option>
|
||||
</select>
|
||||
|
||||
<div class="left">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="pref-chk-compact"
|
||||
id="pref-chk-compact"
|
||||
class="input"
|
||||
@change="setStore('compact', $event.target.checked)"
|
||||
v-model="compact" />
|
||||
<label for="pref-chk-compact">{{ t('pref.compact') }}</label>
|
||||
</div>
|
||||
|
||||
<h2>Language</h2>
|
||||
|
||||
<select
|
||||
|
@ -119,10 +138,10 @@ onMounted(() => {
|
|||
class="input"
|
||||
:value="getStore('page') || 'home'"
|
||||
@change="setStore('page', $event.target.value)">
|
||||
<option value="home">Home</option>
|
||||
<option value="explore">Explore</option>
|
||||
<option value="charts">Charts</option>
|
||||
<option value="library">Library</option>
|
||||
<option value="home">{{ t('pref.home') }}</option>
|
||||
<option value="explore">{{ t('pref.explore') }}</option>
|
||||
<option value="charts">{{ t('pref.charts') }}</option>
|
||||
<option value="library">{{ t('pref.library') }}</option>
|
||||
</select>
|
||||
|
||||
<h2>{{ t('pref.player') }}</h2>
|
||||
|
@ -145,7 +164,7 @@ onMounted(() => {
|
|||
name="pref-codec"
|
||||
class="input"
|
||||
:value="getStore('codec') || 'opus:mp4a'"
|
||||
@change="setStore('codec', $event.target.value)">
|
||||
@change="setCodec($event.target.value)">
|
||||
<option value="opus:mp4a">opus, mp4a</option>
|
||||
<option value="mp4a:opus">mp4a, opus</option>
|
||||
<option value="opus">opus</option>
|
||||
|
|
|
@ -6,7 +6,7 @@ import SongItem from './SongItem.vue';
|
|||
import AlbumItem from './AlbumItem.vue';
|
||||
|
||||
import { getJsonPiped, getPipedQuery } from '@/scripts/fetch.js';
|
||||
import { useRoute, useWrap } from '@/scripts/util.js';
|
||||
import { useRoute, useWrap, useShare } from '@/scripts/util.js';
|
||||
import { useCreatePlaylist, useRemovePlaylist } from '@/scripts/db.js';
|
||||
|
||||
import { useResults, useArtist } from '@/stores/results.js';
|
||||
|
@ -77,9 +77,18 @@ const shuffleAdd = () => {
|
|||
removeSong = i => {
|
||||
console.log(i);
|
||||
|
||||
try {
|
||||
results.items.songs.items.splice(i, 1);
|
||||
} catch {}
|
||||
results.items.songs.items.splice(i, 1);
|
||||
},
|
||||
shareAlbum = () => {
|
||||
const data = {
|
||||
title: `View ${results.items?.songs?.title} on Hyperpipe`,
|
||||
url:
|
||||
location.origin +
|
||||
(results.album.startsWith('/') ? '' : '/') +
|
||||
results.album,
|
||||
};
|
||||
|
||||
useShare(data);
|
||||
},
|
||||
saveAlbum = () => {
|
||||
const urls = results.items?.songs?.items?.map(item => ({
|
||||
|
@ -149,6 +158,7 @@ const shuffleAdd = () => {
|
|||
|
||||
items = json.items;
|
||||
} else {
|
||||
console.log(results.next);
|
||||
const json = await getJsonPiped(`/nextpage/playlists/${results.next}`);
|
||||
key = 'songs';
|
||||
|
||||
|
@ -233,6 +243,8 @@ onDeactivated(() => {
|
|||
class="bi bi-bookmark-plus clickable"
|
||||
@click="saveAlbum"></button>
|
||||
|
||||
<button class="bi bi-share clickable" @click="shareAlbum"></button>
|
||||
|
||||
<button
|
||||
class="bi bi-plus-lg clickable"
|
||||
@click="
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ref, onMounted } from 'vue';
|
|||
|
||||
import { getJsonAuth } from '@/scripts/fetch.js';
|
||||
import { useRand } from '@/scripts/colors.js';
|
||||
import { useStore } from '@/scripts/util.js';
|
||||
import { useStore, useShare } from '@/scripts/util.js';
|
||||
import { useUpdatePlaylist } from '@/scripts/db.js';
|
||||
|
||||
import { useResults, useArtist } from '@/stores/results.js';
|
||||
|
@ -40,9 +40,7 @@ const openSong = el => {
|
|||
thumbnails: [{ url: props.art }],
|
||||
});
|
||||
|
||||
const index = data.state.urls.map(s => s.url).indexOf(data.state.url);
|
||||
|
||||
console.log(data.state.urls);
|
||||
const index = data.state.urls.findIndex(s => s.url == data.state.url);
|
||||
|
||||
if (
|
||||
(index == data.state.urls.length - 1 && player.state.time > 98) ||
|
||||
|
@ -50,6 +48,15 @@ const openSong = el => {
|
|||
)
|
||||
emit('open-song', props.play);
|
||||
},
|
||||
appendSong = () => {
|
||||
const index = data.state.urls.findIndex(s => s.url == data.state.url);
|
||||
|
||||
data.state.urls.splice(index + 1, 0, {
|
||||
url: props.play,
|
||||
title: props.title,
|
||||
thumbnails: [{ url: props.art }],
|
||||
});
|
||||
},
|
||||
Remove = () => {
|
||||
const auth = useStore().getItem('auth'),
|
||||
isRemote = results.items?.songs?.title?.startsWith('Playlist - ');
|
||||
|
@ -78,29 +85,13 @@ const openSong = el => {
|
|||
emit('remove', props.index),
|
||||
);
|
||||
},
|
||||
Share = async () => {
|
||||
if ('share' in navigator) {
|
||||
const data = {
|
||||
title: `Listen to ${props.title} by ${props.author} on Hyperpipe`,
|
||||
url: location.origin + props.play,
|
||||
};
|
||||
Share = () => {
|
||||
const data = {
|
||||
title: `Listen to ${props.title} by ${props.author} on Hyperpipe`,
|
||||
url: location.origin + props.play,
|
||||
};
|
||||
|
||||
try {
|
||||
await navigator.share(data);
|
||||
console.log('Done Sharing!');
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
} else {
|
||||
navigator.clipboard.writeText(location.host + props.play).then(
|
||||
() => {
|
||||
alert('Copied to Clipboard');
|
||||
},
|
||||
err => {
|
||||
console.log(err);
|
||||
},
|
||||
);
|
||||
}
|
||||
useShare(data);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -131,7 +122,12 @@ onMounted(() => {
|
|||
v-if="playlistId"
|
||||
class="bi bi-dash-lg clickable ign"
|
||||
@click="Remove"></span>
|
||||
<span class="bi bi-broadcast ign" @click="$emit('nxt-song')"></span>
|
||||
<span
|
||||
class="bi bi-chevron-bar-right clickable ign"
|
||||
@click="appendSong"></span>
|
||||
<span
|
||||
class="bi bi-broadcast clickable ign"
|
||||
@click="$emit('nxt-song')"></span>
|
||||
<span class="bi bi-plus-lg clickable ign" @click="addSong"></span>
|
||||
<span class="bi bi-share clickable ign" @click="Share"></span>
|
||||
</div>
|
||||
|
@ -173,4 +169,12 @@ span.bi-three-dots-vertical {
|
|||
.bi-dash-lg {
|
||||
color: indianred;
|
||||
}
|
||||
|
||||
[data-compact] .card {
|
||||
margin: 0;
|
||||
}
|
||||
[data-compact] .song-bg {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -85,7 +85,7 @@ async function Like() {
|
|||
|
||||
remote.value = await getAuthPlaylists();
|
||||
|
||||
let fav = remote.value.filter(i => i.name == 'Playlist - Favorites')[0];
|
||||
let fav = remote.value.find(i => i.name == 'Playlist - Favorites');
|
||||
|
||||
if (!fav) {
|
||||
const { playlistId } = await useAuthCreatePlaylist('Favorites');
|
||||
|
@ -134,7 +134,8 @@ async function Like() {
|
|||
plRemote = true;
|
||||
"
|
||||
:data-active="pl == i.id && plRemote == true">
|
||||
<span>{{ i.name }}</span>
|
||||
<span>{{ i.name }}</span
|
||||
><span class="ml-auto">{{ i.videos }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #buttons>
|
||||
|
@ -424,11 +425,6 @@ input[type='range']::-webkit-slider-thumb {
|
|||
background-color: var(--color-foreground);
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease;
|
||||
}
|
||||
input[type='range']:hover::-webkit-slider-thumb,
|
||||
#vol input[type='range']::-webkit-slider-thumb {
|
||||
opacity: 1;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
|
@ -447,13 +443,8 @@ input[type='range']::-webkit-slider-runnable-track {
|
|||
input[type='range']::-moz-range-thumb {
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
opacity: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: opacity 0.4s ease;
|
||||
}
|
||||
input[type='range']:hover::-moz-range-thumb,
|
||||
#vol input[type='range']::-moz-range-thumb {
|
||||
background-color: var(--color-foreground);
|
||||
opacity: 1;
|
||||
height: 1rem;
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
"create": "Yarat",
|
||||
"receive": "Qəbul et",
|
||||
"cancel": "Ləğv et",
|
||||
"send": "Göndər"
|
||||
"send": "Göndər",
|
||||
"import": "İdxal Et",
|
||||
"export": "İxrac Et"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Yerli Pleylistlər",
|
||||
|
@ -50,7 +52,12 @@
|
|||
"light": "İşıqlı",
|
||||
"blur_light": "Bulanıq (İşıqlı)",
|
||||
"dracula": "Drakula",
|
||||
"nord": "Nord"
|
||||
"nord": "Nord",
|
||||
"compact": "Kompakt Görünüş",
|
||||
"charts": "Qrafiklər",
|
||||
"library": "Pleylistlər",
|
||||
"home": "Ana Menu",
|
||||
"explore": "Kəşf Et"
|
||||
},
|
||||
"info": {
|
||||
"see_all": "Hamısını gör",
|
||||
|
|
18
src/locales/bn.json
Normal file
18
src/locales/bn.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"title": {
|
||||
"artists": "শিল্পীরা",
|
||||
"albums": "অ্যালবামগুলো",
|
||||
"singles": "সিঙ্গেলস",
|
||||
"similar_artists": "একইধরণের শিল্পীরা",
|
||||
"playlists": "প্লেলিস্টস",
|
||||
"moods": "মেজাজ",
|
||||
"genres": "জনরাগুলো",
|
||||
"songs": "গানগুলো",
|
||||
"local": "স্থানীয়",
|
||||
"search": "খুঁজুন",
|
||||
"login": "লগিন"
|
||||
},
|
||||
"pref": {
|
||||
"blur": "ঘোলা"
|
||||
}
|
||||
}
|
|
@ -5,7 +5,9 @@
|
|||
"send": "Pošalji",
|
||||
"receive": "Primi",
|
||||
"add": "Dodaj",
|
||||
"create": "Napravi"
|
||||
"create": "Napravi",
|
||||
"import": "Uvezite",
|
||||
"export": "Izvezite"
|
||||
},
|
||||
"title": {
|
||||
"genres": "Žanrovi",
|
||||
|
@ -60,7 +62,12 @@
|
|||
"nord": "Nord",
|
||||
"blur": "Zamagljeno",
|
||||
"blur_light": "Zamućenje (Svjetlo)",
|
||||
"light": "Svjetlo"
|
||||
"light": "Svjetlo",
|
||||
"home": "Početna",
|
||||
"explore": "Istražite",
|
||||
"charts": "Grafikoni",
|
||||
"library": "Biblioteka",
|
||||
"compact": "Kompaktan prikaz"
|
||||
},
|
||||
"info": {
|
||||
"search": "Započni pretraživanje",
|
||||
|
|
|
@ -33,7 +33,12 @@
|
|||
"blur": "Rozmazání",
|
||||
"blur_light": "Rozmazaný (světlý)",
|
||||
"dracula": "Drákula",
|
||||
"nord": "Nord"
|
||||
"nord": "Nord",
|
||||
"explore": "Procházet",
|
||||
"charts": "Žebříčky",
|
||||
"home": "Domů",
|
||||
"library": "Knihovna",
|
||||
"compact": "Kompaktní zobrazení"
|
||||
},
|
||||
"instances": {
|
||||
"name": "Název",
|
||||
|
@ -55,7 +60,9 @@
|
|||
"add": "Přidat",
|
||||
"cancel": "Zrušit",
|
||||
"send": "Poslat",
|
||||
"receive": "Obdržet"
|
||||
"receive": "Obdržet",
|
||||
"import": "Importovat",
|
||||
"export": "Exportovat"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Místní playlisty",
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
"moods": "Stimmungen",
|
||||
"songs": "Lieder",
|
||||
"albums": "Alben",
|
||||
"genres": "Genres",
|
||||
"singles": "Singles",
|
||||
"genres": "Kategorien",
|
||||
"singles": "Einzeltitel",
|
||||
"artists": "Künstler/innen",
|
||||
"similar_artists": "Ähnliche Künstler/innen",
|
||||
"featured": "Ausgewählt",
|
||||
"community": "Gemeinschaft",
|
||||
"login": "Anmeldung",
|
||||
"login": "Anmelden",
|
||||
"local": "Lokal",
|
||||
"search": "Suche",
|
||||
"spotlight": "Scheinwerfer",
|
||||
|
@ -23,7 +23,9 @@
|
|||
"create": "Erstellen",
|
||||
"add": "Hinzufügen",
|
||||
"cancel": "Abbrechen",
|
||||
"receive": "Empfangen"
|
||||
"receive": "Empfangen",
|
||||
"import": "Importieren",
|
||||
"export": "Exportieren"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Lokale Wiedergabelisten",
|
||||
|
@ -49,8 +51,13 @@
|
|||
"light": "Hell",
|
||||
"blur": "Unschärfe",
|
||||
"blur_light": "Unschärfe (hell)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord"
|
||||
"dracula": "Drakula",
|
||||
"nord": "Nord",
|
||||
"library": "Bibliothek",
|
||||
"charts": "Diagramme",
|
||||
"explore": "Erkunden",
|
||||
"home": "Startseite",
|
||||
"compact": "Kompakte Ansicht"
|
||||
},
|
||||
"info": {
|
||||
"no_info": "Keine Informationen verfügbar",
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
"create": "Create",
|
||||
"cancel": "Cancel",
|
||||
"send": "Send",
|
||||
"receive": "Receive"
|
||||
"receive": "Receive",
|
||||
"import": "Import",
|
||||
"export": "Export"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Local Playlists",
|
||||
|
@ -42,6 +44,7 @@
|
|||
"blur_light": "Blur (Light)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord",
|
||||
"compact": "Compact View",
|
||||
"tab": "Default Tab",
|
||||
"player": "Audio Player",
|
||||
"auto_queue": "Automatically Queue Songs",
|
||||
|
@ -50,7 +53,11 @@
|
|||
"auto": "auto",
|
||||
"best": "best",
|
||||
"worst": "worst",
|
||||
"volume": "Default Volume"
|
||||
"volume": "Default Volume",
|
||||
"home": "Home",
|
||||
"explore": "Explore",
|
||||
"charts": "Charts",
|
||||
"library": "Library"
|
||||
},
|
||||
"info": {
|
||||
"see_all": "See All",
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
"create": "Crear",
|
||||
"send": "Enviar",
|
||||
"receive": "Recibir",
|
||||
"cancel": "Cancelar"
|
||||
"cancel": "Cancelar",
|
||||
"import": "Importar",
|
||||
"export": "Exportar"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Listas de reproducción locales",
|
||||
|
@ -50,7 +52,12 @@
|
|||
"light": "Claro",
|
||||
"blur_light": "Desenfoque (Luz)",
|
||||
"dracula": "Drácula",
|
||||
"nord": "Nord"
|
||||
"nord": "Nord",
|
||||
"home": "Inicio",
|
||||
"library": "Biblioteca",
|
||||
"charts": "Gráfica",
|
||||
"explore": "Explorar",
|
||||
"compact": "Vista compacta"
|
||||
},
|
||||
"info": {
|
||||
"see_all": "Ver todo",
|
||||
|
|
|
@ -42,7 +42,12 @@
|
|||
"blur": "Flou",
|
||||
"blur_light": "Flou (clair)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord"
|
||||
"nord": "Nord",
|
||||
"charts": "Graphiques",
|
||||
"explore": "Explorer",
|
||||
"home": "Accueil",
|
||||
"library": "Bibliothèque",
|
||||
"compact": "Vue compacte"
|
||||
},
|
||||
"info": {
|
||||
"see_all": "Voir tout",
|
||||
|
@ -55,7 +60,9 @@
|
|||
"add": "Ajouter",
|
||||
"create": "Créer",
|
||||
"cancel": "Annuler",
|
||||
"receive": "Recevoir"
|
||||
"receive": "Recevoir",
|
||||
"import": "Importer",
|
||||
"export": "Exporter"
|
||||
},
|
||||
"instances": {
|
||||
"up_to_date": "À jour",
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
"receive": "Primi",
|
||||
"create": "Stvori",
|
||||
"back": "Natrag",
|
||||
"add": "Dodaj"
|
||||
"add": "Dodaj",
|
||||
"import": "Uvezite",
|
||||
"export": "Izvezite"
|
||||
},
|
||||
"instances": {
|
||||
"loc": "Lokacije",
|
||||
|
@ -56,7 +58,12 @@
|
|||
"blur_light": "Zamagljeno (Svjetlo)",
|
||||
"dracula": "Drakula",
|
||||
"nord": "Nord",
|
||||
"dark": "Mračno (Zadano)"
|
||||
"dark": "Mračno (Zadano)",
|
||||
"library": "Knjižnica",
|
||||
"home": "Početna",
|
||||
"charts": "Grafikoni",
|
||||
"explore": "Istražite",
|
||||
"compact": "Sažeti prikaz"
|
||||
},
|
||||
"lyrics": {
|
||||
"load": "Dohvaćanje stihova",
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
"title": {
|
||||
"artists": "Artis",
|
||||
"similar_artists": "Artis Serupa",
|
||||
"login": "Login",
|
||||
"login": "Masuk",
|
||||
"albums": "Album",
|
||||
"remote": "Remote",
|
||||
"remote": "Jarak Jauh",
|
||||
"songs": "Lagu",
|
||||
"singles": "Singel",
|
||||
"genres": "Genre",
|
||||
|
@ -14,14 +14,15 @@
|
|||
"spotlight": "Sorotan",
|
||||
"moods": "Suasana",
|
||||
"local": "Lokal",
|
||||
"playlists": "Daftar Putar"
|
||||
"playlists": "Daftar Putar",
|
||||
"feeds": "Umpan"
|
||||
},
|
||||
"playlist": {
|
||||
"select": "Pilih Daftar Putar yang Ingin Ditambahkan",
|
||||
"add": "Tambah Lagu ke Daftar Putar",
|
||||
"sync": "Sinkronisasi Daftar Putar",
|
||||
"local": "Daftar Putar Lokal",
|
||||
"remote": "Daftar Putar Remote",
|
||||
"remote": "Daftar Putar Jarak Jauh",
|
||||
"name": "Judul Daftar Putar",
|
||||
"create": "Buat Daftar Putar Baru"
|
||||
},
|
||||
|
@ -34,7 +35,19 @@
|
|||
"codec": "Codec",
|
||||
"worst": "paling rendah",
|
||||
"auto": "otomatis",
|
||||
"volume": "Volume Default"
|
||||
"volume": "Volume Default",
|
||||
"light": "Terang",
|
||||
"nord": "Nord",
|
||||
"blur_light": "Buram (Terang)",
|
||||
"dracula": "Drakula",
|
||||
"compact": "Tampilan Kompak",
|
||||
"dark": "Gelap (Bawaan)",
|
||||
"blur": "Buram",
|
||||
"tab": "Tab Bawaan",
|
||||
"home": "Beranda",
|
||||
"explore": "Jelajahi",
|
||||
"charts": "Bagan",
|
||||
"library": "Pustaka"
|
||||
},
|
||||
"info": {
|
||||
"no_info": "Tidak Ada Informasi Tersedia",
|
||||
|
@ -47,7 +60,9 @@
|
|||
"cancel": "Batal",
|
||||
"send": "Kirim",
|
||||
"receive": "Terima",
|
||||
"back": "Kembali"
|
||||
"back": "Kembali",
|
||||
"import": "Impor",
|
||||
"export": "Ekspor"
|
||||
},
|
||||
"instances": {
|
||||
"piped": "Instance Piped",
|
||||
|
|
|
@ -15,7 +15,12 @@
|
|||
"blur": "Sfocatura",
|
||||
"blur_light": "Sfocatura (chiaro)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord"
|
||||
"nord": "Nord",
|
||||
"library": "Raccolta",
|
||||
"home": "Pagina principale",
|
||||
"charts": "Grafici",
|
||||
"explore": "Esplora",
|
||||
"compact": "Vista compatta"
|
||||
},
|
||||
"title": {
|
||||
"albums": "Album",
|
||||
|
@ -40,7 +45,9 @@
|
|||
"back": "Indietro",
|
||||
"create": "Crea",
|
||||
"send": "Invia",
|
||||
"cancel": "Annulla"
|
||||
"cancel": "Annulla",
|
||||
"import": "Importa",
|
||||
"export": "Esporta"
|
||||
},
|
||||
"info": {
|
||||
"see_all": "Vedi tutto",
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
"cancel": "Annuleren",
|
||||
"receive": "Ontvangen",
|
||||
"back": "Terug",
|
||||
"create": "Creëren"
|
||||
"create": "Creëren",
|
||||
"import": "Importeren",
|
||||
"export": "Exporteren"
|
||||
},
|
||||
"title": {
|
||||
"community": "Gemeenschap",
|
||||
|
@ -41,7 +43,12 @@
|
|||
"blur_light": "Waas (Licht)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord",
|
||||
"dark": "Donker (standaard)"
|
||||
"dark": "Donker (standaard)",
|
||||
"explore": "Ontdekken",
|
||||
"charts": "Grafieken",
|
||||
"home": "Start",
|
||||
"library": "Bibliotheek",
|
||||
"compact": "Compacte weergave"
|
||||
},
|
||||
"lyrics": {
|
||||
"void": "Geen songtekst",
|
||||
|
|
79
src/locales/pt.json
Normal file
79
src/locales/pt.json
Normal file
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"title": {
|
||||
"playlists": "Listas de reprodução",
|
||||
"songs": "Músicas",
|
||||
"albums": "Álbuns",
|
||||
"artists": "Artistas",
|
||||
"similar_artists": "Artistas Similares",
|
||||
"moods": "Humores",
|
||||
"genres": "Gêneros",
|
||||
"featured": "Destaques",
|
||||
"spotlight": "Foco",
|
||||
"community": "Comunidade",
|
||||
"login": "Entrar",
|
||||
"local": "Local",
|
||||
"remote": "Remoto",
|
||||
"search": "Pesquisar",
|
||||
"feeds": "Feeds"
|
||||
},
|
||||
"action": {
|
||||
"import": "Importar",
|
||||
"back": "Voltar",
|
||||
"add": "Adicionar",
|
||||
"create": "Criar",
|
||||
"cancel": "Cancelar",
|
||||
"send": "Enviar",
|
||||
"receive": "Receber",
|
||||
"export": "Exportar"
|
||||
},
|
||||
"pref": {
|
||||
"dracula": "Drácula",
|
||||
"explore": "Explorar",
|
||||
"charts": "Gráficos",
|
||||
"library": "Biblioteca",
|
||||
"theme": "Tema",
|
||||
"dark": "Escuro (padrão)",
|
||||
"light": "Claro",
|
||||
"blur": "Desfocado",
|
||||
"blur_light": "Desfocado (Claro)",
|
||||
"nord": "Nord",
|
||||
"compact": "Visualização compacta",
|
||||
"tab": "Guia Padrão",
|
||||
"player": "Reprodutor de Áudio",
|
||||
"auto_queue": "Enfileirar músicas automaticamente",
|
||||
"codec": "Codec",
|
||||
"quality": "Qualidade",
|
||||
"auto": "automática",
|
||||
"best": "melhor",
|
||||
"worst": "pior",
|
||||
"volume": "Volume Padrão"
|
||||
},
|
||||
"instances": {
|
||||
"up_to_date": "Atualizado",
|
||||
"version": "Versão",
|
||||
"hyp": "Instância do Hyperpipe",
|
||||
"piped": "Instância do Piped",
|
||||
"auth": "Instância de Autenticação",
|
||||
"name": "Nome",
|
||||
"loc": "Localizações",
|
||||
"cdn": "CDN"
|
||||
},
|
||||
"lyrics": {
|
||||
"load": "Buscando Letras",
|
||||
"void": "Sem Letras"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Playlists Locais",
|
||||
"remote": "Playlists Remotas",
|
||||
"name": "Nome da Playlist",
|
||||
"add": "Adicionar Músicas à Playlist",
|
||||
"create": "Criar uma nova Playlist",
|
||||
"sync": "Sincronizar playlists",
|
||||
"select": "Selecione a Playlist para Adicionar"
|
||||
},
|
||||
"info": {
|
||||
"see_all": "Ver Tudo",
|
||||
"search": "Comece a pesquisar",
|
||||
"no_info": "Nenhuma Informação Disponível"
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@
|
|||
"blur_light": "Desfocado (Claro)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord",
|
||||
"compact": "Visualização compacta",
|
||||
"tab": "Aba Padrão",
|
||||
"player": "Reprodutor de Áudio",
|
||||
"auto_queue": "Enfileirar músicas automaticamente",
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
"create": "Creați",
|
||||
"send": "Trimite",
|
||||
"receive": "Primiți",
|
||||
"cancel": "Anulează"
|
||||
"cancel": "Anulează",
|
||||
"import": "Importă",
|
||||
"export": "Exportă"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Playlisturi locale",
|
||||
|
@ -50,7 +52,12 @@
|
|||
"light": "Luminat",
|
||||
"blur_light": "Blur (Lumină)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord"
|
||||
"nord": "Nord",
|
||||
"compact": "Vizualizare compactă",
|
||||
"home": "Acasă",
|
||||
"explore": "Explorează",
|
||||
"charts": "Topuri",
|
||||
"library": "Bibliotecă"
|
||||
},
|
||||
"instances": {
|
||||
"cdn": "CDN",
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
"cancel": "Отмена",
|
||||
"receive": "Получить",
|
||||
"send": "Отправить",
|
||||
"create": "Создать"
|
||||
"create": "Создать",
|
||||
"import": "Импорт",
|
||||
"export": "Экспорт"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Локальные плейлисты",
|
||||
|
@ -50,7 +52,12 @@
|
|||
"blur": "Размытый",
|
||||
"blur_light": "Размытый (светлый)",
|
||||
"dracula": "Дракула",
|
||||
"nord": "Норд"
|
||||
"nord": "Норд",
|
||||
"compact": "Компактный вид",
|
||||
"home": "Главная",
|
||||
"explore": "Обзор",
|
||||
"charts": "Чарты",
|
||||
"library": "Библиотека"
|
||||
},
|
||||
"instances": {
|
||||
"piped": "Инстанс Piped",
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
"create": "Креирај",
|
||||
"send": "Пошаљи",
|
||||
"cancel": "Откажи",
|
||||
"receive": "Прими"
|
||||
"receive": "Прими",
|
||||
"import": "Увезите",
|
||||
"export": "Извезите"
|
||||
},
|
||||
"title": {
|
||||
"featured": "Истакнуто",
|
||||
|
@ -50,7 +52,12 @@
|
|||
"dracula": "Дракула",
|
||||
"nord": "Норд",
|
||||
"dark": "Мрачно (Подразумевано)",
|
||||
"blur_light": "Замућење (Светло)"
|
||||
"blur_light": "Замућење (Светло)",
|
||||
"home": "Почетна",
|
||||
"charts": "Графикони",
|
||||
"library": "Библиотека",
|
||||
"explore": "Истражите",
|
||||
"compact": "Компактни преглед"
|
||||
},
|
||||
"instances": {
|
||||
"loc": "Локације",
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
"create": "Oluştur",
|
||||
"cancel": "İptal",
|
||||
"receive": "Al",
|
||||
"send": "Gönder"
|
||||
"send": "Gönder",
|
||||
"export": "Dışa aktar",
|
||||
"import": "İçe aktar"
|
||||
},
|
||||
"playlist": {
|
||||
"local": "Yerel Oynatma Listeleri",
|
||||
|
@ -50,7 +52,12 @@
|
|||
"blur": "Bulanıklaştır",
|
||||
"blur_light": "Bulanıklaştır (Açık)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord"
|
||||
"nord": "Nord",
|
||||
"charts": "Grafikler",
|
||||
"library": "Kütüphane",
|
||||
"explore": "Keşfet",
|
||||
"home": "Ana sayfa",
|
||||
"compact": "Sıkı Görünüm"
|
||||
},
|
||||
"info": {
|
||||
"see_all": "Hepsini Gör",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"albums": "Các album",
|
||||
"artists": "Các nhạc sĩ",
|
||||
"similar_artists": "Các nhạc sĩ liên quan",
|
||||
"genres": "Các thể loại",
|
||||
"genres": "Thể loại",
|
||||
"community": "Cộng đồng",
|
||||
"search": "Tìm kiếm",
|
||||
"singles": "Các đĩa đơn",
|
||||
|
@ -12,7 +12,8 @@
|
|||
"moods": "Chủ đề",
|
||||
"playlists": "Các danh sách phát",
|
||||
"featured": "Nổi bật",
|
||||
"feeds": "Nguồn cấp dữ liệu"
|
||||
"feeds": "Nguồn cấp dữ liệu",
|
||||
"spotlight": "Tiêu điểm"
|
||||
},
|
||||
"pref": {
|
||||
"auto": "tự động",
|
||||
|
@ -30,7 +31,12 @@
|
|||
"nord": "Nord",
|
||||
"dracula": "Dracula",
|
||||
"auto_queue": "Tự động xếp hàng các bài hát",
|
||||
"tab": "Thẻ mặc định"
|
||||
"tab": "Thẻ mặc định",
|
||||
"library": "Thư viện",
|
||||
"explore": "Khám phá",
|
||||
"home": "Trang chủ",
|
||||
"charts": "Xếp hạng",
|
||||
"compact": "Chế độ xem nhỏ gọn"
|
||||
},
|
||||
"action": {
|
||||
"cancel": "Hủy",
|
||||
|
@ -38,7 +44,9 @@
|
|||
"add": "Thêm",
|
||||
"back": "Quay lại",
|
||||
"receive": "Nhận",
|
||||
"send": "Gửi"
|
||||
"send": "Gửi",
|
||||
"import": "Nhập",
|
||||
"export": "Xuất"
|
||||
},
|
||||
"instances": {
|
||||
"version": "Phiên bản",
|
||||
|
|
|
@ -32,7 +32,9 @@
|
|||
"back": "返回",
|
||||
"cancel": "取消",
|
||||
"receive": "接收",
|
||||
"send": "发送"
|
||||
"send": "发送",
|
||||
"import": "导入",
|
||||
"export": "导出"
|
||||
},
|
||||
"pref": {
|
||||
"player": "音频播放器",
|
||||
|
@ -50,7 +52,12 @@
|
|||
"light": "亮色",
|
||||
"blur_light": "模糊(亮色)",
|
||||
"dracula": "Dracula",
|
||||
"nord": "Nord"
|
||||
"nord": "Nord",
|
||||
"home": "主页",
|
||||
"explore": "探索",
|
||||
"charts": "排行榜",
|
||||
"library": "收藏库",
|
||||
"compact": "紧凑视图"
|
||||
},
|
||||
"info": {
|
||||
"see_all": "显示全部",
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { Buffer } from 'buffer/';
|
||||
window.Buffer = Buffer;
|
||||
import { json2xml } from 'xml-js';
|
||||
import { useXML } from './xml.js';
|
||||
|
||||
export function useDash(streams, len) {
|
||||
const sets = [],
|
||||
|
@ -119,6 +117,5 @@ export function useDash(streams, len) {
|
|||
],
|
||||
};
|
||||
|
||||
console.log(json2xml(gen));
|
||||
return json2xml(gen);
|
||||
return useXML(gen);
|
||||
}
|
||||
|
|
|
@ -6,15 +6,13 @@ export const HYPERPIPE_INSTANCE = 'hyperpipeapi.onrender.com';
|
|||
export function getPipedQuery() {
|
||||
const papi = new URLSearchParams(location.search).get('pipedapi');
|
||||
|
||||
if (!papi) {
|
||||
return '';
|
||||
}
|
||||
if (!papi) return '';
|
||||
|
||||
return '?pipedapi=' + useSanitize(papi);
|
||||
}
|
||||
|
||||
export async function getJson(url) {
|
||||
const res = await fetch(url)
|
||||
export async function getJson(url, opts) {
|
||||
const res = await fetch(url, opts)
|
||||
.then(res => res.json())
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
|
@ -36,13 +34,13 @@ export async function getJson(url) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function getJsonPiped(path) {
|
||||
export async function getJsonPiped(path, opts) {
|
||||
const root =
|
||||
new URLSearchParams(location.search).get('pipedapi') ||
|
||||
useStore().getItem('pipedapi') ||
|
||||
PIPED_INSTANCE;
|
||||
|
||||
return await getJson('https://' + root + path);
|
||||
return await getJson('https://' + root + path, opts);
|
||||
}
|
||||
|
||||
export async function getJsonHyp(path) {
|
||||
|
@ -51,7 +49,7 @@ export async function getJsonHyp(path) {
|
|||
return await getJson('https://' + root + path);
|
||||
}
|
||||
|
||||
export async function getJsonAuth(path, opts) {
|
||||
export async function getJsonAuth(path, opts = {}) {
|
||||
const root = useStore().getItem('authapi') || PIPED_INSTANCE;
|
||||
|
||||
return await fetch('https://' + root + path, opts)
|
||||
|
|
|
@ -35,9 +35,26 @@ export function useStore() {
|
|||
}
|
||||
}
|
||||
|
||||
export function useShare(data) {
|
||||
if ('share' in navigator) {
|
||||
navigator.share(data).catch(err => {
|
||||
console.err(err);
|
||||
});
|
||||
} else {
|
||||
navigator.clipboard.writeText(data.url).then(
|
||||
() => {
|
||||
alert('Copied to Clipboard');
|
||||
},
|
||||
err => {
|
||||
alert(err);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function useMetadata(url, urls, data) {
|
||||
if ('mediaSession' in navigator) {
|
||||
const now = urls.filter(u => u.url === url)[0];
|
||||
const now = urls.find(u => u.url === url);
|
||||
|
||||
let artwork = [],
|
||||
album = undefined;
|
||||
|
@ -51,7 +68,7 @@ export function useMetadata(url, urls, data) {
|
|||
src: t.url,
|
||||
type: 'image/webp',
|
||||
}));
|
||||
} else artwork = [{ src: data.art, type: 'image/webp' }];
|
||||
} else if (data.art) artwork = [{ src: data.art, type: 'image/webp' }];
|
||||
|
||||
console.log(album, artwork);
|
||||
}
|
||||
|
|
62
src/scripts/xml.js
Normal file
62
src/scripts/xml.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
function useAttr(json) {
|
||||
let attrs = '';
|
||||
|
||||
for (const attr in json) {
|
||||
if (json[attr] != null) {
|
||||
attrs += ' ';
|
||||
attrs += attr;
|
||||
attrs += '="';
|
||||
attrs += ('' + json[attr]).replace(/"/g, '"e;');
|
||||
attrs += '"';
|
||||
}
|
||||
}
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
function useElems(json) {
|
||||
let elems = '';
|
||||
|
||||
json.forEach(elem => {
|
||||
switch (elem.type) {
|
||||
case 'element':
|
||||
elems += '<';
|
||||
elems += elem.name;
|
||||
elems += useAttr(elem.attributes);
|
||||
|
||||
if (elem?.elements?.length > 0) {
|
||||
elems += '>';
|
||||
elems += useElems(elem.elements);
|
||||
elems += '</';
|
||||
elems += elem.name;
|
||||
elems += '>';
|
||||
} else elems += '/>';
|
||||
break;
|
||||
case 'text':
|
||||
elems += ('' + elem.text)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
export function useXML(json) {
|
||||
json = JSON.parse(JSON.stringify(json));
|
||||
|
||||
let base = '';
|
||||
|
||||
base += '<?xml';
|
||||
base += useAttr(json.declaration.attributes);
|
||||
base += '?>';
|
||||
|
||||
base += useElems(json.elements);
|
||||
|
||||
console.log(base);
|
||||
|
||||
return base;
|
||||
}
|
|
@ -12,6 +12,10 @@ export const SUPPORTED_LOCALES = [
|
|||
code: 'az',
|
||||
name: 'Azərbaycan',
|
||||
},
|
||||
{
|
||||
code: 'bn',
|
||||
name: 'বাংলা',
|
||||
},
|
||||
{
|
||||
code: 'bs',
|
||||
name: 'Bosanski',
|
||||
|
@ -68,13 +72,21 @@ export const SUPPORTED_LOCALES = [
|
|||
code: 'nl',
|
||||
name: 'Nederlands',
|
||||
},
|
||||
{
|
||||
code: 'pt',
|
||||
name: 'Português',
|
||||
},
|
||||
{
|
||||
code: 'pt_br',
|
||||
name: 'Português Brasileiro',
|
||||
},
|
||||
{
|
||||
code: 'ro',
|
||||
name: 'Română',
|
||||
},
|
||||
{
|
||||
code: 'ru',
|
||||
name: 'Pусский ',
|
||||
name: 'Pyccкий',
|
||||
},
|
||||
{
|
||||
code: 'sr',
|
||||
|
@ -92,10 +104,6 @@ export const SUPPORTED_LOCALES = [
|
|||
code: 'zh_Hans',
|
||||
name: '中文 (简体)',
|
||||
},
|
||||
{
|
||||
code: 'pt_br',
|
||||
name: 'Português Brasileiro',
|
||||
},
|
||||
];
|
||||
|
||||
export const useNav = defineStore('nav', () => {
|
||||
|
|
|
@ -8,18 +8,17 @@ const store = useStore();
|
|||
|
||||
export const useData = defineStore('data', () => {
|
||||
const state = reactive({
|
||||
title: '',
|
||||
description: '',
|
||||
artist: '',
|
||||
art: '',
|
||||
url: '',
|
||||
artistUrl: '',
|
||||
lyrics: '',
|
||||
src: [],
|
||||
urls: [],
|
||||
});
|
||||
|
||||
const player = usePlayer();
|
||||
title: '',
|
||||
description: '',
|
||||
artist: '',
|
||||
art: '',
|
||||
url: '',
|
||||
artistUrl: '',
|
||||
lyrics: '',
|
||||
src: [],
|
||||
urls: [],
|
||||
}),
|
||||
player = usePlayer();
|
||||
|
||||
async function getSong(e) {
|
||||
console.log(e);
|
||||
|
@ -48,7 +47,7 @@ export const useData = defineStore('data', () => {
|
|||
if (
|
||||
store.getItem('next') !== 'false' &&
|
||||
(!state.urls ||
|
||||
!state.urls.filter(s => s.url == state.url)[0] ||
|
||||
state.urls.findIndex(s => s.url == state.url) < 0 ||
|
||||
state.urls.length == 1)
|
||||
) {
|
||||
const json = await getJsonHyp('/next/' + hash);
|
||||
|
@ -98,8 +97,7 @@ export const useData = defineStore('data', () => {
|
|||
}
|
||||
|
||||
function playNext(u) {
|
||||
const now = state.urls.filter(s => s.url === state.url)[0],
|
||||
i = state.urls.indexOf(now);
|
||||
const i = state.urls.findIndex(s => s.url === state.url);
|
||||
|
||||
if (player.state.loop == 2) getSong(state.url);
|
||||
else if (
|
||||
|
@ -117,8 +115,7 @@ export const useData = defineStore('data', () => {
|
|||
}
|
||||
|
||||
function prevTrack() {
|
||||
const now = state.urls.filter(s => s.url === state.url)[0],
|
||||
i = state.urls.indexOf(now);
|
||||
const i = state.urls.findIndex(s => s.url === state.url);
|
||||
|
||||
if (state.urls[i - 1]) getSong(state.urls[i - 1].url);
|
||||
else if (player.state.loop == 1) {
|
||||
|
@ -130,8 +127,7 @@ export const useData = defineStore('data', () => {
|
|||
}
|
||||
|
||||
function nextTrack() {
|
||||
const now = state.urls.filter(s => s.url === state.url)[0],
|
||||
i = state.urls.indexOf(now);
|
||||
const i = state.urls.findIndex(s => s.url === state.url);
|
||||
|
||||
if (state.urls[i + 1]) getSong(state.urls[i + 1].url);
|
||||
else if (player.state.loop == 1) {
|
||||
|
|
|
@ -3,13 +3,14 @@ import { defineStore } from 'pinia';
|
|||
|
||||
import { useNav } from '@/stores/misc.js';
|
||||
|
||||
import { getJsonPiped, getJsonHyp } from '@/scripts/fetch.js';
|
||||
import { getJsonPiped, getJsonHyp, getJsonAuth } from '@/scripts/fetch.js';
|
||||
import { useRoute } from '@/scripts/util.js';
|
||||
|
||||
export const useResults = defineStore('results', () => {
|
||||
const items = ref({}),
|
||||
search = ref(''),
|
||||
chartsId = ref(''),
|
||||
album = ref(''),
|
||||
next = ref('');
|
||||
|
||||
function setItem(key, val) {
|
||||
|
@ -18,6 +19,9 @@ export const useResults = defineStore('results', () => {
|
|||
}
|
||||
|
||||
function resetItems() {
|
||||
next.value = undefined;
|
||||
album.value = undefined;
|
||||
|
||||
useArtist().reset();
|
||||
for (let i in items.value) {
|
||||
items.value[i] = undefined;
|
||||
|
@ -42,22 +46,24 @@ export const useResults = defineStore('results', () => {
|
|||
console.log('Album: ', e);
|
||||
|
||||
const hash = new URLSearchParams(e.substring(e.indexOf('?'))).get('list'),
|
||||
json = await getJsonPiped('/playlists/' + hash);
|
||||
isAuth =
|
||||
/[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}/.test(
|
||||
hash,
|
||||
),
|
||||
path = '/playlists/' + hash,
|
||||
json = isAuth ? await getJsonAuth(path) : await getJsonPiped(path);
|
||||
|
||||
console.log(json, json.relatedStreams);
|
||||
|
||||
resetItems();
|
||||
|
||||
if (
|
||||
/[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}/.test(
|
||||
hash,
|
||||
)
|
||||
) {
|
||||
album.value = e;
|
||||
|
||||
if (isAuth)
|
||||
json.relatedStreams = json.relatedStreams.map(i => {
|
||||
i.playlistId = hash;
|
||||
return i;
|
||||
});
|
||||
}
|
||||
|
||||
setItem('songs', {
|
||||
items: json.relatedStreams,
|
||||
|
@ -78,6 +84,7 @@ export const useResults = defineStore('results', () => {
|
|||
search,
|
||||
chartsId,
|
||||
next,
|
||||
album,
|
||||
setItem,
|
||||
resetItems,
|
||||
getExplore,
|
||||
|
|
|
@ -2,10 +2,49 @@ import { fileURLToPath, URL } from 'url';
|
|||
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
plugins: [
|
||||
vue(),
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
workbox: {
|
||||
globPatterns: [
|
||||
'**/*.{js,css,html,png,svg,woff2}',
|
||||
'manifest.webmanifest',
|
||||
],
|
||||
},
|
||||
manifest: {
|
||||
name: 'Hyperpipe',
|
||||
short_name: 'Hyperpipe',
|
||||
start_url: '/',
|
||||
display: 'standalone',
|
||||
background_color: '#000',
|
||||
theme_color: '#000',
|
||||
description: 'Privacy respecting YouTube Music Frontend.',
|
||||
icons: [
|
||||
{
|
||||
src: '/android-chrome-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: '/android-chrome-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: '/android-maskable.png',
|
||||
sizes: '1024x1024',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue