mirror of
https://codeberg.org/Hyperpipe/Hyperpipe
synced 2025-06-28 21:18:01 +02:00
Merge pull request 'main' (#2) from main into dev
Reviewed-on: https://codeberg.org/HexagonCDN/Hyperpipe/pulls/2
This commit is contained in:
commit
772664ae27
40 changed files with 989 additions and 838 deletions
|
@ -1,5 +1,9 @@
|
|||
.git
|
||||
.gitea
|
||||
.gitignore
|
||||
.dockerignore
|
||||
.prettierignore
|
||||
.prettierrc.json
|
||||
.woodpecker.yml
|
||||
*.md
|
||||
*.txt
|
39
.woodpecker.yml
Normal file
39
.woodpecker.yml
Normal 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']
|
|
@ -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
|
15
Dockerfile
15
Dockerfile
|
@ -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
|
||||
|
|
|
@ -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'
|
|
@ -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
1174
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ onMounted(() => {
|
|||
v-if="data.state.art"
|
||||
class="art"
|
||||
loading="lazy"
|
||||
:src="data.state.art" />
|
||||
:src="data.state.art.replaceAll('&', '&')" />
|
||||
|
||||
<div class="wrapper">
|
||||
<NowPlaying @get-artist="artist.getArtist" />
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"date": "2022-11-30"
|
||||
"date": "2022-12-13"
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useRand } from '@/scripts/colors.js';
|
|||
|
||||
const rand = useRand();
|
||||
|
||||
const props = defineProps({
|
||||
defineProps({
|
||||
name: String,
|
||||
author: {
|
||||
type: String,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 }}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import SearchBar from '@/components/SearchBar.vue';
|
||||
import IcoHyp from '@/assets/icons/IcoHyp.vue';
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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') }}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
"dark": "Mračno (Zadano)",
|
||||
"dracula": "Drakula",
|
||||
"nord": "Nord",
|
||||
"blur": "Zamućenje",
|
||||
"blur": "Zamagljeno",
|
||||
"blur_light": "Zamućenje (Svjetlo)",
|
||||
"light": "Svjetlo"
|
||||
},
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
},
|
||||
"lyrics": {
|
||||
"load": "Načítání textů",
|
||||
"void": "Žádné texty"
|
||||
"void": "Texty nenalezeny"
|
||||
},
|
||||
"action": {
|
||||
"back": "Zpět",
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
74
src/locales/pt_br.json
Normal 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"
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
"volume": "Подразумевана јачина звука",
|
||||
"tab": "Подразумевана Картица",
|
||||
"light": "Светло",
|
||||
"blur": "Замућење",
|
||||
"blur": "Замагљено",
|
||||
"dracula": "Дракула",
|
||||
"nord": "Норд",
|
||||
"dark": "Мрачно (Подразумевано)",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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": "显示全部",
|
||||
|
|
|
@ -92,6 +92,10 @@ export const SUPPORTED_LOCALES = [
|
|||
code: 'zh_Hans',
|
||||
name: '中文 (简体)',
|
||||
},
|
||||
{
|
||||
code: 'pt_br',
|
||||
name: 'Português Brasileiro',
|
||||
},
|
||||
];
|
||||
|
||||
export const useNav = defineStore('nav', () => {
|
||||
|
|
|
@ -29,7 +29,7 @@ export const useData = defineStore('data', () => {
|
|||
|
||||
console.log(json);
|
||||
|
||||
state.art = json.thumbnailUrl;
|
||||
state.art = json.thumbnailUrl.replaceAll('&', '&');
|
||||
state.description = json.description;
|
||||
state.title = json.title.replaceAll('&', '&');
|
||||
state.artist = json.uploader
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue