Merge pull request 'main' (#2) from main into dev

Reviewed-on: https://codeberg.org/HexagonCDN/Hyperpipe/pulls/2
This commit is contained in:
HexagonCDN 2022-12-19 09:08:53 +00:00
commit 772664ae27
40 changed files with 989 additions and 838 deletions

View file

@ -1,5 +1,9 @@
.git
.gitea
.gitignore
.dockerignore
.prettierignore
.prettierrc.json
.woodpecker.yml
*.md
*.txt

39
.woodpecker.yml Normal file
View file

@ -0,0 +1,39 @@
pipeline:
build:
image: node:alpine
commands:
- npm install
- npm run build
when:
path:
exclude: [ '*.md', '.gitea/*' ]
event: ['push', 'pull_request']
docker:
image: woodpeckerci/plugin-docker-buildx
settings:
platforms: linux/amd64,linux/arm64
repo: codeberg.org/hyperpipe/hyperpipe
registry: codeberg.org
tags: latest
username: snematoda
password:
from_secret: cb_token
when:
branch: main
path:
exclude: [ '*.md', '.gitea/*' ]
event: ['push']
deploy:
image: node:alpine
commands:
- npm install surge
- cp dist/index.html dist/200.html
- npx surge ./dist hyperpipe.surge.sh
secrets: [surge_login, surge_token]
when:
branch: main
path:
exclude: [ '*.md', '.gitea/*' ]
event: ['push']

View file

@ -1,19 +0,0 @@
pipeline:
build:
image: node:alpine
commands:
- npm install
- npm run build
when:
event: [push, pull_request]
deploy:
image: node:alpine
commands:
- npm install surge
- cp dist/index.html dist/200.html
- npx surge ./dist hyperpipe.surge.sh
secrets: [surge_login, surge_token]
when:
branch: main
event: push

View file

@ -1,20 +1,15 @@
FROM node:alpine AS build
ARG api
ARG pipedapi
FROM --platform=$BUILDPLATFORM node:lts AS build
WORKDIR /app/
COPY . .
RUN sed -i "s/hyperpipeapi.onrender.com/$api/g" index.html src/scripts/fetch.js
RUN sed -i "s/pipedapi.kavin.rocks/$pipedapi/g" index.html src/scripts/fetch.js
RUN npm install && \
RUN --mount=type=cache,target=/root/.cache/node \
--mount=type=cache,target=/app/node_modules \
npm install --prefer-offline && \
npm run build
FROM nginx:alpine
FROM --platform=$BUILDPLATFORM nginx:stable
COPY --from=build /app/dist/ /usr/share/nginx/html/
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf

View file

@ -1,14 +0,0 @@
version: '3'
services:
hyperpipe-frontend:
image: hyperpipe-frontend
build:
context: .
args:
pipedapi: pipedapi.kavin.rocks
api: hyperpipeapi.onrender.com
container_name: hyperpipe-frontend
restart: unless-stopped
ports:
- '8080:80'

View file

@ -9,7 +9,6 @@
<link rel="icon" href="/favicon.svg" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta name="msapplication-TileColor" content="#181818" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" />
<link rel="preconnect" href="https://hyperpipeapi.onrender.com" />
<link rel="dns-prefetch" href="https://pipedapi.kavin.rocks" />
<link rel="dns-prefetch" href="https://hyperpipe-proxy.onrender.com" />
@ -29,7 +28,10 @@
<title>Hyperpipe</title>
</head>
<body>
<noscript>Kind Sir, Please Give Me JavaScript</noscript>
<noscript
>JavaScript is required for this site to function. Please enable it in
your browser or browser extension settings.</noscript
>
<div id="app"></div>

1174
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -15,15 +15,15 @@
"dompurify": "^2.4.1",
"mux.js": "^6.2.0",
"peerjs": "^1.4.7",
"pinia": "^2.0.26",
"shaka-player": "^4.3.0",
"pinia": "^2.0.28",
"shaka-player": "^4.3.1",
"stream-browserify": "^3.0.0",
"vue": "^3.2.38",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.2.0",
"prettier": "^2.8.0",
"vite": "^3.2.4"
"@vitejs/plugin-vue": "^4.0.0",
"prettier": "^2.8.1",
"vite": "^4.0.0"
}
}

View file

@ -150,7 +150,7 @@ onMounted(() => {
v-if="data.state.art"
class="art"
loading="lazy"
:src="data.state.art" />
:src="data.state.art.replaceAll('&amp;', '&')" />
<div class="wrapper">
<NowPlaying @get-artist="artist.getArtist" />

View file

@ -1,3 +1,3 @@
{
"date": "2022-11-30"
"date": "2022-12-13"
}

View file

@ -3,7 +3,7 @@ import { useRand } from '@/scripts/colors.js';
const rand = useRand();
const props = defineProps({
defineProps({
name: String,
author: {
type: String,

View file

@ -1,5 +1,5 @@
<script setup>
import { ref, onUpdated } from 'vue';
import { ref } from 'vue';
import Btn from './Btn.vue';
import { useStore } from '@/scripts/util.js';

View file

@ -51,7 +51,9 @@ onMounted(() => {
<template>
<template v-if="data.options.length > 0">
<select :value="id" class="input" @input="id = $event.target.value">
<option v-for="i in data.options" :value="i.id">{{ i.title }}</option>
<option v-for="i in data.options" :value="i.id" :key="i.id">
{{ i.title }}
</option>
</select>
</template>
@ -60,6 +62,7 @@ onMounted(() => {
<div class="grid-3 circle">
<AlbumItem
v-for="i in data.artists"
:key="i.id"
:name="i.title"
:author="i.subtitle"
:art="i.thumbnails[1].url"
@ -72,6 +75,7 @@ onMounted(() => {
<div class="grid">
<SongItem
v-for="i in data.songs"
:key="i.id"
:title="i.title"
:author="i.subtitle"
:channel="'/channel/' + i.subId"

View file

@ -1,5 +1,5 @@
<script setup>
import { reactive, ref, onMounted, onUnmounted } from 'vue';
import { reactive, onMounted } from 'vue';
import { useResults } from '@/stores/results.js';
@ -62,19 +62,21 @@ onMounted(get);
<h2 class="head">{{ data.title }}</h2>
<template v-for="type in ['featured', 'spotlight', 'community']">
<template
v-for="type in ['featured', 'spotlight', 'community']"
:key="type">
<h3 class="head">{{ t('title.' + type) }}</h3>
<div class="grid-3">
<template v-for="i in data[type]">
<AlbumItem
:name="i.title"
:author="i.subtitle"
:art="i.thumbnails[0].url"
@open-album="
getAlbum('/playlist?list=' + i.id);
nav.state.page = 'home';
" />
</template>
<AlbumItem
v-for="i in data[type]"
:key="i.id"
:name="i.title"
:author="i.subtitle"
:art="i.thumbnails[0].url"
@open-album="
getAlbum('/playlist?list=' + i.id);
nav.state.page = 'home';
" />
</div>
</template>
</template>
@ -86,6 +88,7 @@ onMounted(get);
<button
v-for="i in btns.moods"
class="btn"
:key="i.id"
:style="`--btn-color: ${i.subtitle};`"
@click="get(i.id)">
{{ i.title }}
@ -98,6 +101,7 @@ onMounted(get);
<button
v-for="i in btns.genres"
class="btn"
:key="i.id"
:style="`--btn-color: ${i.subtitle};`"
@click="get(i.id)">
{{ i.title }}

View file

@ -1,6 +1,4 @@
<script setup>
import { reactive } from 'vue';
import SearchBar from '@/components/SearchBar.vue';
import IcoHyp from '@/assets/icons/IcoHyp.vue';

View file

@ -30,8 +30,9 @@ const { t } = useI18n(),
results = useResults(),
nav = useNav();
const emit = defineEmits(['play-urls', 'open-playlist']),
list = ref([]),
defineEmits(['play-urls', 'open-playlist']);
const list = ref([]),
show = reactive({
new: false,
sync: false,
@ -137,7 +138,7 @@ const Login = async () => {
},
createPlaylist = async () => {
if (text.value) {
const res = await useAuthCreatePlaylist(text.value);
await useAuthCreatePlaylist(text.value);
getPlaylists();
show.new = false;
@ -312,24 +313,24 @@ onMounted(async () => {
<h2 v-if="list.length > 0">{{ t('playlist.local') }}</h2>
<div class="grid-3">
<template v-for="i in list">
<AlbumItem
:name="i.name"
:author="t('title.songs') + ' • ' + i.urls.length"
:grad="useRand()"
@open-album="Open(i.name)" />
</template>
<AlbumItem
v-for="i in list"
:key="i.name"
:name="i.name"
:author="t('title.songs') + ' • ' + i.urls.length"
:grad="useRand()"
@open-album="Open(i.name)" />
</div>
<h2 class="login-h">{{ t('playlist.remote') }}</h2>
<div v-if="auth" class="grid-3">
<template v-for="i in user.playlists">
<AlbumItem
:name="i.name.replace('Playlist - ', '')"
:art="pathname(i.thumbnail) != '/' ? i.thumbnail : undefined"
@open-album="$emit('open-playlist', '/playlists?list=' + i.id)" />
</template>
<AlbumItem
v-for="i in user.playlists"
:key="i.id"
:name="i.name.replace('Playlist - ', '')"
:art="pathname(i.thumbnail) != '/' ? i.thumbnail : undefined"
@open-album="$emit('open-playlist', '/playlists?list=' + i.id)" />
</div>
<form v-else class="login" @submit.prevent>
<input

View file

@ -1,12 +1,5 @@
<script setup>
import {
ref,
watch,
onMounted,
onUpdated,
onBeforeUnmount,
onUnmounted,
} from 'vue';
import { ref, watch, onMounted, onBeforeUnmount, onUnmounted } from 'vue';
import muxjs from 'mux.js';
window.muxjs = muxjs;
@ -64,7 +57,7 @@ async function Stream() {
if (shaka.Player.isBrowserSupported) {
const audioPlayer = new shaka.Player(audio.value);
const codecs = useStore().getItem('codec');
const codecs = store.getItem('codec');
audioPlayer
.getNetworkingEngine()
@ -94,7 +87,7 @@ async function Stream() {
});
}
const quality = useStore().getItem('quality');
const quality = store.getItem('quality');
if (url) {
window.audioPlayer

View file

@ -12,26 +12,28 @@ defineEmits(['playthis']);
<template>
<Transition name="fade">
<div class="pl-modal placeholder" :data-placeholder="t('playlist.add')">
<template v-for="plurl in data.state.urls">
<div class="pl-item" @click="$emit('playthis', plurl)">
<span
v-if="data.state.url == plurl.url"
class="bars-wrap"
:class="player.state.status">
<div class="bars"></div>
<div class="bars"></div>
<div class="bars"></div>
</span>
<div v-else-if="plurl.thumbnails" class="pl-img">
<img
:src="plurl.thumbnails[0].url"
:height="plurl.thumbnails[0].height"
:width="plurl.thumbnails[0].width"
loading="lazy" />
</div>
<span class="pl-main caps">{{ plurl.title }}</span>
<div
v-for="plurl in data.state.urls"
class="pl-item"
:key="plurl.url"
@click="$emit('playthis', plurl)">
<span
v-if="data.state.url == plurl.url"
class="bars-wrap"
:class="player.state.status">
<div class="bars"></div>
<div class="bars"></div>
<div class="bars"></div>
</span>
<div v-else-if="plurl.thumbnails" class="pl-img">
<img
:src="plurl.thumbnails[0].url"
:height="plurl.thumbnails[0].height"
:width="plurl.thumbnails[0].width"
loading="lazy" />
</div>
</template>
<span class="pl-main caps">{{ plurl.title }}</span>
</div>
</div>
</Transition>
</template>

View file

@ -107,7 +107,9 @@ onMounted(() => {
class="input"
:value="getStore('locale') || 'en'"
@change="setLang($event.target.value)">
<option v-for="i in SUPPORTED_LOCALES" :value="i.code">{{ i.name }}</option>
<option v-for="i in SUPPORTED_LOCALES" :value="i.code" :key="i.code">
{{ i.name }}
</option>
</select>
<h2>{{ t('pref.tab') }}</h2>
@ -205,7 +207,7 @@ onMounted(() => {
<th>{{ t('instances.loc') }}</th>
</tr>
</thead>
<tbody v-for="i in hypInstances">
<tbody v-for="i in hypInstances" :key="i.name">
<tr>
<td>
{{ i.name }}
@ -273,7 +275,7 @@ onMounted(() => {
<th>{{ t('instances.version') }}</th>
</tr>
</thead>
<tbody v-for="i in instances">
<tbody v-for="i in instances" :key="i.name">
<tr>
<td>
{{ i.name.replace('Official', 'Default') }}

View file

@ -1,12 +1,5 @@
<script setup>
import {
ref,
reactive,
watch,
onActivated,
onUpdated,
onDeactivated,
} from 'vue';
import { ref, watch, onActivated, onUpdated, onDeactivated } from 'vue';
import Btn from './Btn.vue';
import SongItem from './SongItem.vue';
@ -241,11 +234,12 @@ onDeactivated(() => {
<button
v-for="f in filters"
class="filter caps"
:key="f"
:data-active="f == filter"
@click="
filter = f;
getSearch(nav.state.search);
"
:data-active="f == filter">
">
{{ t('title.' + f.split('_')[1]) }}
</button>
</div>
@ -255,35 +249,35 @@ onDeactivated(() => {
class="search-wrap">
<h2 v-if="!isSearch">{{ t('title.songs') }}</h2>
<div class="grid">
<template v-for="(song, index) in results.items.songs.items">
<SongItem
:index="index"
:playlistId="song.playlistId"
:author="song.uploaderName || song.subtitle"
:title="song.title || song.name"
:channel="song.uploaderUrl || '/channel/' + song.subId"
:play="song.url || '/watch?v=' + song.id"
:art="
song.thumbnail || song.thumbnails[1]?.url || song.thumbnails[0]?.url
"
@remove="removeSong"
@open-song="
$emit('play-urls', [
{
url: song.url || '/watch?v=' + song.id,
title: song.title || song.name,
thumbnails: [
{
url:
song.thumbnail ||
song.thumbnails[1]?.url ||
song.thumbnails[0]?.url,
},
],
},
])
" />
</template>
<SongItem
v-for="(song, index) in results.items.songs.items"
:key="song.url || song.id"
:index="index"
:playlistId="song.playlistId"
:author="song.uploaderName || song.subtitle"
:title="song.title || song.name"
:channel="song.uploaderUrl || '/channel/' + song.subId"
:play="song.url || '/watch?v=' + song.id"
:art="
song.thumbnail || song.thumbnails[1]?.url || song.thumbnails[0]?.url
"
@remove="removeSong"
@open-song="
$emit('play-urls', [
{
url: song.url || '/watch?v=' + song.id,
title: song.title || song.name,
thumbnails: [
{
url:
song.thumbnail ||
song.thumbnails[1]?.url ||
song.thumbnails[0]?.url,
},
],
},
])
" />
</div>
<a
v-if="artist.state.playlistId"
@ -301,15 +295,15 @@ onDeactivated(() => {
class="search-wrap">
<h2 v-if="!isSearch">{{ t('title.albums') }}</h2>
<div class="grid-3">
<template v-for="album in results.items.albums.items">
<AlbumItem
:author="album.uploaderName || album.subtitle"
:name="album.name || album.title"
:art="album.thumbnail || album.thumbnails[0].url"
@open-album="
results.getAlbum(album.url || '/playlist?list=' + album.id)
" />
</template>
<AlbumItem
v-for="album in results.items.albums.items"
:key="album.url || album.id"
:author="album.uploaderName || album.subtitle"
:name="album.name || album.title"
:art="album.thumbnail || album.thumbnails[0].url"
@open-album="
results.getAlbum(album.url || '/playlist?list=' + album.id)
" />
</div>
</div>
@ -320,6 +314,7 @@ onDeactivated(() => {
<div class="grid-3">
<AlbumItem
v-for="pl in results.items.playlists.items"
:key="pl.url"
:author="pl.videos + ' Songs • ' + pl.uploaderName"
:name="pl.name"
:art="pl.thumbnail"
@ -332,13 +327,13 @@ onDeactivated(() => {
class="search-wrap">
<h2>{{ t('title.singles') }}</h2>
<div class="grid-3">
<template v-for="single in results.items.singles.items">
<AlbumItem
:author="single.subtitle"
:name="single.title"
:art="single.thumbnails[0].url"
@open-album="results.getAlbum('/playlist?list=' + single.id)" />
</template>
<AlbumItem
v-for="single in results.items.singles.items"
:key="single.id"
:author="single.subtitle"
:name="single.title"
:art="single.thumbnails[0].url"
@open-album="results.getAlbum('/playlist?list=' + single.id)" />
</div>
</div>
@ -355,18 +350,17 @@ onDeactivated(() => {
}}
</h2>
<div class="grid-3 circle">
<template
<AlbumItem
v-for="a in results.items.artists
? results.items.artists.items
: results.items.recommendedArtists.items">
<AlbumItem
:author="a.subtitle"
:name="a.name || a.title"
:art="a.thumbnail || a.thumbnails[0].url"
@open-album="
artist.getArtist(a.id || a.url.replace('/channel/', ''))
" />
</template>
: results.items.recommendedArtists.items"
:key="a.id || a.url"
:author="a.subtitle"
:name="a.name || a.title"
:art="a.thumbnail || a.thumbnails[0].url"
@open-album="
artist.getArtist(a.id || a.url.replace('/channel/', ''))
" />
</div>
</div>
</template>

View file

@ -5,6 +5,12 @@ import { useNav, useI18n } from '@/stores/misc.js';
const { t } = useI18n(),
show = ref(false),
nav = useNav();
function search(e) {
nav.state.search = e.target.value;
nav.state.page = 'home';
e.target.blur();
}
</script>
<template>
@ -19,11 +25,8 @@ const { t } = useI18n(),
type="search"
aria-label="Search Input"
:placeholder="t('title.search') + '...'"
@change="
nav.state.search = $event.target.value;
nav.state.page = 'home';
$event.target.blur();
"
@change="search"
@keyup.enter="search"
:value="nav.state.search" />
</div>
</Transition>

View file

@ -5,7 +5,7 @@ import { getJsonAuth } from '@/scripts/fetch.js';
import { useRand } from '@/scripts/colors.js';
import { useStore } from '@/scripts/util.js';
import { useArtist, useResults } from '@/stores/results.js';
import { useArtist } from '@/stores/results.js';
import { useData, usePlayer } from '@/stores/player.js';
const rand = useRand(),

View file

@ -1,5 +1,5 @@
<script setup>
import { ref, reactive, watch } from 'vue';
import { ref, reactive } from 'vue';
import Modal from './Modal.vue';
@ -19,8 +19,9 @@ const { t } = useI18n(),
player = usePlayer(),
store = useStore();
const emit = defineEmits(['save']),
showme = reactive({
defineEmits(['save']);
const showme = reactive({
menu: false,
pl: false,
vol: false,
@ -112,29 +113,29 @@ async function Like() {
}
">
<template #content>
<template v-for="i in list">
<div
class="flex item"
@click="
pl = i.name;
plRemote = false;
"
:data-active="pl == i.name && plRemote == false">
<span>{{ i.name }}</span
><span class="ml-auto">{{ i.urls.length || '' }}</span>
</div>
</template>
<template v-for="i in remote">
<div
class="flex item"
@click="
pl = i.id;
plRemote = true;
"
:data-active="pl == i.id && plRemote == true">
<span>{{ i.name }}</span>
</div>
</template>
<div
v-for="i in list"
:key="i.name"
class="flex item"
@click="
pl = i.name;
plRemote = false;
"
:data-active="pl == i.name && plRemote == false">
<span>{{ i.name }}</span
><span class="ml-auto">{{ i.urls.length || '' }}</span>
</div>
<div
v-for="i in remote"
:key="i.id"
class="flex item"
@click="
pl = i.id;
plRemote = true;
"
:data-active="pl == i.id && plRemote == true">
<span>{{ i.name }}</span>
</div>
</template>
<template #buttons>
<button aria-label="Cancel" @click="showme.pl = false">

View file

@ -58,7 +58,7 @@
"dark": "Mračno (Zadano)",
"dracula": "Drakula",
"nord": "Nord",
"blur": "Zamućenje",
"blur": "Zamagljeno",
"blur_light": "Zamućenje (Svjetlo)",
"light": "Svjetlo"
},

View file

@ -47,7 +47,7 @@
},
"lyrics": {
"load": "Načítání textů",
"void": "Žádné texty"
"void": "Texty nenalezeny"
},
"action": {
"back": "Zpět",

View file

@ -46,9 +46,9 @@
"best": "am Besten",
"tab": "Standard-Registerkarte",
"dark": "Dunkel (Standard)",
"light": "Licht",
"light": "Hell",
"blur": "Unschärfe",
"blur_light": "Unschärfe (Licht)",
"blur_light": "Unschärfe (hell)",
"dracula": "Dracula",
"nord": "Nord"
},

View file

@ -14,7 +14,8 @@
"local": "Regional",
"remote": "Remoto",
"search": "Búsqueda",
"playlists": "Listas de reproducción"
"playlists": "Listas de reproducción",
"feeds": "Fuentes"
},
"action": {
"back": "Atrás",
@ -43,7 +44,13 @@
"auto_queue": "Canciones en la cola de reproducción automáticamente",
"worst": "La peor",
"best": "La mejor",
"tab": "Ficha predeterminada"
"tab": "Ficha predeterminada",
"dark": "Oscuro (Por defecto)",
"blur": "Desenfocar",
"light": "Claro",
"blur_light": "Desenfoque (Luz)",
"dracula": "Drácula",
"nord": "Nord"
},
"info": {
"see_all": "Ver todo",

View file

@ -14,7 +14,8 @@
"remote": "À distance",
"community": "Communauté",
"search": "Recherche",
"playlists": "Listes de lecture"
"playlists": "Listes de lecture",
"feeds": "Flux"
},
"playlist": {
"local": "Listes de lecture locales",
@ -35,7 +36,13 @@
"player": "Lecteur audio",
"auto_queue": "Mettre automatiquement les chansons en file d'attente",
"codec": "Codec",
"tab": "Onglet par défaut"
"tab": "Onglet par défaut",
"dark": "Sombre (par défaut)",
"light": "Clair",
"blur": "Flou",
"blur_light": "Flou (clair)",
"dracula": "Dracula",
"nord": "Nord"
},
"info": {
"see_all": "Voir tout",

View file

@ -1,12 +1,15 @@
{
"title": {
"songs": "Cancións",
"albums": "Álbums",
"singles": "Singles",
"artists": "Artistas",
"similar_artists": "Artistas similares",
"moods": "Estado de ánimo",
"genres": "Xéneros",
"featured": "Destacado"
}
"title": {
"songs": "Cancións",
"albums": "Álbums",
"singles": "Singles",
"artists": "Artistas",
"similar_artists": "Artistas similares",
"moods": "Estado de ánimo",
"genres": "Xéneros",
"featured": "Destacado"
},
"pref": {
"blur": "Desenfoque"
}
}

View file

@ -9,7 +9,13 @@
"quality": "Qualità",
"auto": "automatica",
"worst": "la peggia",
"tab": "Scheda predefinita"
"tab": "Scheda predefinita",
"dark": "Scuro (predefinito)",
"light": "Chiaro",
"blur": "Sfocatura",
"blur_light": "Sfocatura (chiaro)",
"dracula": "Dracula",
"nord": "Nord"
},
"title": {
"albums": "Album",
@ -25,7 +31,8 @@
"local": "Locale",
"search": "Ricerca",
"featured": "In primo piano",
"playlists": "Playlist"
"playlists": "Playlist",
"feeds": "Flussi"
},
"action": {
"add": "Aggiungi",

74
src/locales/pt_br.json Normal file
View file

@ -0,0 +1,74 @@
{
"title": {
"songs": "Músicas",
"albums": "Álbuns",
"singles": "Singles",
"artists": "Artistas",
"similar_artists": "Artistas Similares",
"playlists": "Playlists",
"moods": "Humores",
"genres": "Gêneros",
"featured": "Destaques",
"spotlight": "Foco",
"community": "Comunidade",
"login": "Entrar",
"local": "Local",
"remote": "Remoto",
"search": "Pesquisar",
"feeds": "Feeds"
},
"action": {
"back": "Voltar",
"add": "Adicionar",
"create": "Criar",
"cancel": "Cancelar",
"send": "Enviar",
"receive": "Receber"
},
"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"
},
"pref": {
"theme": "Tema",
"dark": "Escuro (padrão)",
"light": "Claro",
"blur": "Desfocado",
"blur_light": "Desfocado (Claro)",
"dracula": "Dracula",
"nord": "Nord",
"tab": "Aba 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"
},
"info": {
"see_all": "Ver Tudo",
"search": "Comece a pesquisar",
"no_info": "Nenhuma Informação Disponível"
},
"instances": {
"hyp": "Instância do Hyperpipe",
"piped": "Instância do Piped",
"auth": "Instância de Autenticação",
"name": "Nome",
"loc": "Localizações",
"cdn": "CDN",
"up_to_date": "Atualizado",
"version": "Versão"
},
"lyrics": {
"load": "Buscando Letras",
"void": "Sem Letras"
}
}

View file

@ -14,7 +14,8 @@
"login": "Autentificare",
"search": "Căutare",
"local": "Local",
"songs": "Melodii"
"songs": "Melodii",
"feeds": "Fluxuri"
},
"action": {
"back": "Înapoi",
@ -43,7 +44,13 @@
"best": "cel mai bun",
"auto": "auto",
"worst": "cel mai rău",
"volume": "Volum implicit"
"volume": "Volum implicit",
"blur": "Blur",
"dark": "Întunecat (implicit)",
"light": "Luminat",
"blur_light": "Blur (Lumină)",
"dracula": "Dracula",
"nord": "Nord"
},
"instances": {
"cdn": "CDN",

View file

@ -14,7 +14,8 @@
"remote": "Удалённый",
"albums": "Альбомы",
"spotlight": "Поиск",
"playlists": "Плейлисты"
"playlists": "Плейлисты",
"feeds": "Фиды"
},
"action": {
"back": "Назад",
@ -43,7 +44,13 @@
"volume": "Громкость по умолчанию",
"quality": "Качество",
"best": "лучшее",
"tab": "Вкладка по умолчанию"
"tab": "Вкладка по умолчанию",
"dark": "Темный (по умолчанию)",
"light": "Светлый",
"blur": "Размытый",
"blur_light": "Размытый (светлый)",
"dracula": "Дракула",
"nord": "Норд"
},
"instances": {
"piped": "Инстанс Piped",

View file

@ -46,7 +46,7 @@
"volume": "Подразумевана јачина звука",
"tab": "Подразумевана Картица",
"light": "Светло",
"blur": "Замућење",
"blur": "Замагљено",
"dracula": "Дракула",
"nord": "Норд",
"dark": "Мрачно (Подразумевано)",

View file

@ -14,7 +14,8 @@
"genres": "Türler",
"moods": "Ruh hâli",
"spotlight": "Spot ışıkları",
"remote": "Uzak"
"remote": "Uzak",
"feeds": "Akışlar"
},
"action": {
"back": "Geri",
@ -38,12 +39,18 @@
"player": "Ses Çalar",
"auto_queue": "Şarkıları Otomatik Olarak Sırala",
"codec": "Codec",
"tab": "Varsayılan Sekme",
"tab": "Öntanımlı Sekme",
"quality": "Kalite",
"auto": "otomatik",
"best": "en iyi",
"worst": "en kötü",
"volume": "Varsayılan Ses Düzeyi"
"volume": "Öntanımlı Ses Düzeyi",
"dark": "Koyu (Öntanımlı)",
"light": "Açık",
"blur": "Bulanıklaştır",
"blur_light": "Bulanıklaştır (Açık)",
"dracula": "Dracula",
"nord": "Nord"
},
"info": {
"see_all": "Hepsini Gör",

View file

@ -10,7 +10,9 @@
"singles": "Các đĩa đơn",
"login": "Đăng nhập",
"moods": "Chủ đề",
"playlists": "Các danh sách phát"
"playlists": "Các danh sách phát",
"featured": "Nổi bật",
"feeds": "Nguồn cấp dữ liệu"
},
"pref": {
"auto": "tự động",
@ -26,8 +28,9 @@
"blur": "Mờ",
"blur_light": "Mờ (Sáng)",
"nord": "Nord",
"dracula": "Tím",
"auto_queue": "Tự động xếp hàng các bài hát"
"dracula": "Dracula",
"auto_queue": "Tự động xếp hàng các bài hát",
"tab": "Thẻ mặc định"
},
"action": {
"cancel": "Hủy",
@ -42,15 +45,22 @@
"name": "Tên",
"cdn": "CDN",
"loc": "Các vị trí",
"up_to_date": "Phiên bản mới nhất"
"up_to_date": "Phiên bản mới nhất",
"auth": "Instance xác thực"
},
"playlist": {
"create": "Tạo một danh sách phát mới",
"name": "Tên danh sách phát",
"add": "Thêm bài hát vào danh sách phát",
"sync": "Đồng bộ hóa các danh sách phát"
"sync": "Đồng bộ hóa các danh sách phát",
"select": "Lựa chọn danh sách phát để thêm"
},
"info": {
"search": "Bắt đầu tìm kiếm"
"search": "Bắt đầu tìm kiếm",
"see_all": "Xem tất cả"
},
"lyrics": {
"load": "Tìm lời bài hát",
"void": "Không có lời bài hát"
}
}

View file

@ -23,7 +23,8 @@
"songs": "歌曲",
"albums": "专辑",
"login": "登录",
"playlists": "播放列表"
"playlists": "播放列表",
"feeds": "订阅源"
},
"action": {
"add": "添加",
@ -43,7 +44,13 @@
"worst": "最差",
"volume": "默认音量",
"theme": "主题",
"tab": "默认标签页"
"tab": "默认标签页",
"blur": "模糊",
"dark": "深色(默认)",
"light": "亮色",
"blur_light": "模糊(亮色)",
"dracula": "Dracula",
"nord": "Nord"
},
"info": {
"see_all": "显示全部",

View file

@ -92,6 +92,10 @@ export const SUPPORTED_LOCALES = [
code: 'zh_Hans',
name: '中文 (简体)',
},
{
code: 'pt_br',
name: 'Português Brasileiro',
},
];
export const useNav = defineStore('nav', () => {

View file

@ -29,7 +29,7 @@ export const useData = defineStore('data', () => {
console.log(json);
state.art = json.thumbnailUrl;
state.art = json.thumbnailUrl.replaceAll('&amp;', '&');
state.description = json.description;
state.title = json.title.replaceAll('&amp;', '&');
state.artist = json.uploader

View file

@ -68,7 +68,7 @@ export const useResults = defineStore('results', () => {
useNav().state.page = 'home';
next.value =
json.nextpage || json.nextpage != 'null'
json.nextpage && json.nextpage != 'null'
? hash + '?nextpage=' + encodeURIComponent(json.nextpage)
: null;
}