Merge remote-tracking branch 'upstream/master' into feature-add-seen

This commit is contained in:
Chris Hallberg 2023-05-04 10:56:10 -04:00
commit 65bf960c96
97 changed files with 5952 additions and 2954 deletions

View file

@ -22,7 +22,7 @@ body:
label: Official Instance
description: Can the bug be reproduced on the official instance?
options:
- label: The bug is reproducable on the [official hosted instance](http://piped.kavin.rocks/) or is API related.
- label: The bug is reproducable on the [official hosted instance](http://piped.video/) or is API related.
- type: textarea
attributes:

30
.github/workflows/deploy-azure.yml vendored Normal file
View file

@ -0,0 +1,30 @@
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- master
jobs:
build_and_deploy_job:
if: github.event_name == 'push'
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LEMON_WATER_0063A7F03 }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: "upload"
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
# For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
app_location: "/" # App source code path
app_build_command: "yarn build && ./localizefonts.sh"
api_location: "" # Api source code path - optional
output_location: "dist" # Built app content directory - optional
###### End of Repository/Build Configurations ######

View file

@ -17,7 +17,7 @@ jobs:
with:
cache: "yarn"
- run: yarn install --prefer-offline
- run: yarn build --out-dir dist-ci && sed -i 's/fonts.gstatic.com/fonts.kavin.rocks/g' dist-ci/assets/*.css
- run: yarn build && ./localizefonts.sh && mv dist/ dist-ci/
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
@ -33,7 +33,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.ci

View file

@ -17,7 +17,7 @@ jobs:
with:
cache: "yarn"
- run: yarn install --prefer-offline
- run: yarn build && sed -i 's/fonts.gstatic.com/fonts.kavin.rocks/g' dist/assets/*.css && cp dist/index.html dist/ipfs-404.html
- run: yarn build && ./localizefonts.sh && cp dist/index.html dist/ipfs-404.html
- uses: aquiladev/ipfs-action@v0.3.1-alpha.2
id: ipfs-add
with:

22
.github/workflows/weblate-merge.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: Merge Weblate translations
on:
pull_request:
types: [opened, reopened]
jobs:
merge:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check if en.json has been updated
run: |
if -n git diff ${{ github.event.pull_request.base.sha }}..${{ github.sha }} src/locales/en.json; then
exit 1
fi
- name: AutoMerge Weblate translations
if: github.event.pull_request.user.login == 'weblate'
run: gh pr merge --auto --delete-branch --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -2,12 +2,16 @@ FROM node:lts-alpine AS build
WORKDIR /app/
RUN --mount=type=cache,target=/var/cache/apk \
apk add --no-cache \
curl
COPY . .
RUN --mount=type=cache,target=/root/.cache/yarn \
--mount=type=cache,target=/app/node_modules \
yarn install --prefer-offline && \
yarn build && sed -i 's/fonts.gstatic.com/fonts.kavin.rocks/g' dist/assets/*.css
yarn build && ./localizefonts.sh
FROM nginx:alpine

View file

@ -2,7 +2,7 @@
[![AGPL v3](https://shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.en.html)
[![Matrix](https://img.shields.io/matrix/piped:matrix.org)](https://matrix.to/#/#piped:matrix.org)
[![Registered Users](https://pipedapi.kavin.rocks/registered/badge)](https://piped.kavin.rocks/register)
[![Registered Users](https://pipedapi.kavin.rocks/registered/badge)](https://piped.video/register)
[![IPFS Build](https://github.com/TeamPiped/Piped/actions/workflows/ipfs-build.yml/badge.svg)](https://piped-ipfs.kavin.rocks/)
[![GitHub Repo stars](https://img.shields.io/github/stars/TeamPiped/Piped-Frontend?style=social)](https://github.com/TeamPiped/Piped/stargazers)
[![GitHub last commit](https://img.shields.io/github/last-commit/TeamPiped/Piped-Frontend)](https://github.com/TeamPiped/Piped/commits)
@ -39,6 +39,7 @@ By using Piped, you can freely watch and listen to content without the fear of p
- [x] [Available in many languages](src/locales), thanks to [our translators](https://hosted.weblate.org/projects/piped/frontend/)
- [x] Embedded video support
- [x] No age restriction
- [x] Bypasses Geo restrictions if possible through a federated network
**Technical Features**
@ -46,7 +47,8 @@ By using Piped, you can freely watch and listen to content without the fear of p
- [x] Performant by design, designed to handle 1000s of users concurrently
- [x] Does not use official YouTube APIs
- [x] Uses [NewPipeExtractor](https://github.com/TeamNewPipe/NewPipeExtractor) to extract information
- [x] Public [JSON API](https://piped-docs.kavin.rocks/docs/api-documentation/)
- [x] Public [JSON API](https://docs.piped.video/docs/api-documentation/)
- [x] Federated protocol on Matrix to let instances collaborate with each other
## Screenshots
@ -61,13 +63,13 @@ By using Piped, you can freely watch and listen to content without the fear of p
## Self-Hosting
See https://piped-docs.kavin.rocks/docs/self-hosting/ for more details.
See https://docs.piped.video/docs/self-hosting/ for more details.
The source code of the documentation website is available at https://github.com/TeamPiped/Documentation.
## Documentation
The documentation can be found at https://piped-docs.kavin.rocks (accessible via IPNS as well).
The documentation can be found at https://docs.piped.video (accessible via IPNS as well).
## Extensions
@ -79,6 +81,16 @@ To redirect all YouTube links to Piped, you are highly recommended to use either
You can help by translating the project to a language you speak at https://hosted.weblate.org/projects/piped/frontend/
### Mirrors
- Cloudflare Pages - [cf.piped.video](https://cf.piped.video/)
- Vercel - [vc.piped.video](https://vc.piped.video/)
- Render - [re.piped.video](https://re.piped.video/)
- Fleek - [fl.piped.video](https://fl.piped.video/)
- DigitalOcean - [do.piped.video](https://do.piped.video/)
- Netlify - [nf.piped.video](https://nf.piped.video/)
- Azure - [az.piped.video](https://az.piped.video/)
### Forking, and contributing
- Fork the repository on GitHub: https://github.com/TeamPiped/Piped/fork
@ -129,6 +141,11 @@ Contributions in any other form are also welcomed.
- [Yattee](https://github.com/yattee/yattee) - an alternative frontend for YouTube, for IOS.
- [LibreTube](https://github.com/Libre-tube/LibreTube) - an alternative frontend for YouTube, for Android.
- [Hyperpipe](https://codeberg.org/Hyperpipe/Hyperpipe) - an alternative privacy respecting frontend for YouTube Music.
- [Musicale](https://github.com/Bellisario/musicale) - an alternative to YouTube Music, with style.
- [ytify](https://github.com/n-ce/ytify) - a complementary minimal audio streaming frontend for YouTube.
- [PsTube](https://github.com/prateekmedia/pstube) - Watch and download videos without ads on Android, Linux, Windows, iOS, and Mac OSX.
- [Piped-Material](https://github.com/mmjee/Piped-Material) - A fork of Piped, focusing on better performance and a more usable design.
- [ReacTube](https://github.com/NeeRaj-2401/ReacTube) - Privacy friendly & distraction free Youtube front-end using Piped API.
## YourKit

View file

@ -2,6 +2,7 @@ server {
listen 80;
listen [::]:80;
server_name localhost;
error_log off;
location / {
root /usr/share/nginx/html;

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html style="background: #0f0f0f" lang="en">
<html style="background: #0f0f0f" lang="en" >
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
@ -7,6 +7,7 @@
<link rel="icon" href="/favicon.ico" />
<link title="Piped" type="application/opensearchdescription+xml" rel="search" href="/opensearch.xml" />
<title>Piped</title>
<meta name="theme-color" content="#0f0f0f">
<meta property="og:title" content="Piped" />
<meta property="og:type" content="website" />
<meta property="og:image" content="/img/icons/favicon-32x32.png" />

10
localizefonts.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
base='https://fonts\.(gstatic\.com|kavin\.rocks)'
fonts=$(cat dist/assets/* | grep -Po "$base[^)]*" | sort | uniq)
for font in $fonts; do
file="dist/fonts$(echo "$font" | sed -E "s#$base##")"
mkdir -p "$(dirname "$file")"
curl -L "$font" -o "$file"
done
sed -Ei "s#$base#/fonts#g" dist/assets/*

View file

@ -6,44 +6,45 @@
"serve": "vite",
"build": "vite build",
"preview": "vite preview",
"format": "prettier -w --ignore-path .gitignore **/**.{js,vue}",
"lint": "eslint --fix --color --ignore-path .gitignore --ext .js,.vue ."
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "6.2.0",
"@fortawesome/free-brands-svg-icons": "6.2.0",
"@fortawesome/free-solid-svg-icons": "6.2.0",
"@fortawesome/vue-fontawesome": "3.0.2",
"@fortawesome/fontawesome-svg-core": "6.4.0",
"@fortawesome/free-brands-svg-icons": "6.4.0",
"@fortawesome/free-solid-svg-icons": "6.4.0",
"@fortawesome/vue-fontawesome": "3.0.3",
"buffer": "6.0.3",
"dompurify": "2.4.0",
"hotkeys-js": "3.10.0",
"dompurify": "3.0.2",
"hotkeys-js": "3.10.2",
"javascript-time-ago": "2.5.9",
"mux.js": "6.2.0",
"shaka-player": "4.2.3",
"mux.js": "6.3.0",
"shaka-player": "4.3.6",
"stream-browserify": "3.0.0",
"vue": "3.2.43",
"vue": "3.2.47",
"vue-i18n": "9.2.2",
"vue-router": "4.1.6",
"xml-js": "1.6.11"
},
"devDependencies": {
"@iconify/json": "2.1.135",
"@iconify/json": "2.2.58",
"@intlify/vite-plugin-vue-i18n": "6.0.3",
"@unocss/preset-icons": "0.46.4",
"@unocss/preset-web-fonts": "0.46.4",
"@unocss/transformer-directives": "0.46.4",
"@unocss/transformer-variant-group": "0.46.4",
"@vitejs/plugin-legacy": "2.3.1",
"@vitejs/plugin-vue": "3.2.0",
"@vue/compiler-sfc": "3.2.43",
"eslint": "8.27.0",
"eslint-config-prettier": "8.5.0",
"@unocss/preset-icons": "0.51.8",
"@unocss/preset-web-fonts": "0.51.8",
"@unocss/transformer-directives": "0.51.8",
"@unocss/transformer-variant-group": "0.51.8",
"@vitejs/plugin-legacy": "4.0.3",
"@vitejs/plugin-vue": "4.2.1",
"@vue/compiler-sfc": "3.2.47",
"eslint": "8.39.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-vue": "9.7.0",
"prettier": "2.7.1",
"unocss": "0.46.4",
"vite": "3.2.3",
"eslint-plugin-vue": "9.11.0",
"prettier": "2.8.8",
"unocss": "0.51.8",
"vite": "4.3.4",
"vite-plugin-eslint": "1.8.1",
"vite-plugin-pwa": "0.13.3"
"vite-plugin-pwa": "0.14.7"
},
"eslintConfig": {
"root": true,

1
public/_redirects Normal file
View file

@ -0,0 +1 @@
/* /index.html 200

View file

@ -4,7 +4,7 @@
<LongName>Piped Search</LongName>
<Description>Search for videos, channels, and playlists on Piped</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="48" height="48" type="image/x-icon">https://piped.kavin.rocks/favicon.ico</Image>
<Url method="get" rel="results" type="text/html" template="https://piped.kavin.rocks/results?search_query={searchTerms}" />
<Image width="48" height="48" type="image/x-icon">https://piped.video/favicon.ico</Image>
<Url method="get" rel="results" type="text/html" template="https://piped.video/results?search_query={searchTerms}" />
<Url method="get" rel="suggestions" type="application/x-suggestions+json" template="https://pipedapi.kavin.rocks/opensearch/suggestions?query={searchTerms}" />
</OpenSearchDescription>

View file

@ -1,2 +1,2 @@
User-agent: *
Disallow:
Disallow: /

View file

@ -17,5 +17,10 @@
],
"groupName": "unocss"
}
]
],
"lockFileMaintenance": {
"enabled": true,
"automerge": true
},
"platformAutomerge": true
}

View file

@ -1,12 +1,13 @@
<template>
<div class="w-full min-h-screen px-1vw reset" :class="[theme]">
<NavBar />
<router-view v-slot="{ Component }">
<keep-alive :max="5">
<component :is="Component" :key="$route.fullPath" />
</keep-alive>
</router-view>
<div class="flex flex-col w-full min-h-screen px-1vw py-5 antialiased reset" :class="[theme]">
<div class="flex-1">
<NavBar />
<router-view v-slot="{ Component }">
<keep-alive :max="5">
<component :is="Component" :key="$route.fullPath" />
</keep-alive>
</router-view>
</div>
<FooterComponent />
</div>
@ -33,6 +34,18 @@ export default {
let themePref = this.getPreferenceString("theme", "dark");
if (themePref == "auto") this.theme = darkModePreference.matches ? "dark" : "light";
else this.theme = themePref;
// Change title bar color based on user's theme
const themeColor = document.querySelector("meta[name='theme-color']");
if (this.theme === "light") {
themeColor.setAttribute("content", "#FFF");
} else {
themeColor.setAttribute("content", "#0F0F0F");
}
// Used for the scrollbar
const root = document.querySelector(":root");
this.theme == "dark" ? root.classList.add("dark") : root.classList.remove("dark");
},
},
mounted() {
@ -40,29 +53,30 @@ export default {
darkModePreference.addEventListener("change", () => {
this.setTheme();
});
if (this.getPreferenceBoolean("watchHistory", false) || this.getPreferenceBoolean("hideWatched", false)) {
if ("indexedDB" in window) {
const request = indexedDB.open("piped-db", 2);
request.onupgradeneeded = ev => {
const db = request.result;
console.log("Upgrading object store.");
if (!db.objectStoreNames.contains("watch_history")) {
const store = db.createObjectStore("watch_history", { keyPath: "videoId" });
store.createIndex("video_id_idx", "videoId", { unique: true });
store.createIndex("id_idx", "id", { unique: true, autoIncrement: true });
}
if (ev.oldVersion < 2) {
const store = request.transaction.objectStore("watch_history");
store.createIndex("watchedAt", "watchedAt", { unique: false });
}
};
request.onsuccess = e => {
window.db = e.target.result;
};
} else {
console.log("This browser doesn't support IndexedDB");
}
}
if ("indexedDB" in window) {
const request = indexedDB.open("piped-db", 3);
request.onupgradeneeded = ev => {
const db = request.result;
console.log("Upgrading object store.");
if (!db.objectStoreNames.contains("watch_history")) {
const store = db.createObjectStore("watch_history", { keyPath: "videoId" });
store.createIndex("video_id_idx", "videoId", { unique: true });
store.createIndex("id_idx", "id", { unique: true, autoIncrement: true });
}
if (ev.oldVersion < 2) {
const store = request.transaction.objectStore("watch_history");
store.createIndex("watchedAt", "watchedAt", { unique: false });
}
if (!db.objectStoreNames.contains("playlist_bookmarks")) {
const store = db.createObjectStore("playlist_bookmarks", { keyPath: "playlistId" });
store.createIndex("playlist_id_idx", "playlistId", { unique: true });
store.createIndex("id_idx", "id", { unique: true, autoIncrement: true });
}
};
request.onsuccess = e => {
window.db = e.target.result;
};
} else console.log("This browser doesn't support IndexedDB");
const App = this;
@ -99,8 +113,12 @@ b {
text-align: start;
}
:root {
color-scheme: only light;
}
::-webkit-scrollbar {
background-color: #15191a;
background-color: #d1d5db;
}
::-webkit-scrollbar-thumb {
@ -119,13 +137,40 @@ b {
background-color: #0b0e0f;
}
:root {
scrollbar-color: #4b4f52 #d1d5db;
}
.dark ::-webkit-scrollbar {
background-color: #15191a;
}
.dark ::-webkit-scrollbar-thumb {
background-color: #4b4f52;
}
.dark ::-webkit-scrollbar-thumb:hover {
background-color: #5b6469;
}
.dark ::-webkit-scrollbar-thumb:active {
background-color: #485053;
}
.dark ::-webkit-scrollbar-corner {
background-color: #0b0e0f;
}
:root.dark {
scrollbar-color: #4b4f52 #15191a;
}
* {
scrollbar-color: #15191a #444a4e;
@apply font-sans;
}
.video-grid {
@apply grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 col-auto lt-md:gap-x-2.5 md:gap-x-1vw gap-y-1.5;
@apply grid grid-cols-1 mx-2 sm:mx-0 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 col-auto lt-md:gap-x-3 md:gap-x-6 gap-y-5;
}
.btn {

View file

@ -1,43 +1,64 @@
<template>
<ErrorHandler v-if="channel && channel.error" :message="channel.message" :error="channel.error" />
<div v-if="channel" v-show="!channel.error">
<div class="flex justify-center place-items-center">
<img height="48" width="48" class="rounded-full m-1" :src="channel.avatarUrl" />
<h1 v-text="channel.name" />
<font-awesome-icon class="ml-1.5 !text-3xl" v-if="channel.verified" icon="check" />
<LoadingIndicatorPage :show-content="channel != null && !channel.error">
<img
v-if="channel.bannerUrl"
:src="channel.bannerUrl"
class="w-full py-1.5 h-30 md:h-50 object-cover"
loading="lazy"
/>
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="flex place-items-center">
<img height="48" width="48" class="rounded-full m-1" :src="channel.avatarUrl" />
<div class="flex gap-1 items-center">
<h1 v-text="channel.name" class="!text-xl" />
<font-awesome-icon class="!text-xl" v-if="channel.verified" icon="check" />
</div>
</div>
<div class="flex gap-2">
<button
class="btn"
@click="subscribeHandler"
v-t="{
path: `actions.${subscribed ? 'unsubscribe' : 'subscribe'}`,
args: { count: numberFormat(channel.subscriberCount) },
}"
></button>
<!-- RSS Feed button -->
<a
aria-label="RSS feed"
title="RSS feed"
role="button"
v-if="channel.id"
:href="`${apiUrl()}/feed/unauthenticated/rss?channels=${channel.id}`"
target="_blank"
class="btn flex-col"
>
<font-awesome-icon icon="rss" />
</a>
</div>
</div>
<img v-if="channel.bannerUrl" :src="channel.bannerUrl" class="w-full pb-1.5" loading="lazy" />
<!-- eslint-disable-next-line vue/no-v-html -->
<p class="whitespace-pre-wrap">
<span v-html="purifyHTML(urlify(channel.description))" />
</p>
<button
class="btn"
@click="subscribeHandler"
v-t="{
path: `actions.${subscribed ? 'unsubscribe' : 'subscribe'}`,
args: { count: numberFormat(channel.subscriberCount) },
}"
></button>
<!-- RSS Feed button -->
<a
aria-label="RSS feed"
title="RSS feed"
role="button"
v-if="channel.id"
:href="`${apiUrl()}/feed/unauthenticated/rss?channels=${channel.id}`"
target="_blank"
class="btn flex-col mx-3"
>
<font-awesome-icon icon="rss" />
</a>
<div v-if="channel.description" class="whitespace-pre-wrap py-2 mx-1">
<span v-if="fullDescription" v-html="purifyHTML(rewriteDescription(channel.description))" />
<span v-html="purifyHTML(rewriteDescription(channel.description.slice(0, 100)))" v-else />
<span v-if="channel.description.length > 100 && !fullDescription">...</span>
<button
v-if="channel.description.length > 100"
class="hover:underline font-semibold text-neutral-500 block whitespace-normal"
@click="fullDescription = !fullDescription"
>
[{{ fullDescription ? $t("actions.show_less") : $t("actions.show_more") }}]
</button>
</div>
<WatchOnYouTubeButton :link="`https://youtube.com/channel/${this.channel.id}`" />
<div class="flex mt-4 mb-2">
<div class="flex my-2 mx-1">
<button
v-for="(tab, index) in tabs"
:key="tab.name"
@ -61,19 +82,21 @@
hide-channel
/>
</div>
</div>
</LoadingIndicatorPage>
</template>
<script>
import ErrorHandler from "./ErrorHandler.vue";
import ContentItem from "./ContentItem.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
export default {
components: {
ErrorHandler,
ContentItem,
WatchOnYouTubeButton,
LoadingIndicatorPage,
},
data() {
return {
@ -82,6 +105,7 @@ export default {
tabs: [],
selectedTab: 0,
contentItems: [],
fullDescription: false,
};
},
mounted() {
@ -121,7 +145,9 @@ export default {
});
},
async fetchChannel() {
const url = this.apiUrl() + "/" + this.$route.params.path + "/" + this.$route.params.channelId;
const url = this.$route.path.includes("@")
? this.apiUrl() + "/@/" + this.$route.params.channelId
: this.apiUrl() + "/" + this.$route.params.path + "/" + this.$route.params.channelId;
return await this.fetchJson(url);
},
async getChannelData() {
@ -136,10 +162,12 @@ export default {
this.tabs.push({
translatedName: this.$t("video.videos"),
});
const tabQuery = this.$route.query.tab;
for (let i = 0; i < this.channel.tabs.length; i++) {
let tab = this.channel.tabs[i];
tab.translatedName = this.getTranslatedTabName(tab.name);
this.tabs.push(tab);
if (tab.name === tabQuery) this.loadTab(i + 1);
}
}
});
@ -195,23 +223,23 @@ export default {
},
});
} else {
this.handleLocalSubscriptions(this.channel.id);
if (!this.handleLocalSubscriptions(this.channel.id)) return;
}
this.subscribed = !this.subscribed;
},
getTranslatedTabName(tabName) {
let translatedTabName = tabName;
switch (tabName) {
case "Livestreams":
case "livestreams":
translatedTabName = this.$t("titles.livestreams");
break;
case "Playlists":
case "playlists":
translatedTabName = this.$t("titles.playlists");
break;
case "Channels":
case "channels":
translatedTabName = this.$t("titles.channels");
break;
case "Shorts":
case "shorts":
translatedTabName = this.$t("video.shorts");
break;
default:
@ -222,10 +250,17 @@ export default {
},
loadTab(index) {
this.selectedTab = index;
// update the tab query in the url path
const url = new URL(window.location);
url.searchParams.set("tab", this.tabs[index].name ?? "videos");
window.history.replaceState(window.history.state, "", url);
if (index == 0) {
this.contentItems = this.channel.relatedStreams;
return;
}
if (this.tabs[index].content) {
this.contentItems = this.tabs[index].content;
return;

View file

@ -1,6 +1,6 @@
<template>
<!-- desktop view -->
<div v-if="!mobileLayout" class="flex-col overflow-y-scroll max-h-75vh min-h-64 lt-lg:hidden">
<div v-if="!mobileLayout" class="flex-col overflow-y-scroll max-w-35vw max-h-75vh min-h-64 lt-lg:hidden">
<h2 class="mb-2 bg-gray-500/50 p-2" aria-label="chapters" title="chapters">
{{ $t("video.chapters") }} ({{ chapters.length }})
</h2>
@ -13,7 +13,7 @@
>
<div class="flex">
<span class="mt-5 mr-2 text-current" v-text="index + 1" />
<img :src="chapter.image" :alt="chapter.title" />
<img class="shrink-0" :src="chapter.image" :alt="chapter.title" />
<div class="flex flex-col m-2">
<span class="text-sm" :title="chapter.title" v-text="chapter.title" />
<span class="text-sm font-bold text-blue-500" v-text="timeFormat(chapter.start)" />

View file

@ -28,7 +28,7 @@
</div>
<div class="comment-meta text-sm mb-1.5" v-text="comment.commentedTime" />
</div>
<div class="whitespace-pre-wrap" v-html="urlify(comment.commentText)" />
<div class="whitespace-pre-wrap" v-html="purifyHTML(comment.commentText)" />
<div class="comment-footer mt-1 flex items-center">
<div class="i-fa-solid:thumbs-up" />
<span class="ml-1" v-text="numberFormat(comment.likeCount)" />

View file

@ -11,38 +11,45 @@
</a>
</span>
<label for="filters" class="ml-10 mr-2">
<strong v-text="`${$t('actions.filter')}:`" />
</label>
<select id="filters" v-model="selectedFilter" default="all" class="select w-auto" @change="onFilterChange()">
<option v-for="filter in availableFilters" :key="filter" :value="filter" v-t="`video.${filter}`" />
</select>
<span class="md:float-right">
<SortingSelector by-key="uploaded" @apply="order => videos.sort(order)" />
</span>
<hr />
<div class="video-grid">
<VideoItem
:is-feed="true"
v-for="video in videos"
:key="video.url"
:item="video"
@update:watched="onUpdateWatched"
/>
</div>
<LoadingIndicatorPage :show-content="videosStore != null" class="video-grid">
<template v-for="video in videos" :key="video.url">
<VideoItem v-if="shouldShowVideo(video)" :is-feed="true" :item="video" @update:watched="onUpdateWatched" />
</template>
</LoadingIndicatorPage>
</template>
<script>
import VideoItem from "./VideoItem.vue";
import SortingSelector from "./SortingSelector.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
export default {
components: {
VideoItem,
SortingSelector,
LoadingIndicatorPage,
},
data() {
return {
currentVideoCount: 0,
videoStep: 100,
videosStore: [],
videosStore: null,
videos: [],
availableFilters: ["all", "shorts", "videos"],
selectedFilter: "all",
};
},
computed: {
@ -57,6 +64,8 @@ export default {
this.loadMoreVideos();
this.updateWatched(this.videos);
});
this.selectedFilter = this.getPreferenceString("feedFilter") ?? "all";
},
activated() {
document.title = this.$t("titles.feed") + " - Piped";
@ -100,6 +109,19 @@ export default {
const subset = this.videos.filter(({ url }) => urls.includes(url));
if (subset.length > 0) this.updateWatched(subset);
},
shouldShowVideo(video) {
switch (this.selectedFilter.toLowerCase()) {
case "shorts":
return video.isShort;
case "videos":
return !video.isShort;
default:
return true;
}
},
onFilterChange() {
this.setPreference("feedFilter", this.selectedFilter);
},
},
};
</script>

View file

@ -1,10 +1,10 @@
<template>
<footer class="text-center py-4 rounded-xl children:(mx-3) w-full mt-10 mb-5">
<footer class="text-center py-4 rounded-xl children:(mx-3) w-full mt-10">
<a aria-label="GitHub" href="https://github.com/TeamPiped/Piped" target="_blank">
<font-awesome-icon :icon="['fab', 'github']" />
<span class="ml-2" v-t="'actions.source_code'" />
</a>
<a href="https://piped-docs.kavin.rocks/" target="_blank">
<a href="https://docs.piped.video/" target="_blank">
<font-awesome-icon :icon="['fa', 'book']" />
<span class="ml-2" v-t="'actions.documentation'" />
</a>

View file

@ -4,6 +4,8 @@
<div class="flex">
<div>
<button class="btn" v-t="'actions.clear_history'" @click="clearHistory" />
<button class="btn mx-3" v-t="'actions.export_to_json'" @click="exportHistory" />
</div>
<div class="right-1">
@ -36,7 +38,7 @@ export default {
},
mounted() {
(async () => {
if (window.db) {
if (window.db && this.getPreferenceBoolean("watchHistory", false)) {
var tx = window.db.transaction("watch_history", "readonly");
var store = tx.objectStore("watch_history");
const cursorRequest = store.index("watchedAt").openCursor(null, "prev");
@ -52,6 +54,8 @@ export default {
duration: video.duration,
thumbnail: video.thumbnail,
watchedAt: video.watchedAt,
watched: true,
currentTime: video.currentTime,
});
if (this.videos.length < 1000) cursor.continue();
}
@ -71,6 +75,22 @@ export default {
}
this.videos = [];
},
exportHistory() {
const dateStr = new Date().toISOString().split(".")[0];
let json = {
format: "Piped",
version: 1,
playlists: [
{
name: `Piped History ${dateStr}`,
type: "history",
visibility: "private",
videos: this.videos.map(video => "https://youtube.com" + video.url),
},
],
};
this.download(JSON.stringify(json), `piped_history_${dateStr}.json`, "application/json");
},
},
};
</script>

View file

@ -158,7 +158,11 @@ export default {
: [...new Set((this.getLocalSubscriptions() ?? []).concat(newChannels))];
// Sort for better cache hits
subscriptions.sort();
localStorage.setItem("localSubscriptions", JSON.stringify(subscriptions));
try {
localStorage.setItem("localSubscriptions", JSON.stringify(subscriptions));
} catch (e) {
alert(this.$t("info.local_storage"));
}
},
},
};

View file

@ -0,0 +1,55 @@
<template>
<div v-if="!showContent" class="flex min-h-[75vh] w-full justify-center items-center">
<span id="spinner" />
</div>
<div v-else>
<slot />
</div>
</template>
<style>
#spinner:after {
--spinner-color: #000;
}
.dark #spinner:after {
--spinner-color: #fff;
}
#spinner {
display: inline-block;
width: 70px;
height: 70px;
}
#spinner:after {
content: " ";
display: block;
width: 54px;
height: 54px;
margin: 8px;
border-radius: 50%;
border: 4px solid var(--spinner-color);
border-color: var(--spinner-color) transparent var(--spinner-color) transparent;
animation: spinner 1.2s linear infinite;
}
@keyframes spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
<script>
export default {
props: {
showContent: {
type: Boolean,
required: true,
},
},
};
</script>

View file

@ -34,7 +34,10 @@ export default {
<style scoped>
.modal {
@apply fixed z-50 top-0 left-0 w-full h-full bg-dark-900 bg-opacity-80 transition-opacity table;
@apply fixed z-50 top-0 left-0 w-full h-full bg-gray bg-opacity-80 transition-opacity table;
}
.dark .modal {
@apply bg-dark-900 bg-opacity-80;
}
.modal > div {
@ -42,7 +45,10 @@ export default {
}
.modal-container {
@apply w-min m-auto px-8 bg-dark-700 p-6 rounded-xl min-w-[20vw] relative;
@apply w-min m-auto px-8 bg-white p-6 rounded-xl min-w-[20vw] relative;
}
.dark .modal-container {
@apply bg-dark-700;
}
.modal-container > button {

View file

@ -1,5 +1,5 @@
<template>
<nav class="flex flex-wrap items-center justify-center px-2 sm:px-4 py-2.5 w-full relative">
<nav class="flex flex-wrap items-center justify-center px-2 sm:px-4 pb-2.5 w-full relative">
<div class="flex-1 flex justify-start">
<router-link class="flex font-bold text-3xl items-center font-sans" to="/"
><img
@ -11,10 +11,10 @@
/>iped</router-link
>
</div>
<div class="lt-md:hidden">
<div class="lt-md:hidden search-container">
<input
v-model="searchText"
class="input w-72 h-10"
class="input w-72 h-10 pr-20"
type="text"
role="search"
ref="videoSearch"
@ -25,6 +25,7 @@
@focus="onInputFocus"
@blur="onInputBlur"
/>
<span v-if="searchText" class="delete-search" @click="searchText = ''"></span>
</div>
<!-- three vertical lines for toggling the hamburger menu on mobile -->
<button class="md:hidden flex flex-col justify-end mr-3" @click="showTopNav = !showTopNav">
@ -49,7 +50,7 @@
<li v-if="shouldShowHistory">
<router-link v-t="'titles.history'" to="/history" />
</li>
<li v-if="authenticated">
<li>
<router-link v-t="'titles.playlists'" to="/playlists" />
</li>
<li v-if="!shouldShowTrending">
@ -78,7 +79,7 @@
<li v-if="shouldShowHistory">
<router-link v-t="'titles.history'" to="/history" />
</li>
<li v-if="authenticated">
<li>
<router-link v-t="'titles.playlists'" to="/playlists" />
</li>
<li v-if="!shouldShowTrending">
@ -86,7 +87,7 @@
</li>
</ul>
<!-- search suggestions for mobile devices -->
<div class="w-{full - 4} md:hidden mx-2">
<div class="mobile-search md:hidden mx-2 search-container">
<input
v-model="searchText"
class="input h-10 w-full"
@ -99,6 +100,7 @@
@focus="onInputFocus"
@blur="onInputBlur"
/>
<span v-if="searchText" class="delete-search" @click="searchText = ''"></span>
</div>
<SearchSuggestions
v-show="(searchText || showSearchHistory) && suggestionsVisible"
@ -137,8 +139,8 @@ export default {
shouldShowTrending(_this) {
return _this.getPreferenceString("homepage", "trending") != "trending";
},
showSearchHistory() {
return localStorage.getItem("searchHistory") && localStorage.getItem("search_history");
showSearchHistory(_this) {
return _this.getPreferenceBoolean("searchHistory", false) && localStorage.getItem("search_history");
},
},
methods: {
@ -178,3 +180,17 @@ export default {
},
};
</script>
<style>
.search-container {
@apply relative inline-flex items-center;
}
.delete-search {
@apply absolute right-3 cursor-pointer rounded-full bg-[#ccc] w-4 h-4 text-center text-black opacity-50 hover:(opacity-70) text-size-[13px];
line-height: 1.05;
}
.mobile-search {
width: calc(100% - 1rem);
@apply mx-2;
}
</style>

View file

@ -10,14 +10,15 @@
</p>
</router-link>
<p v-if="props.item.description" v-text="props.item.description" />
<router-link v-if="props.item.uploaderUrl" class="link" :to="props.item.uploaderUrl">
<p>
<span v-text="props.item.uploader" />
<span v-text="props.item.uploaderName" />
<font-awesome-icon class="ml-1.5" v-if="props.item.uploaderVerified" icon="check" />
</p>
</router-link>
<a v-else-if="props.item.uploaderName" class="link" v-text="props.item.uploaderName" />
<a v-if="props.item.uploaderName" class="link" v-text="props.item.uploaderName" />
<template v-if="props.item.videos >= 0">
<br v-if="props.item.uploaderName" />
<strong v-text="`${props.item.videos} ${$t('video.videos')}`" />

View file

@ -1,7 +1,7 @@
<template>
<ErrorHandler v-if="playlist && playlist.error" :message="playlist.message" :error="playlist.error" />
<div v-if="playlist" v-show="!playlist.error">
<LoadingIndicatorPage :show-content="playlist" v-show="!playlist.error">
<h1 class="text-center my-4" v-text="playlist.name" />
<div class="flex justify-between items-center">
@ -14,6 +14,10 @@
<div>
<strong v-text="`${playlist.videos} ${$t('video.videos')}`" />
<br />
<button class="btn mr-1" v-if="!isPipedPlaylist" @click="bookmarkPlaylist">
{{ $t(`actions.${isBookmarked ? "playlist_bookmarked" : "bookmark_playlist"}`)
}}<font-awesome-icon class="ml-3" icon="bookmark" />
</button>
<button class="btn mr-1" v-if="authenticated && !isPipedPlaylist" @click="clonePlaylist">
{{ $t("actions.clone_playlist") }}<font-awesome-icon class="ml-3" icon="clone" />
</button>
@ -42,11 +46,12 @@
width="168"
/>
</div>
</div>
</LoadingIndicatorPage>
</template>
<script>
import ErrorHandler from "./ErrorHandler.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import VideoItem from "./VideoItem.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
@ -55,11 +60,13 @@ export default {
ErrorHandler,
VideoItem,
WatchOnYouTubeButton,
LoadingIndicatorPage,
},
data() {
return {
playlist: null,
admin: false,
isBookmarked: false,
};
},
computed: {
@ -83,8 +90,9 @@ export default {
},
}).then(json => {
if (json.error) alert(json.error);
else if (json.filter(playlist => playlist.id === playlistId).length > 0) this.admin = true;
else if (json.some(playlist => playlist.id === playlistId)) this.admin = true;
});
this.isPlaylistBookmarked();
},
activated() {
window.addEventListener("scroll", this.handleScroll);
@ -100,7 +108,10 @@ export default {
async getPlaylistData() {
this.fetchPlaylist()
.then(data => (this.playlist = data))
.then(() => this.updateTitle());
.then(() => {
this.updateTitle();
this.updateWatched(this.playlist.relatedStreams);
});
},
async updateTitle() {
document.title = this.playlist.name + " - Piped";
@ -140,10 +151,52 @@ export default {
downloadPlaylistAsTxt() {
var data = "";
this.playlist.relatedStreams.forEach(element => {
data += "https://piped.kavin.rocks" + element.url + "\n";
data += "https://piped.video" + element.url + "\n";
});
this.download(data, this.playlist.name + ".txt", "text/plain");
},
async bookmarkPlaylist() {
if (!this.playlist) return;
if (this.isBookmarked) {
this.removePlaylistBookmark();
return;
}
if (window.db) {
const playlistId = this.$route.query.list;
var tx = window.db.transaction("playlist_bookmarks", "readwrite");
var store = tx.objectStore("playlist_bookmarks");
store.put({
playlistId: playlistId,
name: this.playlist.name,
uploader: this.playlist.uploader,
uploaderUrl: this.playlist.uploaderUrl,
thumbnail: this.playlist.thumbnailUrl,
uploaderAvatar: this.playlist.uploaderAvatar,
videos: this.playlist.videos,
});
this.isBookmarked = true;
}
},
async removePlaylistBookmark() {
var tx = window.db.transaction("playlist_bookmarks", "readwrite");
var store = tx.objectStore("playlist_bookmarks");
store.delete(this.$route.query.list);
this.isBookmarked = false;
},
async isPlaylistBookmarked() {
// needed in order to change the is bookmarked var later
const App = this;
const playlistId = this.$route.query.list;
var tx = window.db.transaction("playlist_bookmarks", "readwrite");
var store = tx.objectStore("playlist_bookmarks");
var req = store.openCursor(playlistId);
req.onsuccess = function (e) {
var cursor = e.target.result;
App.isBookmarked = cursor ? true : false;
};
},
},
};
</script>

View file

@ -32,6 +32,7 @@ export default {
},
mounted() {
this.updateScroll();
this.updateWatched(this.playlist.relatedStreams);
},
methods: {
updateScroll() {

View file

@ -1,9 +1,19 @@
<template>
<h1 class="font-bold text-center my-4" v-t="'titles.playlists'" />
<h2 v-if="authenticated" class="font-bold my-4" v-t="'titles.playlists'" />
<hr />
<button v-t="'actions.create_playlist'" class="btn" @click="createPlaylist" />
<div v-if="authenticated" class="flex justify-between mb-3">
<button v-t="'actions.create_playlist'" class="btn" @click="onCreatePlaylist" />
<div class="flex">
<button
v-if="this.playlists.length > 0"
v-t="'actions.export_to_json'"
class="btn"
@click="exportPlaylists"
/>
<input id="fileSelector" ref="fileSelector" type="file" class="display-none" @change="importPlaylists" />
<label for="fileSelector" v-t="'actions.import_from_json'" class="btn ml-2" />
</div>
</div>
<div class="video-grid">
<div v-for="playlist in playlists" :key="playlist.id">
@ -26,6 +36,35 @@
<button class="btn h-auto ml-2" @click="deletePlaylist(playlist.id)" v-t="'actions.delete_playlist'" />
</div>
</div>
<hr />
<h2 class="font-bold my-4" v-t="'titles.bookmarks'" />
<div v-if="bookmarks" class="video-grid">
<router-link
v-for="(playlist, index) in bookmarks"
:key="playlist.playlistId"
:to="`/playlist?list=${playlist.playlistId}`"
>
<img class="w-full" :src="playlist.thumbnail" alt="thumbnail" />
<div class="relative text-sm">
<span class="thumbnail-overlay thumbnail-right" v-text="`${playlist.videos} ${$t('video.videos')}`" />
<div class="absolute bottom-100px right-5px px-5px z-100" @click.prevent="removeBookmark(index)">
<font-awesome-icon class="ml-3" icon="bookmark" />
</div>
</div>
<p
style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical"
class="my-2 overflow-hidden flex link"
:title="playlist.name"
v-text="playlist.name"
/>
<a :href="playlist.uploaderUrl" class="flex items-center">
<img class="rounded-full w-32px h-32px" :src="playlist.uploaderAvatar" />
<span class="ml-3 hover:underline" v-text="playlist.uploader" />
</a>
</router-link>
</div>
<br />
</template>
@ -34,11 +73,12 @@ export default {
data() {
return {
playlists: [],
bookmarks: [],
};
},
mounted() {
if (this.authenticated) this.fetchPlaylists();
else this.$router.push("/login");
this.loadPlaylistBookmarks();
},
activated() {
document.title = this.$t("titles.playlists") + " - Piped";
@ -94,22 +134,120 @@ export default {
else this.playlists = this.playlists.filter(playlist => playlist.id !== id);
});
},
createPlaylist() {
onCreatePlaylist() {
const name = prompt(this.$t("actions.create_playlist"));
if (name)
this.fetchJson(this.authApiUrl() + "/user/playlists/create", null, {
method: "POST",
body: JSON.stringify({
name: name,
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
}).then(json => {
if (json.error) alert(json.error);
else this.fetchPlaylists();
});
if (!name) return;
this.createPlaylist(name).then(json => {
if (json.error) alert(json.error);
else this.fetchPlaylists();
});
},
async createPlaylist(name) {
let json = await this.fetchJson(this.authApiUrl() + "/user/playlists/create", null, {
method: "POST",
body: JSON.stringify({
name: name,
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
});
return json;
},
async exportPlaylists() {
if (!this.playlists) return;
let json = {
format: "Piped",
version: 1,
playlists: [],
};
let tasks = this.playlists.map(playlist => this.fetchPlaylistJson(playlist.id));
json.playlists = await Promise.all(tasks);
this.download(JSON.stringify(json), "playlists.json", "application/json");
},
async fetchPlaylistJson(playlistId) {
let playlist = await this.fetchJson(this.authApiUrl() + "/playlists/" + playlistId);
let playlistJson = {
name: playlist.name,
// possible other types: history, watch later, ...
type: "playlist",
// as Invidious supports public and private playlists
visibility: "private",
// list of the videos, starting with "https://youtube.com" to clarify that those are YT videos
videos: playlist.relatedStreams.map(stream => "https://youtube.com" + stream.url),
};
return playlistJson;
},
async importPlaylists() {
const file = this.$refs.fileSelector.files[0];
let text = await file.text();
let tasks = [];
// list of playlists exported from Piped
if (text.includes("playlists")) {
let playlists = JSON.parse(text).playlists;
if (!playlists.length) {
alert(this.$t("actions.no_valid_playlists"));
return;
}
for (var i = 0; i < playlists.length; i++) {
tasks.push(this.createPlaylistWithVideos(playlists[i]));
}
// CSV from Google Takeout
} else if (file.name.slice(-4).toLowerCase() == ".csv") {
const lines = text.split("\n");
const playlist = {
name: lines[1].split(",")[4],
videos: lines
.slice(4, lines.length)
.filter(line => line != "")
.map(line => `https://youtube.com/watch?v=${line.split(",")[0]}`),
};
tasks.push(this.createPlaylistWithVideos(playlist));
} else {
alert(this.$t("actions.no_valid_playlists"));
return;
}
await Promise.all(tasks);
window.location.reload();
},
async createPlaylistWithVideos(playlist) {
let newPlaylist = await this.createPlaylist(playlist.name);
let videoIds = playlist.videos.map(url => url.substr(-11));
await this.addVideosToPlaylist(newPlaylist.playlistId, videoIds);
},
async addVideosToPlaylist(playlistId, videoIds) {
await this.fetchJson(this.authApiUrl() + "/user/playlists/add", null, {
method: "POST",
body: JSON.stringify({
playlistId: playlistId,
videoIds: videoIds,
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
});
},
async loadPlaylistBookmarks() {
if (!window.db) return;
var tx = window.db.transaction("playlist_bookmarks", "readonly");
var store = tx.objectStore("playlist_bookmarks");
const cursorRequest = store.openCursor();
cursorRequest.onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
const bookmark = cursor.value;
this.bookmarks.push(bookmark);
cursor.continue();
}
};
},
async removeBookmark(index) {
var tx = window.db.transaction("playlist_bookmarks", "readwrite");
var store = tx.objectStore("playlist_bookmarks");
store.delete(this.bookmarks[index].playlistId);
this.bookmarks.splice(index, 1);
},
},
};

View file

@ -45,6 +45,16 @@
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkAutoPlayNextCountdown">
<strong v-t="'actions.autoplay_next_countdown'" />
<input
id="chkAutoPlayNextCountdown"
v-model="autoPlayNextCountdown"
class="input w-24"
type="number"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkAudioOnly">
<strong v-t="'actions.audio_only'" />
<input id="chkAudioOnly" v-model="listen" class="checkbox" type="checkbox" @change="onChange($event)" />
@ -183,71 +193,36 @@
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipSponsors">
<strong v-t="'actions.skip_sponsors'" />
<input id="chkSkipSponsors" v-model="skipSponsor" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<label class="pref" for="chkSkipIntro">
<strong v-t="'actions.skip_intro'" />
<input id="chkSkipIntro" v-model="skipIntro" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<label class="pref" for="chkSkipOutro">
<strong v-t="'actions.skip_outro'" />
<input id="chkSkipOutro" v-model="skipOutro" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<label class="pref" for="chkSkipPreview">
<strong v-t="'actions.skip_preview'" />
<input id="chkSkipPreview" v-model="skipPreview" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<label class="pref" for="chkSkipInteraction">
<strong v-t="'actions.skip_interaction'" />
<input
id="chkSkipInteraction"
v-model="skipInteraction"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipSelfPromo">
<strong v-t="'actions.skip_self_promo'" />
<input
id="chkSkipSelfPromo"
v-model="skipSelfPromo"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipNonMusic">
<strong v-t="'actions.skip_non_music'" />
<input
id="chkSkipNonMusic"
v-model="skipMusicOffTopic"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipHighlight">
<strong v-t="'actions.skip_highlight'" />
<input
id="chkSkipHighlight"
v-model="skipHighlight"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkSkipFiller">
<strong v-t="'actions.skip_filler_tangent'" />
<input id="chkSkipFiller" v-model="skipFiller" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<label class="pref" for="chkShowMarkers">
<strong v-t="'actions.show_markers'" />
<input id="chkShowMarkers" v-model="showMarkers" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<div v-if="sponsorBlock">
<label v-for="[name, item] in skipOptions" class="pref" :for="'ddlSkip_' + name" :key="name">
<strong v-t="item.label" />
<select :id="'ddlSkip_' + name" v-model="item.value" class="select w-auto" @change="onChange($event)">
<option v-t="'actions.no'" value="no" />
<option v-t="'actions.skip_button_only'" value="button" />
<option v-t="'actions.skip_automatically'" value="auto" />
</select>
</label>
<label class="pref" for="chkShowMarkers">
<strong v-t="'actions.show_markers'" />
<input
id="chkShowMarkers"
v-model="showMarkers"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="txtMinSegmentLength">
<strong v-t="'actions.min_segment_length'" />
<input
id="txtMinSegmentLength"
v-model="minSegmentLength"
class="input w-24"
type="text"
@change="onChange($event)"
/>
</label>
</div>
<h2 class="text-center" v-t="'titles.instance'" />
<label class="pref" for="ddlInstanceSelection">
<strong v-text="`${$t('actions.instance_selection')}:`" />
@ -366,18 +341,22 @@ export default {
selectedAuthInstance: null,
instances: [],
sponsorBlock: true,
skipSponsor: true,
skipIntro: false,
skipOutro: false,
skipPreview: false,
skipInteraction: true,
skipSelfPromo: true,
skipMusicOffTopic: true,
skipHighlight: false,
skipFiller: false,
skipOptions: new Map([
["sponsor", { value: "auto", label: "actions.skip_sponsors" }],
["intro", { value: "no", label: "actions.skip_intro" }],
["outro", { value: "no", label: "actions.skip_outro" }],
["preview", { value: "no", label: "actions.skip_preview" }],
["interaction", { value: "auto", label: "actions.skip_interaction" }],
["selfpromo", { value: "auto", label: "actions.skip_self_promo" }],
["music_offtopic", { value: "auto", label: "actions.skip_non_music" }],
["poi_highlight", { value: "no", label: "actions.skip_highlight" }],
["filler", { value: "no", label: "actions.skip_filler_tangent" }],
]),
showMarkers: true,
minSegmentLength: 0,
selectedTheme: "dark",
autoPlayVideo: true,
autoPlayNextCountdown: 5,
listen: false,
resolutions: [144, 240, 360, 480, 720, 1080, 1440, 2160, 4320],
defaultQuality: 0,
@ -397,6 +376,7 @@ export default {
languages: [
{ code: "ar", name: "Arabic" },
{ code: "az", name: "Azərbaycan" },
{ code: "bg", name: "Български" },
{ code: "bn", name: "বাংলা" },
{ code: "bs", name: "Bosanski" },
{ code: "ca", name: "Català" },
@ -415,6 +395,7 @@ export default {
{ code: "hi", name: "हिंदी" },
{ code: "id", name: "Indonesia" },
{ code: "is", name: "Íslenska" },
{ code: "kab", name: "Taqbaylit" },
{ code: "hr", name: "Hrvatski" },
{ code: "it", name: "Italiano" },
{ code: "ja", name: "日本語" },
@ -423,12 +404,15 @@ export default {
{ code: "ml", name: "മലയാളം" },
{ code: "nb_NO", name: "Norwegian Bokmål" },
{ code: "nl", name: "Nederlands" },
{ code: "oc", name: "Occitan" },
{ code: "or", name: "ଓଡ଼ିଆ" },
{ code: "pl", name: "Polski" },
{ code: "pt", name: "Português" },
{ code: "pt_PT", name: "Português (Portugal)" },
{ code: "pt_BR", name: "Português (Brasil)" },
{ code: "ro", name: "Română" },
{ code: "ru", name: "Русский" },
{ code: "si", name: "සිංහල" },
{ code: "sr", name: "Српски" },
{ code: "sv", name: "Svenska" },
{ code: "ta", name: "தமிழ்" },
@ -453,7 +437,7 @@ export default {
this.fetchJson("https://piped-instances.kavin.rocks/").then(resp => {
this.instances = resp;
if (this.instances.filter(instance => instance.api_url == this.apiUrl()).length == 0)
if (!this.instances.some(instance => instance.api_url == this.apiUrl()))
this.instances.push({
name: "Custom Instance",
api_url: this.apiUrl(),
@ -468,57 +452,28 @@ export default {
this.selectedAuthInstance = this.getPreferenceString("auth_instance_url", this.selectedInstance);
this.sponsorBlock = this.getPreferenceBoolean("sponsorblock", true);
if (localStorage.getItem("selectedSkip") !== null) {
var skipList = localStorage.getItem("selectedSkip").split(",");
this.skipSponsor =
this.skipIntro =
this.skipOutro =
this.skipPreview =
this.skipInteraction =
this.skipSelfPromo =
this.skipMusicOffTopic =
this.skipHighlight =
this.skipFiller =
false;
var skipOptions, skipList;
if ((skipOptions = this.getPreferenceJSON("skipOptions")) !== undefined) {
Object.entries(skipOptions).forEach(([key, value]) => {
var opt = this.skipOptions.get(key);
if (opt !== undefined) opt.value = value;
else console.log("Unknown sponsor type: " + key);
});
} else if ((skipList = this.getPreferenceString("selectedSkip")) !== undefined) {
skipList = skipList.split(",");
this.skipOptions.forEach(opt => (opt.value = "no"));
skipList.forEach(skip => {
switch (skip) {
case "sponsor":
this.skipSponsor = true;
break;
case "intro":
this.skipIntro = true;
break;
case "outro":
this.skipOutro = true;
break;
case "preview":
this.skipPreview = true;
break;
case "interaction":
this.skipInteraction = true;
break;
case "selfpromo":
this.skipSelfPromo = true;
break;
case "music_offtopic":
this.skipMusicOffTopic = true;
break;
case "poi_highlight":
this.skipHighlight = true;
break;
case "filler":
this.skipFiller = true;
break;
default:
console.log("Unknown sponsor type: " + skip);
break;
}
var opt = this.skipOptions.get(skip);
if (opt !== undefined) opt.value = "auto";
else console.log("Unknown sponsor type: " + skip);
});
}
this.showMarkers = this.getPreferenceBoolean("showMarkers", true);
this.minSegmentLength = Math.max(this.getPreferenceNumber("minSegmentLength", 0), 0);
this.selectedTheme = this.getPreferenceString("theme", "dark");
this.autoPlayVideo = this.getPreferenceBoolean("playerAutoPlay", true);
this.autoPlayNextCountdown = this.getPreferenceNumber("autoPlayNextCountdown", 5);
this.listen = this.getPreferenceBoolean("listen", false);
this.defaultQuality = Number(localStorage.getItem("quality"));
this.bufferingGoal = Math.max(Number(localStorage.getItem("bufferGoal")), 10);
@ -565,21 +520,15 @@ export default {
localStorage.setItem("auth_instance_url", this.selectedAuthInstance);
localStorage.setItem("sponsorblock", this.sponsorBlock);
var sponsorSelected = [];
if (this.skipSponsor) sponsorSelected.push("sponsor");
if (this.skipIntro) sponsorSelected.push("intro");
if (this.skipOutro) sponsorSelected.push("outro");
if (this.skipPreview) sponsorSelected.push("preview");
if (this.skipInteraction) sponsorSelected.push("interaction");
if (this.skipSelfPromo) sponsorSelected.push("selfpromo");
if (this.skipMusicOffTopic) sponsorSelected.push("music_offtopic");
if (this.skipHighlight) sponsorSelected.push("poi_highlight");
if (this.skipFiller) sponsorSelected.push("filler");
localStorage.setItem("selectedSkip", sponsorSelected);
var skipOptions = {};
this.skipOptions.forEach((v, k) => (skipOptions[k] = v.value));
localStorage.setItem("skipOptions", JSON.stringify(skipOptions));
localStorage.setItem("showMarkers", this.showMarkers);
localStorage.setItem("minSegmentLength", this.minSegmentLength);
localStorage.setItem("theme", this.selectedTheme);
localStorage.setItem("playerAutoPlay", this.autoPlayVideo);
localStorage.setItem("autoPlayNextCountdown", this.autoPlayNextCountdown);
localStorage.setItem("listen", this.listen);
localStorage.setItem("quality", this.defaultQuality);
localStorage.setItem("bufferGoal", this.bufferingGoal);
@ -667,4 +616,10 @@ export default {
.pref {
@apply flex justify-between items-center my-2 mx-[15vw] lt-md:mx-[2vw];
}
.pref:nth-child(odd) {
@apply bg-gray-200;
}
.dark .pref:nth-child(odd) {
@apply bg-dark-800;
}
</style>

View file

@ -33,6 +33,8 @@
</template>
<script>
import { isEmail } from "../utils/Misc.js";
export default {
data() {
return {
@ -52,6 +54,8 @@ export default {
methods: {
register() {
if (!this.username || !this.password) return;
if (isEmail(this.username) && !confirm(this.$t("info.register_no_email_note"))) return;
this.fetchJson(this.authApiUrl() + "/register", null, {
method: "POST",
body: JSON.stringify({

View file

@ -18,19 +18,21 @@
</i18n-t>
</div>
<div v-if="results" class="video-grid">
<LoadingIndicatorPage :show-content="results != null && results.items?.length" class="video-grid">
<template v-for="result in results.items" :key="result.url">
<ContentItem :item="result" height="94" width="168" />
</template>
</div>
</LoadingIndicatorPage>
</template>
<script>
import ContentItem from "./ContentItem.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
export default {
components: {
ContentItem,
LoadingIndicatorPage,
},
data() {
return {
@ -72,7 +74,10 @@ export default {
},
async updateResults() {
document.title = this.$route.query.search_query + " - Piped";
this.results = this.fetchResults().then(json => (this.results = json));
this.results = this.fetchResults().then(json => {
this.results = json;
this.updateWatched(this.results.items);
});
},
updateFilter() {
this.$router.replace({

View file

@ -1,19 +1,25 @@
<template>
<ModalComponent>
<h2 v-t="'actions.share'" />
<div class="flex justify-between mt-4">
<label v-t="'actions.with_timecode'" for="withTimeCode" />
<input id="withTimeCode" type="checkbox" v-model="withTimeCode" @change="onChange" />
</div>
<div class="flex justify-between">
<label v-t="'actions.piped_link'" />
<input type="checkbox" v-model="pipedLink" @change="onChange" />
</div>
<div class="flex justify-between mt-2">
<div v-if="this.hasPlaylist" class="flex justify-between">
<label v-t="'actions.with_playlist'" />
<input type="checkbox" v-model="withPlaylist" @change="onChange" />
</div>
<div class="flex justify-between">
<label v-t="'actions.with_timecode'" for="withTimeCode" />
<input id="withTimeCode" type="checkbox" v-model="withTimeCode" @change="onChange" />
</div>
<div v-if="this.withTimeCode" class="flex justify-between mt-2">
<label v-t="'actions.time_code'" />
<input class="input w-12" type="text" v-model="timeStamp" />
</div>
<a :href="generatedLink" target="_blank"><h3 class="mt-4" v-text="generatedLink" /></a>
<a :href="generatedLink" target="_blank">
<h3 class="mt-4" v-text="generatedLink" />
</a>
<div class="flex justify-end mt-4">
<button class="btn" v-t="'actions.follow_link'" @click="followLink()" />
<button class="btn ml-3" v-t="'actions.copy_link'" @click="copyLink()" />
@ -34,6 +40,12 @@ export default {
type: Number,
required: true,
},
playlistId: {
type: String,
},
playlistIndex: {
type: Number,
},
},
components: {
ModalComponent,
@ -42,13 +54,17 @@ export default {
return {
withTimeCode: true,
pipedLink: true,
withPlaylist: true,
timeStamp: null,
hasPlaylist: false,
};
},
mounted() {
this.timeStamp = parseInt(this.currentTime);
this.withTimeCode = this.getPreferenceBoolean("shareWithTimeCode", true);
this.pipedLink = this.getPreferenceBoolean("shareAsPipedLink", true);
this.withPlaylist = this.getPreferenceBoolean("shareWithPlaylist", true);
this.hasPlaylist = this.playlistId != undefined && !isNaN(this.playlistIndex);
},
methods: {
followLink() {
@ -66,8 +82,9 @@ export default {
}
},
onChange() {
this.setPreference("shareWithTimeCode", this.withTimeCode);
this.setPreference("shareAsPipedLink", this.pipedLink);
this.setPreference("shareWithTimeCode", this.withTimeCode, true);
this.setPreference("shareAsPipedLink", this.pipedLink, true);
this.setPreference("shareWithPlaylist", this.withPlaylist, true);
},
},
computed: {
@ -77,6 +94,10 @@ export default {
: "https://youtu.be/" + this.videoId;
var url = new URL(baseUrl);
if (this.withTimeCode && this.timeStamp > 0) url.searchParams.append("t", this.timeStamp);
if (this.hasPlaylist && this.withPlaylist) {
url.searchParams.append("list", this.playlistId);
url.searchParams.append("index", this.playlistIndex);
}
return url.href;
},
},

View file

@ -1,6 +1,6 @@
<template>
<h1 class="font-bold text-center my-4" v-t="'titles.subscriptions'" />
<!-- import / export section -->
<div class="flex justify-between w-full">
<div class="flex">
<button class="btn mx-1">
@ -8,25 +8,31 @@
</button>
<button class="btn" @click="exportHandler" v-t="'actions.export_to_json'" />
</div>
<i18n-t keypath="subscriptions.subscribed_channels_count">{{ subscriptions.length }}</i18n-t>
<!-- subscriptions count, only shown if there are any -->
<i18n-t v-if="subscriptions.length > 0" keypath="subscriptions.subscribed_channels_count">{{
subscriptions.length
}}</i18n-t>
</div>
<br />
<hr />
<div class="grid">
<div class="mb-3" v-for="subscription in subscriptions" :key="subscription.url">
<div class="flex justify-center place-items-center">
<div class="w-full flex justify-between items-center">
<router-link :to="subscription.url" class="flex text-center font-bold text-4xl">
<img :src="subscription.avatar" class="rounded-full h-[fit-content]" width="48" height="48" />
<span class="mx-2" v-text="subscription.name" />
</router-link>
<button
class="btn w-min"
@click="handleButton(subscription)"
v-t="`actions.${subscription.subscribed ? 'unsubscribe' : 'subscribe'}`"
/>
</div>
</div>
<!-- Subscriptions card list -->
<div class="xl:grid xl:grid-cols-5 <md:flex-wrap">
<!-- channel info card -->
<div
class="col m-2 p-1 border rounded-lg border-gray-500"
v-for="subscription in subscriptions"
:key="subscription.url"
>
<router-link :to="subscription.url" class="flex p-2 font-bold text-4x4">
<img :src="subscription.avatar" class="rounded-full h-[fit-content]" width="48" height="48" />
<span class="self-center mx-2" v-text="subscription.name" />
</router-link>
<!-- subscribe / unsubscribe btn -->
<button
class="btn w-full mt-2"
@click="handleButton(subscription)"
v-t="`actions.${subscription.subscribed ? 'unsubscribe' : 'subscribe'}`"
/>
</div>
</div>
<br />

View file

@ -0,0 +1,28 @@
<template>
<div class="toast">
<slot />
<button @click="dismiss" v-t="'actions.dismiss'" />
</div>
</template>
<script>
export default {
methods: {
dismiss() {
this.$emit("dismissed");
},
},
};
</script>
<style>
.toast {
@apply bg-white/80 text-black flex flex-col justify-center fixed top-12 right-12 p-4 min-w-max shadow rounded duration-200 z-9999;
}
.dark .toast {
@apply bg-dark-900/80 text-white;
}
.toast button {
@apply underline;
}
</style>

View file

@ -3,17 +3,19 @@
<hr />
<div class="video-grid">
<LoadingIndicatorPage :show-content="videos.length != 0" class="video-grid">
<VideoItem v-for="video in videos" :key="video.url" :item="video" height="118" width="210" />
</div>
</LoadingIndicatorPage>
</template>
<script>
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import VideoItem from "./VideoItem.vue";
export default {
components: {
VideoItem,
LoadingIndicatorPage,
},
data() {
return {
@ -21,6 +23,7 @@ export default {
};
},
mounted() {
if (this.$route.path == "/" && this.getPreferenceString("homepage", "trending") == "feed") return;
let region = this.getPreferenceString("region", "US");
this.fetchTrending(region).then(videos => {

View file

@ -1,6 +1,7 @@
<template>
<div v-if="showVideo">
<router-link
class="focus:underline hover:underline inline-block w-full"
:to="{
path: '/watch',
query: {
@ -10,13 +11,24 @@
},
}"
>
<img
class="w-full"
:src="item.thumbnail"
:alt="item.title"
:class="{ 'shorts-img': item.isShort }"
loading="lazy"
/>
<div class="w-full">
<img
class="w-full aspect-video object-contain"
:src="item.thumbnail"
:alt="item.title"
:class="{ 'shorts-img': item.isShort, 'opacity-75': item.watched }"
loading="lazy"
/>
<!-- progress bar -->
<div class="relative w-full h-1">
<div
class="absolute bottom-0 left-0 h-1 bg-red-600"
v-if="item.watched && item.duration > 0"
:style="{ width: `clamp(0%, ${(item.currentTime / item.duration) * 100}%, 100%` }"
/>
</div>
</div>
<div class="relative text-sm">
<span
class="thumbnail-overlay thumbnail-right"
@ -39,72 +51,29 @@
<div>
<p
style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical"
class="my-2 overflow-hidden flex link"
class="pt-2 overflow-hidden flex link font-bold"
:title="item.title"
v-text="item.title"
/>
</div>
</router-link>
<div class="float-right m-0 inline-block children:px-1">
<router-link
:to="{
path: '/watch',
query: {
v: item.url.substr(-11),
...(playlistId && { list: playlistId }),
...(index >= 0 && { index: index + 1 }),
listen: '1',
},
}"
:aria-label="'Listen to ' + item.title"
:title="'Listen to ' + item.title"
>
<font-awesome-icon icon="headphones" />
</router-link>
<button v-if="authenticated" :title="$t('actions.add_to_playlist')" @click="showModal = !showModal">
<font-awesome-icon icon="circle-plus" />
</button>
<button
v-if="admin"
:title="$t('actions.remove_from_playlist')"
ref="removeButton"
@click="removeVideo(item.url.substr(-11))"
>
<font-awesome-icon icon="circle-minus" />
</button>
<button
v-if="
isFeed &&
(this.getPreferenceBoolean('watchHistory', false) ||
this.getPreferenceBoolean('hideWatched', false))
"
@click="toggleWatched(item.url.substr(-11))"
ref="watchButton"
>
<font-awesome-icon icon="eye-slash" :title="$t('actions.mark_as_unwatched')" v-if="item.watched" />
<font-awesome-icon icon="eye" :title="$t('actions.mark_as_watched')" v-else />
</button>
<PlaylistAddModal v-if="showModal" :video-id="item.url.substr(-11)" @close="showModal = !showModal" />
</div>
<div class="flex">
<router-link :to="item.uploaderUrl">
<img
v-if="item.uploaderAvatar"
:src="item.uploaderAvatar"
loading="lazy"
:alt="item.uploaderName"
class="rounded-full mr-0.5 mt-0.5 w-32px h-32px"
width="68"
height="68"
/>
</router-link>
<div class="w-[calc(100%-32px-1rem)]">
<div class="px-2 flex-1">
<router-link
v-if="item.uploaderUrl && item.uploaderName && !hideChannel"
class="link-secondary overflow-hidden block"
class="link-secondary overflow-hidden block text-sm"
:to="item.uploaderUrl"
:title="item.uploaderName"
>
@ -112,14 +81,56 @@
<font-awesome-icon class="ml-1.5" v-if="item.uploaderVerified" icon="check" />
</router-link>
<strong v-if="item.views >= 0 || item.uploadedDate" class="text-sm">
<div v-if="item.views >= 0 || item.uploadedDate" class="text-xs font-normal text-gray-300 mt-1">
<span v-if="item.views >= 0">
<font-awesome-icon icon="eye" />
<span class="pl-0.5" v-text="`${numberFormat(item.views)} •`" />
<span class="pl-1" v-text="`${numberFormat(item.views)} •`" />
</span>
<span v-if="item.uploaded > 0" class="pl-0.5" v-text="timeAgo(item.uploaded)" />
<span v-else-if="item.uploadedDate" class="pl-0.5" v-text="item.uploadedDate" />
</strong>
</div>
</div>
<div class="flex items-center gap-2.5">
<router-link
:to="{
path: '/watch',
query: {
v: item.url.substr(-11),
...(playlistId && { list: playlistId }),
...(index >= 0 && { index: index + 1 }),
listen: '1',
},
}"
:aria-label="'Listen to ' + item.title"
:title="'Listen to ' + item.title"
>
<font-awesome-icon icon="headphones" />
</router-link>
<button v-if="authenticated" :title="$t('actions.add_to_playlist')" @click="showModal = !showModal">
<font-awesome-icon icon="circle-plus" />
</button>
<button
v-if="admin"
:title="$t('actions.remove_from_playlist')"
ref="removeButton"
@click="removeVideo(item.url.substr(-11))"
>
<font-awesome-icon icon="circle-minus" />
</button>
<button
v-if="
isFeed &&
(this.getPreferenceBoolean('watchHistory', false) ||
this.getPreferenceBoolean('hideWatched', false))
"
@click="toggleWatched(item.url.substr(-11))"
ref="watchButton"
>
<font-awesome-icon icon="eye-slash" v-if="item.watched" :title="$t('actions.mark_as_unwatched')" />
<font-awesome-icon icon="eye" v-else :title="$t('actions.mark_as_watched')" />
</button>
<PlaylistAddModal v-if="showModal" :video-id="item.url.substr(-11)" @close="showModal = !showModal" />
</div>
</div>
</div>
@ -127,7 +138,7 @@
<style>
.shorts-img {
@apply max-h-[17.5vh] w-full object-contain;
@apply w-full object-contain;
}
</style>

View file

@ -6,11 +6,23 @@
:class="{ 'player-container': !isEmbed }"
>
<video ref="videoEl" class="w-full" data-shaka-player :autoplay="shouldAutoPlay" :loop="selectedAutoLoop" />
<canvas id="preview" />
<button
v-if="inSegment"
class="skip-segment-button"
type="button"
:aria-label="$t('actions.skip_segment')"
aria-pressed="false"
@click="onClickSkipSegment"
>
<span v-t="'actions.skip_segment'" />
<i class="material-icons-round">skip_next</i>
</button>
</div>
</template>
<script>
import("shaka-player/dist/controls.css");
import "shaka-player/dist/controls.css";
const shaka = import("shaka-player/dist/shaka-player.ui.js");
if (!window.muxjs) {
import("mux.js").then(muxjs => {
@ -27,14 +39,6 @@ export default {
return {};
},
},
playlist: {
type: Object,
default: null,
},
index: {
type: Number,
default: -1,
},
sponsors: {
type: Object,
default: () => {
@ -51,6 +55,8 @@ export default {
lastUpdate: new Date().getTime(),
initialSeekComplete: false,
destroying: false,
inSegment: false,
isHoveringTimebar: false,
};
},
computed: {
@ -88,7 +94,7 @@ export default {
this.hotkeysPromise.then(() => {
var self = this;
this.$hotkeys(
"f,m,j,k,l,c,space,up,down,left,right,0,1,2,3,4,5,6,7,8,9,shift+n,shift+,,shift+.",
"f,m,j,k,l,c,space,up,down,left,right,0,1,2,3,4,5,6,7,8,9,shift+n,shift+,,shift+.,alt+p,return,.,,",
function (e, handler) {
const videoEl = self.$refs.videoEl;
switch (handler.key) {
@ -184,6 +190,22 @@ export default {
case "shift+.":
self.$player.trickPlay(Math.min(videoEl.playbackRate + 0.25, 2));
break;
case "alt+p":
document.pictureInPictureElement
? document.exitPictureInPicture()
: videoEl.requestPictureInPicture();
break;
case "return":
self.skipSegment(videoEl);
break;
case ".":
videoEl.currentTime += 0.04;
e.preventDefault();
break;
case ",":
videoEl.currentTime -= 0.04;
e.preventDefault();
break;
}
},
);
@ -199,6 +221,8 @@ export default {
},
methods: {
async loadVideo() {
this.updateSponsors();
const component = this;
const videoEl = this.$refs.videoEl;
@ -226,7 +250,7 @@ export default {
}
videoEl.currentTime = start;
this.initialSeekComplete = true;
} else if (window.db) {
} else if (window.db && this.getPreferenceBoolean("watchHistory", false)) {
var tx = window.db.transaction("watch_history", "readonly");
var store = tx.objectStore("watch_history");
var request = store.get(this.video.id);
@ -256,9 +280,7 @@ export default {
const MseSupport = window.MediaSource !== undefined;
const lbry = this.getPreferenceBoolean("disableLBRY", false)
? null
: this.video.videoStreams.filter(stream => stream.quality === "LBRY")[0];
const lbry = null;
var uri;
var mime;
@ -268,12 +290,17 @@ export default {
mime = "application/x-mpegURL";
} else if (this.video.audioStreams.length > 0 && !lbry && MseSupport) {
if (!this.video.dash) {
const dash = (
await import("@/utils/DashUtils.js").then(mod => mod.default)
).generate_dash_file_from_formats(streams, this.video.duration);
const dash = (await import("../utils/DashUtils.js")).generate_dash_file_from_formats(
streams,
this.video.duration,
);
uri = "data:application/dash+xml;charset=utf-8;base64," + btoa(dash);
} else uri = this.video.dash;
} else {
const url = new URL(this.video.dash);
url.searchParams.set("rewrite", false);
uri = url.toString();
}
mime = "application/dash+xml";
} else if (lbry) {
uri = lbry.url;
@ -302,7 +329,7 @@ export default {
uri = this.video.hls;
mime = "application/x-mpegURL";
} else {
uri = this.video.videoStreams.filter(stream => stream.codec == null).slice(-1)[0].url;
uri = this.video.videoStreams.findLast(stream => stream.codec == null).url;
mime = "video/mp4";
}
@ -352,48 +379,54 @@ export default {
else this.setPlayerAttrs(this.$player, videoEl, uri, mime, this.$shaka);
if (noPrevPlayer) {
videoEl.addEventListener("loadeddata", () => {
if (document.pictureInPictureElement) videoEl.requestPictureInPicture();
});
videoEl.addEventListener("timeupdate", () => {
const time = videoEl.currentTime;
this.$emit("timeupdate", time);
this.updateProgressDatabase(time);
if (this.sponsors && this.sponsors.segments) {
this.sponsors.segments.map(segment => {
if (!segment.skipped || this.selectedAutoLoop) {
const end = segment.segment[1];
if (time >= segment.segment[0] && time < end) {
console.log("Skipped segment at " + time);
videoEl.currentTime = end;
segment.skipped = true;
return;
}
}
});
const segment = this.findCurrentSegment(time);
this.inSegment = !!segment;
if (segment?.autoskip && (!segment.skipped || this.selectedAutoLoop)) {
this.skipSegment(videoEl, segment);
}
}
});
videoEl.addEventListener("volumechange", () => {
this.setPreference("volume", videoEl.volume);
this.setPreference("volume", videoEl.volume, true);
});
videoEl.addEventListener("ratechange", e => {
const rate = videoEl.playbackRate;
if (rate > 0 && !isNaN(videoEl.duration) && !isNaN(videoEl.duration - e.timeStamp / 1000))
this.setPreference("rate", rate);
this.setPreference("rate", rate, true);
});
videoEl.addEventListener("ended", () => {
if (
!this.selectedAutoLoop &&
this.selectedAutoPlay &&
(this.playlist?.relatedStreams?.length > 0 || this.video.relatedStreams.length > 0)
) {
this.navigateNext();
}
this.$emit("ended");
});
}
//TODO: Add sponsors on seekbar: https://github.com/ajayyy/SponsorBlock/blob/e39de9fd852adb9196e0358ed827ad38d9933e29/src/js-components/previewBar.ts#L12
},
findCurrentSegment(time) {
return this.sponsors?.segments?.find(s => time >= s.segment[0] && time < s.segment[1]);
},
onClickSkipSegment() {
const videoEl = this.$refs.videoEl;
this.skipSegment(videoEl);
},
skipSegment(videoEl, segment) {
const time = videoEl.currentTime;
if (!segment) segment = this.findCurrentSegment(time);
if (!segment) return;
console.log("Skipped segment at " + time);
videoEl.currentTime = segment.segment[1];
segment.skipped = true;
},
setPlayerAttrs(localPlayer, videoEl, uri, mime, shaka) {
const url = "/watch?v=" + this.video.id;
@ -440,7 +473,14 @@ export default {
this.$ui = new shaka.ui.Overlay(localPlayer, this.$refs.container, videoEl);
const overflowMenuButtons = ["quality", "captions", "picture_in_picture", "playback_rate", "airplay"];
const overflowMenuButtons = [
"quality",
"language",
"captions",
"picture_in_picture",
"playback_rate",
"airplay",
];
if (this.isEmbed) {
overflowMenuButtons.push("open_new_tab");
@ -460,8 +500,13 @@ export default {
this.updateMarkers();
const event = new Event("playerInit");
window.dispatchEvent(event);
const player = this.$ui.getControls().getPlayer();
this.setupSeekbarPreview();
this.$player = player;
const disableVideo = this.getPreferenceBoolean("listen", false) && !this.video.livestream;
@ -480,22 +525,40 @@ export default {
if (qualityConds) this.$player.configure("abr.enabled", false);
player.load(uri, 0, mime).then(() => {
const isSafari = window.navigator?.vendor?.includes("Apple");
if (!isSafari) {
// Set the audio language
const prefLang = this.getPreferenceString("hl", "en").substr(0, 2);
var lang = "en";
for (var l in player.getAudioLanguages()) {
if (l == prefLang) {
lang = l;
return;
}
}
player.selectAudioLanguage(lang);
}
if (qualityConds) {
var leastDiff = Number.MAX_VALUE;
var bestStream = null;
var bestAudio = 0;
const tracks = player
.getVariantTracks()
.filter(track => track.language == lang || track.language == "und");
// Choose the best audio stream
if (quality >= 480)
player.getVariantTracks().forEach(track => {
tracks.forEach(track => {
const audioBandwidth = track.audioBandwidth;
if (audioBandwidth > bestAudio) bestAudio = audioBandwidth;
});
// Find best matching stream based on resolution and bitrate
player
.getVariantTracks()
tracks
.sort((a, b) => a.bandwidth - b.bandwidth)
.forEach(stream => {
if (stream.audioBandwidth < bestAudio) return;
@ -514,7 +577,7 @@ export default {
player.addTextTrackAsync(
subtitle.url,
subtitle.code,
"SUBTITLE",
"subtitles",
subtitle.mimeType,
null,
subtitle.name,
@ -525,6 +588,10 @@ export default {
videoEl.playbackRate = rate;
videoEl.defaultPlaybackRate = rate;
});
// expand the player to fullscreen when the fullscreen query equals true
if (this.$route.query.fullscreen === "true" && !this.$ui.getControls().isFullScreenEnabled())
this.$ui.getControls().toggleFullScreen();
},
async updateProgressDatabase(time) {
// debounce
@ -549,29 +616,6 @@ export default {
this.$refs.videoEl.currentTime = time;
}
},
navigateNext() {
const params = this.$route.query;
let url = this.playlist?.relatedStreams?.[this.index]?.url ?? this.video.relatedStreams[0].url;
const searchParams = new URLSearchParams();
for (var param in params)
switch (param) {
case "v":
case "t":
break;
case "index":
if (this.index < this.playlist.relatedStreams.length) searchParams.set("index", this.index + 1);
break;
case "list":
if (this.index < this.playlist.relatedStreams.length) searchParams.set("list", params.list);
break;
default:
searchParams.set(param, params[param]);
break;
}
const paramStr = searchParams.toString();
if (paramStr.length > 0) url += "&" + paramStr;
this.$router.push(url);
},
updateMarkers() {
const markers = this.$refs.container.querySelector(".shaka-ad-markers");
const array = ["to right"];
@ -624,29 +668,120 @@ export default {
if (markers) markers.style.background = `linear-gradient(${array.join(",")})`;
},
updateSponsors() {
const skipOptions = this.getPreferenceJSON("skipOptions", {});
this.sponsors?.segments?.forEach(segment => {
const option = skipOptions[segment.category];
segment.autoskip = option === undefined || option === "auto";
});
if (this.getPreferenceBoolean("showMarkers", true)) {
this.shakaPromise.then(() => {
this.updateMarkers();
});
}
},
setupSeekbarPreview() {
if (!this.video.previewFrames) return;
let seekBar = document.querySelector(".shaka-seek-bar");
// load the thumbnail preview when the user moves over the seekbar
seekBar.addEventListener("mousemove", e => {
this.isHoveringTimebar = true;
const position = (e.offsetX / e.target.offsetWidth) * this.video.duration;
this.showSeekbarPreview(position * 1000);
});
// hide the preview when the user stops hovering the seekbar
seekBar.addEventListener("mouseout", () => {
this.isHoveringTimebar = false;
let canvas = document.querySelector("#preview");
canvas.style.display = "none";
});
},
async showSeekbarPreview(position) {
const frame = this.getFrame(position);
const originalImage = await this.loadImage(frame.url);
if (!this.isHoveringTimebar) return;
const seekBar = document.querySelector(".shaka-seek-bar");
const canvas = document.querySelector("#preview");
const ctx = canvas.getContext("2d");
// get the new sizes for the image to be drawn into the canvas
const originalWidth = originalImage.naturalWidth;
const originalHeight = originalImage.naturalHeight;
// image can have less frames than server told us so calculate them ourselves
const imageFramesPerPageX = originalImage.naturalWidth / frame.frameWidth;
const imageFramesPerPageY = originalImage.naturalHeight / frame.frameHeight;
const offsetX = originalWidth * (frame.positionX / imageFramesPerPageX);
const offsetY = originalHeight * (frame.positionY / imageFramesPerPageY);
canvas.width = frame.frameWidth > 100 ? frame.frameWidth : frame.frameWidth * 2;
canvas.height = frame.frameWidth > 100 ? frame.frameHeight : frame.frameHeight * 2;
// draw the thumbnail preview into the canvas by cropping only the relevant part
ctx.drawImage(
originalImage,
offsetX,
offsetY,
frame.frameWidth,
frame.frameHeight,
0,
0,
canvas.width,
canvas.height,
);
// calculate the thumbnail preview offset and display it
const seekbarPadding = 2; // percentage of seekbar padding
const centerOffset = position / this.video.duration / 10;
const left = centerOffset - ((0.5 * canvas.width) / seekBar.clientWidth) * 100;
const maxLeft = ((seekBar.clientWidth - canvas.clientWidth) / seekBar.clientWidth) * 100 - seekbarPadding;
canvas.style.left = `max(${seekbarPadding}%, min(${left}%, ${maxLeft}%))`;
canvas.style.display = "block";
},
// ineffective algorithm to find the thumbnail corresponding to the currently hovered position in the video
getFrame(position) {
let startPosition = 0;
const framePage = this.video.previewFrames.at(-1);
for (let i = 0; i < framePage.urls.length; i++) {
for (let positionY = 0; positionY < framePage.framesPerPageY; positionY++) {
for (let positionX = 0; positionX < framePage.framesPerPageX; positionX++) {
const endPosition = startPosition + framePage.durationPerFrame;
if (position >= startPosition && position <= endPosition) {
return {
url: framePage.urls[i],
positionX: positionX,
positionY: positionY,
frameWidth: framePage.frameWidth,
frameHeight: framePage.frameHeight,
};
}
startPosition = endPosition;
}
}
}
return null;
},
// creates a new image from an URL
loadImage(url) {
return new Promise(r => {
const i = new Image();
i.onload = () => r(i);
i.src = url;
});
},
destroy(hotkeys) {
if (this.$ui) {
if (this.$ui && !document.pictureInPictureElement) {
this.$ui.destroy();
this.$ui = undefined;
this.$player = undefined;
}
if (this.$player) {
this.$player.destroy();
this.$player = undefined;
if (!document.pictureInPictureElement) this.$player = undefined;
}
if (hotkeys) this.$hotkeys?.unbind();
this.$refs.container?.querySelectorAll("div").forEach(node => node.remove());
},
},
watch: {
sponsors() {
if (this.getPreferenceBoolean("showMarkers", true)) {
this.shakaPromise.then(() => {
this.updateMarkers();
});
}
},
},
};
</script>
@ -681,4 +816,43 @@ export default {
background-color: rgba(0, 0, 0, 0.6) !important;
padding: 0.09em 0;
}
.skip-segment-button {
/* position button above player overlay */
z-index: 1000;
position: absolute;
transform: translate(0, -50%);
top: 50%;
right: 0;
background-color: rgb(0 0 0 / 0.5);
border: 2px rgba(255, 255, 255, 0.75) solid;
border-right: 0;
border-radius: 0.75em;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
padding: 0.5em;
/* center text vertically */
display: flex;
align-items: center;
justify-content: center;
color: #fff;
line-height: 1.5em;
}
.skip-segment-button .material-icons-round {
font-size: 1.6em !important;
line-height: inherit !important;
}
#preview {
position: absolute;
z-index: 2000;
bottom: 0;
margin-bottom: 4.5%;
border-radius: 0.3rem;
}
</style>

View file

@ -4,29 +4,33 @@
ref="videoPlayer"
:video="video"
:sponsors="sponsors"
:playlist="playlist"
:index="index"
:selected-auto-play="false"
:selected-auto-loop="selectedAutoLoop"
:is-embed="isEmbed"
/>
</div>
<div v-if="video && !isEmbed" class="w-full">
<LoadingIndicatorPage :show-content="video && !isEmbed" class="w-full">
<ErrorHandler v-if="video && video.error" :message="video.message" :error="video.error" />
<Transition>
<ToastComponent v-if="shouldShowToast" @dismissed="dismiss">
<i18n-t keypath="info.next_video_countdown">{{ counter }}</i18n-t>
</ToastComponent>
</Transition>
<div v-show="!video.error">
<div :class="isMobile ? 'flex-col' : 'flex'">
<VideoPlayer
ref="videoPlayer"
:video="video"
:sponsors="sponsors"
:playlist="playlist"
:index="index"
:selected-auto-play="selectedAutoPlay"
:selected-auto-loop="selectedAutoLoop"
@timeupdate="onTimeUpdate"
/>
<keep-alive>
<VideoPlayer
ref="videoPlayer"
:video="video"
:sponsors="sponsors"
:selected-auto-play="selectedAutoPlay"
:selected-auto-loop="selectedAutoLoop"
@timeupdate="onTimeUpdate"
@ended="onVideoEnded"
/>
</keep-alive>
<ChaptersBar
:mobileLayout="isMobile"
v-if="video?.chapters?.length > 0 && showChapters"
@ -92,6 +96,8 @@
v-if="showShareModal"
:video-id="getVideoId()"
:current-time="currentTime"
:playlist-id="playlistId"
:playlist-index="index"
@close="showShareModal = !showShareModal"
/>
<div class="flex">
@ -146,10 +152,13 @@
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-show="showDesc" class="break-words" v-html="purifyHTML(video.description)" />
<div
v-if="showDesc && sponsors && sponsors.segments"
v-text="`${$t('video.sponsor_segments')}: ${sponsors.segments.length}`"
/>
<template v-if="showDesc">
<div
v-if="sponsors && sponsors.segments"
v-text="`${$t('video.sponsor_segments')}: ${sponsors.segments.length}`"
/>
<div v-if="video.category" v-text="`${$t('video.category')}: ${video.category}`" />
</template>
</div>
<hr />
@ -212,7 +221,7 @@
<hr class="sm:hidden" />
</div>
</div>
</div>
</LoadingIndicatorPage>
</template>
<script>
@ -225,6 +234,8 @@ import PlaylistAddModal from "./PlaylistAddModal.vue";
import ShareModal from "./ShareModal.vue";
import PlaylistVideos from "./PlaylistVideos.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import ToastComponent from "./ToastComponent.vue";
export default {
name: "App",
@ -238,13 +249,13 @@ export default {
ShareModal,
PlaylistVideos,
WatchOnYouTubeButton,
LoadingIndicatorPage,
ToastComponent,
},
data() {
const smallViewQuery = window.matchMedia("(max-width: 640px)");
return {
video: {
title: "Loading...",
},
video: null,
playlistId: null,
playlist: null,
index: null,
@ -265,6 +276,9 @@ export default {
showShareModal: false,
isMobile: true,
currentTime: 0,
shouldShowToast: false,
timeoutCounter: null,
counter: 0,
};
},
computed: {
@ -286,6 +300,9 @@ export default {
year: "numeric",
});
},
defaultCounter(_this) {
return _this.getPreferenceNumber("autoPlayNextCountdown", 5);
},
},
mounted() {
// check screen size
@ -304,7 +321,7 @@ export default {
(async () => {
const videoId = this.getVideoId();
const instance = this;
if (window.db && !this.video.error) {
if (window.db && this.getPreferenceBoolean("watchHistory", false) && !this.video.error) {
var tx = window.db.transaction("watch_history", "readwrite");
var store = tx.objectStore("watch_history");
var request = store.get(videoId);
@ -334,6 +351,7 @@ export default {
this.getPlaylistData();
this.getSponsors();
if (!this.isEmbed && this.showComments) this.getComments();
window.addEventListener("click", this.handleClick);
window.addEventListener("resize", () => {
this.smallView = this.smallViewQuery.matches;
});
@ -345,7 +363,7 @@ export default {
this.showDesc = !this.getPreferenceBoolean("minimizeDescription", false);
this.showRecs = !this.getPreferenceBoolean("minimizeRecommendations", false);
this.showChapters = !this.getPreferenceBoolean("minimizeChapters", false);
if (this.video.duration) {
if (this.video?.duration) {
document.title = this.video.title + " - Piped";
this.$refs.videoPlayer.loadVideo();
}
@ -354,24 +372,40 @@ export default {
deactivated() {
this.active = false;
window.removeEventListener("scroll", this.handleScroll);
this.dismiss();
},
unmounted() {
window.removeEventListener("scroll", this.handleScroll);
window.removeEventListener("click", this.handleClick);
this.dismiss();
},
methods: {
fetchVideo() {
return this.fetchJson(this.apiUrl() + "/streams/" + this.getVideoId());
},
async fetchSponsors() {
return await this.fetchJson(this.apiUrl() + "/sponsors/" + this.getVideoId(), {
category:
'["' +
this.getPreferenceString("selectedSkip", "sponsor,interaction,selfpromo,music_offtopic").replaceAll(
",",
'","',
) +
'"]',
var selectedSkip = this.getPreferenceString(
"selectedSkip",
"sponsor,interaction,selfpromo,music_offtopic",
).split(",");
const skipOptions = this.getPreferenceJSON("skipOptions");
if (skipOptions !== undefined) {
selectedSkip = Object.keys(skipOptions).filter(
k => skipOptions[k] !== undefined && skipOptions[k] !== "no",
);
}
const sponsors = await this.fetchJson(this.apiUrl() + "/sponsors/" + this.getVideoId(), {
category: JSON.stringify(selectedSkip),
});
const minSegmentLength = Math.max(this.getPreferenceNumber("minSegmentLength", 0), 0);
sponsors.segments = sponsors.segments?.filter(segment => {
const length = segment.segment[1] - segment.segment[0];
return length >= minSegmentLength;
});
return sponsors;
},
toggleComments() {
this.showComments = !this.showComments;
@ -383,7 +417,7 @@ export default {
return this.fetchJson(this.apiUrl() + "/comments/" + this.getVideoId());
},
onChange() {
this.setPreference("autoplay", this.selectedAutoPlay);
this.setPreference("autoplay", this.selectedAutoPlay, true);
},
async getVideoData() {
await this.fetchVideo()
@ -404,13 +438,8 @@ export default {
elem.outerHTML = elem.getAttribute("href");
});
xmlDoc.querySelectorAll("br").forEach(elem => (elem.outerHTML = "\n"));
this.video.description = this.urlify(xmlDoc.querySelector("body").innerHTML)
.replaceAll(/(?:http(?:s)?:\/\/)?(?:www\.)?youtube\.com(\/[/a-zA-Z0-9_?=&-]*)/gm, "$1")
.replaceAll(
/(?:http(?:s)?:\/\/)?(?:www\.)?youtu\.be\/(?:watch\?v=)?([/a-zA-Z0-9_?=&-]*)/gm,
"/watch?v=$1",
)
.replaceAll("\n", "<br>");
this.video.description = this.rewriteDescription(xmlDoc.querySelector("body").innerHTML);
this.updateWatched(this.video.relatedStreams);
}
});
},
@ -449,7 +478,10 @@ export default {
this.fetchSponsors().then(data => (this.sponsors = data));
},
async getComments() {
this.fetchComments().then(data => (this.comments = data));
this.fetchComments().then(data => {
this.rewriteComments(data.comments);
this.comments = data;
});
},
async fetchSubscribedStatus() {
if (!this.channelId) return;
@ -472,6 +504,23 @@ export default {
this.subscribed = json.subscribed;
});
},
rewriteComments(data) {
data.forEach(comment => {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(comment.commentText, "text/html");
xmlDoc.querySelectorAll("a").forEach(elem => {
if (!elem.innerText.match(/(?:[\d]{1,2}:)?(?:[\d]{1,2}):(?:[\d]{1,2})/))
elem.outerHTML = elem.getAttribute("href");
});
comment.commentText = xmlDoc
.querySelector("body")
.innerHTML.replaceAll(/(?:http(?:s)?:\/\/)?(?:www\.)?youtube\.com(\/[/a-zA-Z0-9_?=&-]*)/gm, "$1")
.replaceAll(
/(?:http(?:s)?:\/\/)?(?:www\.)?youtu\.be\/(?:watch\?v=)?([/a-zA-Z0-9_?=&-]*)/gm,
"/watch?v=$1",
);
});
},
subscribeHandler() {
if (this.authenticated) {
this.fetchJson(this.authApiUrl() + (this.subscribed ? "/unsubscribe" : "/subscribe"), null, {
@ -485,10 +534,23 @@ export default {
},
});
} else {
this.handleLocalSubscriptions(this.channelId);
if (!this.handleLocalSubscriptions(this.channelId)) return;
}
this.subscribed = !this.subscribed;
},
handleClick(event) {
if (!event || !event.target) return;
var target = event.target;
if (
!target.nodeName == "A" ||
!target.getAttribute("href") ||
!target.innerText.match(/(?:[\d]{1,2}:)?(?:[\d]{1,2}):(?:[\d]{1,2})/)
)
return;
const time = parseInt(target.getAttribute("href").match(/(?<=t=)\d+/)[0]);
this.navigate(time);
event.preventDefault();
},
handleScroll() {
if (this.loading || !this.comments || !this.comments.nextpage) return;
if (window.innerHeight + window.scrollY >= this.$refs.comments?.offsetHeight - window.innerHeight) {
@ -498,7 +560,8 @@ export default {
}).then(json => {
this.comments.nextpage = json.nextpage;
this.loading = false;
json.comments.map(comment => this.comments.comments.push(comment));
this.rewriteComments(json.comments);
this.comments.comments = this.comments.comments.concat(json.comments);
});
}
},
@ -511,6 +574,68 @@ export default {
onTimeUpdate(time) {
this.currentTime = time;
},
onVideoEnded() {
if (
!this.selectedAutoLoop &&
this.selectedAutoPlay &&
(this.playlist?.relatedStreams?.length > 0 || this.video.relatedStreams.length > 0)
) {
this.showToast();
}
},
showToast() {
this.counter = this.defaultCounter;
if (this.counter < 1) {
this.navigateNext();
return;
}
if (this.timeoutCounter) clearInterval(this.timeoutCounter);
this.timeoutCounter = setInterval(() => {
this.counter--;
if (this.counter === 0) {
this.dismiss();
this.navigateNext();
}
}, 1000);
this.shouldShowToast = true;
},
dismiss() {
clearInterval(this.timeoutCounter);
this.shouldShowToast = false;
},
navigateNext() {
const params = this.$route.query;
let url = this.playlist?.relatedStreams?.[this.index]?.url ?? this.video.relatedStreams[0].url;
const searchParams = new URLSearchParams();
for (var param in params)
switch (param) {
case "v":
case "t":
break;
case "index":
if (this.index < this.playlist.relatedStreams.length) searchParams.set("index", this.index + 1);
break;
case "list":
if (this.index < this.playlist.relatedStreams.length) searchParams.set("list", params.list);
break;
default:
searchParams.set(param, params[param]);
break;
}
// save the fullscreen state
searchParams.set("fullscreen", this.$refs.videoPlayer.$ui.getControls().isFullScreenEnabled());
const paramStr = searchParams.toString();
if (paramStr.length > 0) url += "&" + paramStr;
this.$router.push(url);
},
},
};
</script>
<style>
.v-enter-from,
.v-leave-to {
opacity: 0;
transform: translateX(100%) scale(0.5);
}
</style>

1
src/locales/ang.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -4,15 +4,16 @@
"login": "تسجيل الدخول",
"register": "إنشاء حساب",
"preferences": "الإعدادات",
"history": "تاريخ التصفح",
"history": "سجل المشاهدة",
"subscriptions": "الاشتراكات",
"playlists": "قوائم التشغيل",
"feed": "التغذية",
"feed": "محتوى الاشتراكات",
"account": "الحساب",
"instance": "الخادم",
"player": "المشغل",
"livestreams": "البث المباشر",
"channels": "القنوات"
"channels": "القنوات",
"bookmarks": "الاشارات المرجعيه"
},
"player": {
"watch_on": "شاهد عبر"
@ -41,7 +42,7 @@
"enable_sponsorblock": "تفعيل مانع الإعلانات",
"auto": "تلقائي",
"dark": "داكن",
"search": "بحث",
"search": "بحث (Ctrl+K)",
"autoplay_video": "تشغيل تلقائي",
"audio_only": "صوت فقط",
"default_quality": "الجودة الأساسية",
@ -117,7 +118,19 @@
"reply_count": "{count} الردود",
"minimize_comments_default": "تصغير التعليقات بشكل افتراضي",
"minimize_comments": "تصغير التعليقات",
"show_watch_on_youtube": "عرض زر مشاهدة على يوتيوب"
"show_watch_on_youtube": "عرض زر مشاهدة على يوتيوب",
"minimize_chapters_default": "تصغير الفصول بشكل افتراضي",
"no_valid_playlists": "لا يحتوي الملف على قوائم تشغيل صالحة!",
"with_playlist": "المشاركة مع قائمة التشغيل",
"bookmark_playlist": "الاشاره المرجعيه",
"playlist_bookmarked": "تم وضعها في الاشارات المرجعية",
"skip_button_only": "إظهار زر التخطي",
"skip_automatically": "تلقائيا",
"min_segment_length": "الحد الأدنى لطول الفصل (بالثواني)",
"skip_segment": "تخطي الجزء",
"show_less": "عرض أقل",
"autoplay_next_countdown": "العد التنازلي الافتراضي حتى الفيديو التالي ( ثانية )",
"dismiss": "تجاهل"
},
"video": {
"sponsor_segments": "المقاطع الإعلانية",
@ -127,7 +140,9 @@
"views": "{views} عدد المشاهدات",
"shorts": "فديوهات قصيرة",
"videos": "الفيديوات",
"live": "{0} مباشر"
"live": "{0} مباشر",
"all": "الكل",
"category": "الفئة"
},
"search": {
"channels": "يوتيوب: القنوات",
@ -169,6 +184,9 @@
"preferences_note": "ملاحظة: يتم حفظ التفضيلات في وحدة التخزين المحلية في متصفحك. سيؤدي حذف بيانات المتصفح إلى إعادة تعيينها.",
"page_not_found": "لم يتم العثور على الصفحة",
"copied": "نسخ!",
"cannot_copy": "لا يمكن نسخه!"
"cannot_copy": "لا يمكن نسخه!",
"local_storage": "يتطلب هذا الإجراء التخزين المحلي، هل يتم تمكين ملفات تعريف الارتباط؟",
"register_no_email_note": "لا ينصح باستخدام البريد الإلكتروني كاسم مستخدم. المضي قدما على أي حال؟",
"next_video_countdown": "تشغيل الفيديو التالي بعد { 0 } ق"
}
}

View file

@ -1,131 +1,147 @@
{
"titles": {
"trending": "Trenddə olan",
"login": "Daxil olun",
"register": "Qeydiyyatdan keçin",
"login": "Daxil ol",
"register": "Qeydiyyatdan keç",
"feed": "Axın",
"preferences": "Seçimlər",
"preferences": "Üstünlüklər",
"history": "Tarixçə",
"subscriptions": "Abunəliklər",
"playlists": "Pleylistlər",
"playlists": "Oynatma Siyahıları",
"account": "Hesab",
"instance": "Nümunə",
"player": "Oynadıcı"
"player": "Oynadıcı",
"livestreams": "Canlı Yayımlar",
"channels": "Kanallar",
"bookmarks": "Əlfəcinlər"
},
"player": {
"watch_on": "{0} saytında baxın"
"watch_on": "{0} saytında bax"
},
"actions": {
"subscribe": "Abunə Olun - {count}",
"unsubscribe": "Abunəlikdən Çıxın- {count}",
"subscribe": "Abunə Ol - {count}",
"unsubscribe": "Abunəlikdən Çıx - {count}",
"view_subscriptions": "Abunəliklərə Baxın",
"sort_by": "Sıralama qaydası:",
"sort_by": "Çeşidlə:",
"most_recent": "Ən Yeni",
"least_recent": "Ən Köhnə",
"channel_name_asc": "Kanal Adı (A-Z)",
"channel_name_desc": "Kanal Adı (Z-A)",
"back": "Geri",
"uses_api_from": "API-dən istifadə edir ",
"enable_sponsorblock": "SponsorBlok'u Aktivləşdirin",
"skip_sponsors": "Sponsorları Ötürün",
"skip_intro": "Fasilə/Giriş Animasiyasını Ötür",
"skip_outro": "Bitiş Kartları/Kanal Nişanı Seqmentlərini Ötür",
"skip_preview": "Önbaxışı/Anonsu Ötürün",
"skip_interaction": "İnteraksiya Xatırlatıcısını Ötürün(Abunə Olun)",
"skip_self_promo": "Ödənişsiz/Özünü Reklamı Ötürün",
"skip_non_music": "Musiqisizliyi Ötür: Musiqi Olmayan Bölmə",
"enable_sponsorblock": "SponsorBlok'u Aktivləşdir",
"skip_sponsors": "Sponsorları Ötür",
"skip_intro": "Fasilə/Giriş Animasiyasın Ötür",
"skip_outro": "Son Kartları/Kreditləri Ötür",
"skip_preview": "Önbaxışı/Anonsu Ötür",
"skip_interaction": "Əlaqələndirmə Xatırladıcısın Ötür(Abunə Ol)",
"skip_self_promo": "Ödənilməmiş/Özünü Reklamı Ötür",
"skip_non_music": "Musiqini Ötür: Musiqisiz Bölmə",
"skip_highlight": "Anonsu Ötür",
"skip_filler_tangent": "Doldurucu Səhnələri Ötür",
"theme": "Mövzu",
"theme": "Tema",
"auto": "Avtomatik",
"dark": "Qaranlıq",
"light": "İşıqlı",
"autoplay_video": "Videonu Avto-oynat",
"audio_only": "Yalnız Səs",
"default_quality": "Defolt Keyfiyyət",
"buffering_goal": "Tamponlama Məqsədi (saniyələrlə)",
"export_to_json": "JSON-a İxrac Edin",
"import_from_json": "JSON/CSV-dan İdxal Edin",
"default_quality": "Standart Keyfiyyət",
"buffering_goal": "Tamponlama hədəfi (saniyələrlə)",
"export_to_json": "JSON-a İxrac Et",
"import_from_json": "JSON/CSV-dan İdxal Et",
"loop_this_video": "Bu Videonu Təkrarla",
"auto_play_next_video": "Növbəti Videonu Avto-Oynat",
"donations": "İnkişaf ianələri",
"minimize_description": "Açıqlamanı Kiçildin",
"show_description": "Açıqlamanı Göstərin",
"minimize_recommendations": "Tövsiyələri ən aza endirin",
"show_recommendations": "Tövsiyələri Göstərin",
"disable_lbry": "Yayım üçün LBRY-ni Söndürün",
"enable_lbry_proxy": "LBRY üçün Proksi-ni Aktivləşdirin",
"view_ssl_score": "SSL Nəticəsinə Baxın",
"search": "Axtarın",
"minimize_description": "Açıqlamanı Kiçilt",
"show_description": "Açıqlamanı Göstər",
"minimize_recommendations": "Tövsiyələri Kiçilt",
"show_recommendations": "Tövsiyələri Göstər",
"disable_lbry": "Yayım üçün LBRY-ni deaktiv et",
"enable_lbry_proxy": "LBRY üçün Proksi-ni Aktivləşdir",
"view_ssl_score": "SSL Nəticəsinə Bax",
"search": "Axtarış (Ctrl+K)",
"filter": "Filtr",
"loading": "Yüklənir...",
"clear_history": "Tarixçəni Təmizləyin",
"hide_replies": "Cavabları Gizlədin",
"clear_history": "Tarixçəni Təmizlə",
"hide_replies": "Cavabları Gizlət",
"load_more_replies": "Daha Çox Cavab Yüklə",
"add_to_playlist": "Pleylistə Əlavə Edin",
"remove_from_playlist": "Pleylistdən Silin",
"delete_playlist_video_confirm": "Video pleylistdən silinsin?",
"create_playlist": "Pleylist Yaradın",
"delete_playlist": "Pleylisti Silin",
"select_playlist": "Pleylist Seçin",
"delete_playlist_confirm": "Bu pleylist silinsin?",
"please_select_playlist": "Lütfən, pleylist seçin",
"add_to_playlist": "Oynatma siyahısına əlavə et",
"remove_from_playlist": "Oynatma siyahısından təmizlə",
"delete_playlist_video_confirm": "Video oynatma siyahısından silinsin?",
"create_playlist": "Oynatma Siyahısı Yarat",
"delete_playlist": "Oynatma Siyahısın Sil",
"select_playlist": "Oynatma Siyahısı Seç",
"delete_playlist_confirm": "Bu oynatma siyahısı silinsin?",
"please_select_playlist": "Xahiş edilir, oynatma siyahısı seç",
"country_selection": "Ölkə Seçimi",
"default_homepage": "Defolt Əsas Səhifə",
"show_comments": "Şərhləri Göstərin",
"default_homepage": "Standart Əsas Səhifə",
"show_comments": "Şərhləri Göstər",
"instance_selection": "Nümunə Seçimi",
"minimize_description_default": "Açıqlamanı Defolt Olaraq Kiçildin",
"minimize_description_default": "Açıqlamanı Standart Olaraq Kiçilt",
"language_selection": "Dil Seçimi",
"instances_list": "Nümunələr Siyahısı",
"show_more": "Daha Çox Göstər",
"show_more": "Daha çox göstər",
"no": "Xeyr",
"store_watch_history": "Baxış Tarixçəsini Saxlayın",
"enabled_codecs": "Aktiv Kodeklər (Birdən çox)",
"store_watch_history": "Baxış Tarixçəsin Saxla",
"enabled_codecs": "Aktiv Kodlayıcılar (Çoxlu)",
"yes": "Bəli",
"show_markers": "Oynadıcıda Markerləri Göstərin",
"delete_account": "Hesabı Silin",
"logout": "Bu cihazdan çıxın",
"minimize_recommendations_default": "Defolt olaraq Tövsiyələri minimuma endir",
"download_as_txt": ".txt kimi endirin",
"reset_preferences": "Seçimləri sıfırlayın",
"confirm_reset_preferences": "Seçimləri sıfırlamaq istədiyinizə əminsiniz?",
"backup_preferences": "Yedəkləmə seçimləri",
"restore_preferences": "Seçimləri bərpa edin",
"show_markers": "Oynadıcıda Markerləri Göstər",
"delete_account": "Hesabı Sil",
"logout": "Bu cihazdan çıx",
"minimize_recommendations_default": "Standart olaraq Tövsiyələri kiçilt",
"download_as_txt": ".txt kimi endir",
"reset_preferences": "Üstünlükləri sıfırla",
"confirm_reset_preferences": "Seçimlərinizi sıfırlamaq istədiyinizə əminsiniz?",
"backup_preferences": "Nüsxələmə seçimləri",
"restore_preferences": "Seçimləri bərpa et",
"invalidate_session": "Bütün cihazlardan çıxın",
"different_auth_instance": "Doğrulama üçün fərqli bir nümunədən istifadə edin",
"instance_auth_selection": "Doğrulama Nümunəsi Seçilməsi",
"clone_playlist": "Pleylist Klonlanması",
"different_auth_instance": "Təsdiqləmə üçün fərqli nümunə istifadə et",
"instance_auth_selection": "Təsdiqləmə Nümunəsi Seçimi",
"clone_playlist": "Oynatma Siyahısın Klonla",
"clone_playlist_success": "Uğurla klonlandı!",
"rename_playlist": "Pleylistin adını dəyiş",
"rename_playlist": "Oynatma siyahısın yenidən adlandır",
"time_code": "Vaxt kodu (saniyələrlə)",
"store_search_history": "Axtarış tarixçəsini saxla",
"documentation": "Sertifikatlaşdırma",
"documentation": "Sənədləşdirmə",
"status_page": "Vəziyyət",
"source_code": "Mənbə kodu",
"instance_donations": "Nümunə ianələri",
"hide_watched": "Axında baxılan videoları gizlədin",
"hide_watched": "Axında baxılan videoları gizlət",
"show_chapters": "Bölmələr",
"new_playlist_name": "Yeni pleylist adı",
"share": "Paylaşın",
"with_timecode": "Vaxt kodu ilə paylaşın",
"follow_link": "Linki izləyin",
"piped_link": "Piped linki",
"copy_link": "Linki kopyalayın",
"new_playlist_name": "Yeni oynatma siyahısı adı",
"share": "Paylaş",
"with_timecode": "Vaxt kodu ilə paylaş",
"follow_link": "Bağlantını izlə",
"piped_link": "Piped bağlantısı",
"copy_link": "Bağlantını kopyala",
"back_to_home": "Evə qayıt",
"reply_count": "{count} cavab",
"minimize_comments_default": "Şərhləri standart olaraq kiçilt",
"minimize_comments": "Şərhləri Kiçilt"
"minimize_comments": "Şərhləri Kiçilt",
"minimize_chapters_default": "Standart olaraq bölmələri kiçilt",
"show_watch_on_youtube": "YouTube-da Baxış düyməsin göstər",
"no_valid_playlists": "Faylın etibarlı oynatma siyahıları yoxdur!",
"with_playlist": "Oynatma siyahısıyla paylaş",
"bookmark_playlist": "Əlfəcin",
"playlist_bookmarked": "Əlfəcinləndi",
"skip_button_only": "Ötürmə düyməsin göstər",
"skip_automatically": "Avtomatik olaraq",
"min_segment_length": "Minimum Seqment Uzunluğu (saniyələrlə)",
"skip_segment": "Seqmenti ötür",
"show_less": "Daha az göstər",
"autoplay_next_countdown": "Növbəti videoya qədər standart geri sayım (saniyə)",
"dismiss": "Rədd et"
},
"comment": {
"pinned_by": "Tərəfindən Sabitləndi {author}",
"disabled": "Şərhlər yükləyici tərəfindən deaktiv edilib.",
"loading": "Şərhlər yüklənir...",
"user_disabled": "Şərhlər tənzimləmələrdə deaktiv edilib."
"pinned_by": "{author} tərəfindən sabitlənib",
"disabled": "Şərhlər yükləyici tərəfindən bağlanıb.",
"loading": "Şərhlər yüklənilir...",
"user_disabled": "Şərhlər tənzimləmələrdə qeyri-aktiv edilib."
},
"preferences": {
"instance_name": "Nümunə Adı",
"instance_locations": "Nümunə Məkanları",
"has_cdn": "CDN varmı?",
"has_cdn": "CDN Varmı?",
"registered_users": "Qeydiyyatdan Keçmiş İstifadəçilər",
"version": "Versiya",
"up_to_date": "Güncəllənib?",
@ -140,21 +156,23 @@
"views": "{views} baxış",
"watched": "Baxılıb",
"sponsor_segments": "Sponsorlar Seqmentləri",
"ratings_disabled": "Reytinqlər Deaktivdir",
"chapters": "Bölümlər",
"ratings_disabled": "Reytinqlər Qeyri-aktivdir",
"chapters": "Bölmələr",
"live": "{0} Canlı",
"shorts": "Qısa"
"shorts": "Qısa",
"all": "Hamısı",
"category": "Kateqoriya"
},
"search": {
"did_you_mean": "Bunu nəzərdə tutursunuz: {0}?",
"all": "YouTube: Hamısı",
"videos": "YouTube: Videolar",
"channels": "YouTube: Kanallar",
"playlists": "YouTube: Pleylistlər",
"playlists": "YouTube: Oynatma siyahıları",
"music_songs": "YT Music: Mahnılar",
"music_videos": "YT Music: Videolar",
"music_albums": "YT Music: Albomlar",
"music_playlists": "YT Music: Pleylistlər"
"music_playlists": "YT Music: Oynatma Siyahıları"
},
"subscriptions": {
"subscribed_channels_count": "Abunə oldu: {0}"
@ -163,9 +181,12 @@
"preferences_note": "Qeyd: seçimlər brauzerinizin yerli yaddaşında saxlanılır. Brauzer məlumatlarınızın silinməsi onları sıfırlayacaq."
},
"info": {
"preferences_note": "Qeyd: Seçimlər brauzerinizin yerli yaddaşında saxlanılır. Brauzer məlumatlarınızın silinməsi onları sıfırlayacaq.",
"preferences_note": "Qeyd: Seçimlər brauzerinizin öz yaddaşında saxlanılır. Brauzer məlumatınızın silinməsi onları sıfırlayacaq.",
"page_not_found": "Səhifə tapılmadı",
"copied": "Kopyalandı!",
"cannot_copy": "Kopyalanmır!"
"copied": "Nüsxələndi!",
"cannot_copy": "Nüsxələnmir!",
"local_storage": "Bu fəaliyyət yerli yaddaş tələb edir, məlumat bazası aktivdir?",
"register_no_email_note": "E-poçt-u istifadəçi adı kimi istifadə etmək tövsiyə edilmir. Baxmayaraq ki, davam edilsin?",
"next_video_countdown": "Növbəti video {0} saniyəyə oynadılır"
}
}

168
src/locales/bg.json Normal file
View file

@ -0,0 +1,168 @@
{
"titles": {
"channels": "Канали",
"login": "Вход",
"register": "Регистрация",
"feed": "Абонаменти",
"history": "История",
"playlists": "Плейлисти",
"instance": "Инстанция",
"player": "Плейър",
"livestreams": "Излъчвания на живо",
"bookmarks": "Отметки",
"trending": "Набиращи популярност",
"account": "Профил",
"preferences": "Настройки",
"subscriptions": "Абонаменти"
},
"actions": {
"most_recent": "Най-скорошен",
"unsubscribe": "Отписване - {count}",
"uses_api_from": "Използва API от ",
"skip_sponsors": "Пропускане на спонсори",
"skip_preview": "Пропускане на преглед/обобщение",
"skip_self_promo": "Пропускане на самореклама/неплатена реклама",
"min_segment_length": "Минимална дължина на сегмента (в секунди)",
"default_quality": "Качество по подразбиране",
"minimize_comments_default": "Минимизиране на коментарите по подразбиране",
"subscribe": "Абониране - {count}",
"view_subscriptions": "Преглед на абонаменти",
"sort_by": "Сортиране по:",
"least_recent": "Най-малко скорошен",
"channel_name_asc": "Име на канал (А-Я)",
"channel_name_desc": "Име на канал (Я-А)",
"back": "Назад",
"enable_sponsorblock": "Активиране на SponsorBlock",
"skip_button_only": "Показване на бутона за пропускане",
"skip_automatically": "Автоматично",
"skip_intro": "Пропускане на прекъсване/въвеждаща анимация",
"skip_outro": "Пропускане на крайни карти/надписи",
"skip_interaction": "Пропускане на напомняне за абониране",
"skip_non_music": "Попускане Немузикален раздел в музика",
"skip_highlight": "Пропускане на видео акцент",
"show_markers": "Показване на маркери в плейъра",
"skip_segment": "Пропускане на сегмент",
"theme": "Тема",
"auto": "Автоматично",
"dark": "Тъмна",
"light": "Светла",
"autoplay_video": "Автоматично пускане на видео",
"audio_only": "Само аудио",
"buffering_goal": "Буфериране (в секунди)",
"country_selection": "Избор на държава",
"default_homepage": "Начална страница по подразбиране",
"minimize_description_default": "Минимизиране на описанието по подразбиране",
"store_watch_history": "Запазване на историята на гледане",
"language_selection": "Избор на език",
"instances_list": "Списък на инстанциите",
"enabled_codecs": "Разрешени кодеци (множество)",
"instance_selection": "Избор на инстанция",
"show_more": "Покажи повече",
"yes": "Да",
"no": "Не",
"export_to_json": "Експорт в JSON",
"import_from_json": "Импорт от JSON/CSV",
"loop_this_video": "Повтаряне на това видео",
"auto_play_next_video": "Автоматично пускане на следващото видео",
"donations": "Дарения за разработка",
"minimize_comments": "Минимизиране на коментарите",
"show_comments": "Показване на коментарите",
"show_description": "Показване на описание",
"search": "Търси",
"minimize_description": "Минимизиране на описание",
"filter": "Филтър",
"clear_history": "Изчистване на историята",
"minimize_recommendations": "Минимизиране на препоръчани",
"show_recommendations": "Показване на препоръчани",
"view_ssl_score": "Преглед на SSL резултат",
"loading": "Зареждане...",
"hide_replies": "Скрий отговорите",
"load_more_replies": "Зареди още отговори",
"remove_from_playlist": "Премахване от плейлист",
"create_playlist": "Създаване на плейлист",
"reset_preferences": "Нулиране на настройките",
"with_timecode": "Сподели с текущото време",
"piped_link": "Piped връзка",
"documentation": "Документация",
"delete_account": "Изтрий акаунта",
"download_as_txt": "Изтегляне като .txt",
"share": "Сподели",
"follow_link": "Последвай връзката",
"add_to_playlist": "Добави към плейлист",
"delete_playlist_video_confirm": "Да се премахне ли видеото от плейлиста?",
"show_watch_on_youtube": "Показване на бутона \"Гледай в YouTube\"",
"source_code": "Изходен код",
"minimize_chapters_default": "Минимизиране на разделите по подразбиране",
"minimize_recommendations_default": "Минимизиране на препоръчани по подразбиране",
"show_chapters": "Раздели",
"logout": "Отписване от това устройство",
"clone_playlist": "Клониране на плейлист",
"clone_playlist_success": "Успешно клониране!",
"backup_preferences": "Архивиране на настройките",
"rename_playlist": "Преименуване на плейлиста",
"new_playlist_name": "Ново име на плейлиста",
"back_to_home": "Обратно към начална страница",
"status_page": "Статус",
"copy_link": "Копирай връзката",
"time_code": "Текущо време (в секунди)",
"reply_count": "{count} отговора",
"restore_preferences": "Възстановяване на настройките",
"invalidate_session": "Отписване от всички устройства",
"different_auth_instance": "Използване на различна инстанция за удостоверяване",
"store_search_history": "Запазване на историята на търсене",
"instance_auth_selection": "Избор на инстанция за удостоверяване",
"confirm_reset_preferences": "Сигурни ли сте, че искате да нулирате настройките?",
"hide_watched": "Скриване на гледани видеоклипове в Абонаменти"
},
"player": {
"watch_on": "Гледай в {0}"
},
"login": {
"username": "Потребителско име",
"password": "Парола"
},
"video": {
"videos": "Видеоклипове",
"views": "{views} показвания",
"chapters": "Раздели",
"all": "Всички",
"watched": "Гледани",
"category": "Категория"
},
"preferences": {
"version": "Версия",
"registered_users": "Регистрирани потребители",
"instance_locations": "Местоположения на инстанция",
"instance_name": "Име на инстанция",
"has_cdn": "Има ли CDN?",
"up_to_date": "Актуален?",
"ssl_score": "SSL резултат"
},
"comment": {
"disabled": "Коментарите са деактивирани.",
"pinned_by": "Фиксиран от {author}",
"loading": "Коментарите се зареждат...",
"user_disabled": "Коментарите са деактивирани в настройките."
},
"search": {
"did_you_mean": "Имахте предвид: {0}?",
"all": "YouTube: Всички",
"videos": "YouTube: Видеоклипове",
"channels": "YouTube: Канали",
"playlists": "YouTube: Плейлисти",
"music_songs": "YT Music: Песни",
"music_videos": "YT Music: Видеоклипове",
"music_albums": "YT Music: Албуми",
"music_playlists": "YT Music: Плейлисти"
},
"subscriptions": {
"subscribed_channels_count": "Абониран за: {0}"
},
"info": {
"page_not_found": "Страницата не е намерена",
"copied": "Копирано!",
"cannot_copy": "Не може да се копира!",
"local_storage": "Това действие изисква localStorage, разрешени ли са бисквитките?",
"register_no_email_note": "Използването на имейл като потребителско име не се препоръчва. Продължете все пак?"
}
}

View file

@ -47,7 +47,7 @@
"country_selection": "Izbor zemalja",
"minimize_description_default": "Umanji Opis po podrazumijevanim podešavanjima",
"no": "Ne",
"donations": "Donacije",
"donations": "Donacije za razvoj",
"show_description": "Prikaži opis",
"load_more_replies": "Učitajte još odgovora",
"enabled_codecs": "Omogućeni kodeci (množina)",
@ -63,9 +63,53 @@
"view_ssl_score": "Pogledajte SSL rezultat",
"hide_replies": "Sakrijte odgovore",
"remove_from_playlist": "Uklonite iz popisa snimaka",
"delete_playlist_video_confirm": "Jel baš želite ukloniti ovaj video iz ovog popisa snimaka?",
"delete_playlist_video_confirm": "Uklonite ovaj video iz popisa snimaka?",
"select_playlist": "Odaberite popis snimaka",
"delete_playlist_confirm": "Jeste li sigurni da želite izbrisati ovaj popis snimaka?"
"delete_playlist_confirm": "Izbrisati ovaj popis snimaka?",
"show_markers": "Prikaži markere na Pokretniku",
"share": "Podijeli",
"invalidate_session": "Odjavite se sa svih uređaja",
"show_chapters": "Poglavlja",
"status_page": "Status",
"source_code": "Izvorni kod",
"clone_playlist_success": "Uspješno klonirano!",
"store_search_history": "Pohrani historiju pretraživanja",
"minimize_chapters_default": "Smanjite poglavlja po zadanom",
"show_watch_on_youtube": "Prikaži „Gledaj na YouTube-u” dugme",
"different_auth_instance": "Koristite drugu instancu za autentifikaciju",
"rename_playlist": "Preimenuj listu izvođenja",
"new_playlist_name": "Novi naziv liste izvođenja",
"with_timecode": "Podijelite s vremenskim kodom",
"piped_link": "Piped poveznica",
"follow_link": "Prati poveznicu",
"time_code": "Vremenski kod (u sekundama)",
"hide_watched": "Sakrijte gledane videozapise u sažetku sadržaja",
"instance_donations": "Donacije za instancu",
"reply_count": "{count} odgovora",
"logout": "Odjavite se sa ovog uređaja",
"download_as_txt": "Preuzmite kao .txt",
"backup_preferences": "Spremi sigurnosnu kopiju postavki",
"instance_auth_selection": "Odabir instance autentikacije",
"restore_preferences": "Vrati postavke",
"back_to_home": "Povratak na početnu",
"copy_link": "Kopiraj poveznicu",
"no_valid_playlists": "Datoteka ne sadrži važeće liste za reprodukciju!",
"with_playlist": "Podijeli sa listom izvođenja",
"clone_playlist": "Kloniraj listu za reprodukciju",
"documentation": "Dokumentacija",
"confirm_reset_preferences": "Jeste li sigurni da želite obnoviti svoje postavke?",
"minimize_comments_default": "Po zadanom smanjite komentare",
"minimize_comments": "Minimizirajte komentare",
"delete_account": "Izbriši račun",
"minimize_recommendations_default": "Smanjite preporuke po zadanom",
"reset_preferences": "Vrati postavke na zadano",
"bookmark_playlist": "Bilježak",
"playlist_bookmarked": "Obilježeno",
"show_less": "Prikaži manje",
"skip_button_only": "Prikaži dugme za preskakanje",
"skip_automatically": "Automatski",
"min_segment_length": "Najmanja dužina segmenta (u sekundama)",
"skip_segment": "Preskoči segment"
},
"titles": {
"register": "Registrirajte se",
@ -75,7 +119,13 @@
"feed": "Novosti",
"preferences": "Podešavanja",
"playlists": "Popisi Snimaka",
"subscriptions": "Pretplate"
"subscriptions": "Pretplate",
"instance": "Instanca",
"account": "Račun",
"player": "Pokretnik",
"channels": "Kanali",
"livestreams": "Prijenosi uživo",
"bookmarks": "Bilješci"
},
"search": {
"music_songs": "YT Music: Pjesme",
@ -111,9 +161,26 @@
"ratings_disabled": "Ocjene su isključene",
"watched": "Pogledano",
"videos": "Video zapisi",
"live": "{0} Uživo"
"live": "{0} Uživo",
"shorts": "Kratki videi",
"category": "Kategorija",
"all": "Sve"
},
"comment": {
"pinned_by": "Prikačeno od {author}"
"pinned_by": "Prikačeno od {author}",
"disabled": "Komentari su onemogućeni od strane prijenosnika.",
"loading": "Učitavanje komentara...",
"user_disabled": "Komentari su onemogućeni u postavkama."
},
"subscriptions": {
"subscribed_channels_count": "Pretplaćeni ste na: {0}"
},
"info": {
"preferences_note": "Napomena: Preferencije se čuvaju u lokalnom skladištu vašeg pretraživača. Brisanje podataka vašeg preglednika će ih resetirati.",
"cannot_copy": "Nije moguće kopirati!",
"page_not_found": "Stranica nije pronađena",
"copied": "Kopirano!",
"local_storage": "Ova radnja zahtijeva lokalno pohranjivanje, jesu li kolačići omogućeni?",
"register_no_email_note": "Korištenje e-maila kao korisničko ime se ne preporučuje. Svejedno nastaviti?"
}
}

View file

@ -10,7 +10,10 @@
"playlists": "Llistes de reproducció",
"account": "Compte",
"instance": "Instància",
"player": "Reproductor"
"player": "Reproductor",
"livestreams": "Retransmissió en directe",
"channels": "Canals",
"bookmarks": "Marcadors"
},
"actions": {
"channel_name_desc": "Nom del Canal (Z-A)",
@ -40,7 +43,7 @@
"enabled_codecs": "Còdecs Habilitats (Múltiple)",
"instances_list": "Llista d'Instàncies",
"instance_selection": "Selecció d'Instàncies",
"show_more": "Mostrar Més",
"show_more": "Mostrar més",
"yes": "Sí",
"no": "No",
"export_to_json": "Exportar a JSON",
@ -108,7 +111,21 @@
"show_chapters": "Capítols",
"status_page": "Estat",
"source_code": "Codi font",
"documentation": "Documentació"
"documentation": "Documentació",
"show_watch_on_youtube": "Mostra el botó \"Veure a Youtube\"",
"reply_count": "{count} respostes",
"minimize_comments_default": "Minimitzar els comentaris per defecte",
"minimize_comments": "Minimitza els comentaris",
"no_valid_playlists": "L'arxiu no conté llistes de reproducció vàlides!",
"bookmark_playlist": "Marcador",
"playlist_bookmarked": "Afegit a marcadors",
"minimize_chapters_default": "Minimitzar capítols per defecte",
"skip_button_only": "Mostra el botó de saltar",
"skip_automatically": "Automàticament",
"min_segment_length": "Longitud de segment mínima (en segons)",
"skip_segment": "Saltar segment",
"with_playlist": "Comparteix amb llista de reproducció",
"show_less": "Mostrar menys"
},
"comment": {
"pinned_by": "Fixat per {author}",
@ -133,7 +150,9 @@
"live": "{0} En Directe",
"videos": "Vídeos",
"views": "{views} visualitzacions",
"shorts": "Curts"
"shorts": "Curts",
"all": "Tot",
"category": "Categoria"
},
"search": {
"did_you_mean": "Volies dir: {0}?",
@ -163,6 +182,8 @@
"preferences_note": "Nota: les preferències es desen a l'emmagatzematge local del navegador. Si elimineu les dades del navegador, es restabliran.",
"page_not_found": "No s'ha torbat la pàgina",
"copied": "Copiat!",
"cannot_copy": "No es pot copiar!"
"cannot_copy": "No es pot copiar!",
"local_storage": "Aquesta acció requereix emmagatzematge local, estan les cookies habilitades?",
"register_no_email_note": "Utilitzar un correu elextrònic com a usuari no és recomanable. Continuar de totes maneres?"
}
}

View file

@ -12,7 +12,8 @@
"instance": "Instance",
"player": "Přehrávač",
"livestreams": "Živé přenosy",
"channels": "Kanály"
"channels": "Kanály",
"bookmarks": "Záložky"
},
"actions": {
"loop_this_video": "Přehrávat video ve smyčce",
@ -59,7 +60,7 @@
"disable_lbry": "Zakázat LBRY pro streamování",
"enable_lbry_proxy": "Povolit proxy pro LBRY",
"view_ssl_score": "Zobrazit stav SSL",
"search": "Vyhledat",
"search": "Vyhledávání (Ctrl+K)",
"filter": "Filtr",
"loading": "Načítání...",
"clear_history": "Smazat historii",
@ -113,7 +114,20 @@
"status_page": "Stav",
"reply_count": "{count} odpovědí",
"minimize_comments_default": "Ve výchozím nastavení skrýt komentáře",
"minimize_comments": "Skrýt komentáře"
"minimize_comments": "Skrýt komentáře",
"show_watch_on_youtube": "Zobrazit tlačítko Sledovat na YouTube",
"minimize_chapters_default": "Ve výchozím nastavení skrýt kapitoly",
"no_valid_playlists": "Soubor neobsahuje platné playlisty!",
"with_playlist": "Sdílet s playlistem",
"bookmark_playlist": "Záložka",
"playlist_bookmarked": "Uloženo",
"skip_automatically": "Automaticky",
"skip_segment": "Přeskočit segment",
"skip_button_only": "Zobrazit tlačítko přeskočení",
"min_segment_length": "Minimální délka segmentu (v sekundách)",
"show_less": "Zobrazit méně",
"autoplay_next_countdown": "Výchozí odpočet do dalšího videa (v sekundách)",
"dismiss": "Zavřít"
},
"player": {
"watch_on": "Sledovat na {0}"
@ -145,7 +159,9 @@
"ratings_disabled": "Hodnocení zakázáno",
"chapters": "Kapitoly",
"live": "{0} Živě",
"shorts": "Shorts"
"shorts": "Shorts",
"all": "Vše",
"category": "Kategorie"
},
"search": {
"did_you_mean": "Mysleli jste: {0}?",
@ -168,6 +184,9 @@
"preferences_note": "Poznámka: předvolby se ukládají do místního úložiště prohlížeče. Vymazáním dat prohlížeče budou obnoveny.",
"page_not_found": "Stránka nenalezena",
"copied": "Zkopírováno!",
"cannot_copy": "Nelze zkopírovat!"
"cannot_copy": "Nelze zkopírovat!",
"local_storage": "Tato akce vyžaduje localStorage, jsou povoleny cookies?",
"register_no_email_note": "Použití e-mailu jako uživatelského jména se nedoporučuje. Chcete přesto pokračovat?",
"next_video_countdown": "Přehrávání dalšího videa za {0}s"
}
}

View file

@ -1,16 +1,16 @@
{
"actions": {
"skip_outro": "Abspann überspringen",
"skip_non_music": "Musik überspringen: Nicht-Musik-Bereich",
"skip_outro": "Endkarten und Abspann überspringen",
"skip_non_music": "Musik: Nicht-Musik-Abschnitte überspringen",
"skip_self_promo": "Unbezahlte Werbung/Eigenwerbung überspringen",
"skip_interaction": "Interaktionserinnerung überspringen (Abonnieren)",
"skip_preview": "Vorschau/Rückschau überspringen",
"skip_interaction": "Interaktionserinnerungen überspringen (Daumen hoch, abonnieren, ...)",
"skip_preview": "Vorschau und Rückblick überspringen",
"instances_list": "Liste der Instanzen",
"language_selection": "Sprachauswahl",
"store_watch_history": "Wiedergabeverlauf speichern",
"minimize_description_default": "Beschreibung standardmäßig minimieren",
"show_comments": "Kommentare anzeigen",
"default_homepage": "Standard-Startseite",
"default_homepage": "Startseite",
"country_selection": "Länderauswahl",
"buffering_goal": "Pufferungsziel (in Sekunden)",
"default_quality": "Standardqualität",
@ -20,9 +20,9 @@
"dark": "Dunkel",
"auto": "Automatisch",
"theme": "Farbschema",
"skip_intro": "Pausen-/Intro-Animation überspringen",
"skip_sponsors": "Sponsoren überspringen",
"enable_sponsorblock": "Sponsorblock einschalten",
"skip_intro": "Unterbrechungen und Intro-Animation überspringen",
"skip_sponsors": "Gesponsorte Videoabschnitte überspringen",
"enable_sponsorblock": "SponsorBlock verwenden",
"uses_api_from": "Verwendet die API von ",
"back": "Zurück",
"channel_name_desc": "Kanalname (Z-A)",
@ -30,13 +30,13 @@
"least_recent": "Am wenigsten neu",
"most_recent": "Am Neuesten",
"sort_by": "Sortieren nach:",
"view_subscriptions": "Abonnements anzeigen",
"view_subscriptions": "Abos anzeigen",
"unsubscribe": "Deabonnieren - {count}",
"subscribe": "Abonnieren - {count}",
"enabled_codecs": "Aktivierte Codecs (mehrere)",
"enabled_codecs": "Aktivierte Codecs (Auswahl mehrerer Codecs möglich)",
"enable_lbry_proxy": "Proxy für LBRY einschalten",
"disable_lbry": "LBRY für Streaming deaktivieren",
"instance_selection": "Instanzauswahl",
"instance_selection": "Instanz auswählen",
"show_description": "Beschreibung anzeigen",
"minimize_description": "Beschreibung minimieren",
"show_recommendations": "Empfehlungen anzeigen",
@ -51,32 +51,32 @@
"yes": "Ja",
"loading": "Wird geladen…",
"filter": "Filtern",
"search": "Suchen",
"search": "Suchen (Strg+K)",
"view_ssl_score": "SSL-Bewertung anzeigen",
"clear_history": "Verlauf löschen",
"hide_replies": "Antworten ausblenden",
"load_more_replies": "Mehr Antworten laden",
"skip_highlight": "Höhepunkt überspringen",
"skip_filler_tangent": "Lückenfüller überspringen",
"delete_playlist_confirm": "Diese Wiedergabeliste löschen?",
"remove_from_playlist": "Aus Wiedergabeliste entfernen",
"add_to_playlist": "Zur Wiedergabeliste hinzufügen",
"create_playlist": "Wiedergabeliste erstellen",
"delete_playlist_video_confirm": "Video aus Wiedergabeliste entfernen?",
"delete_playlist": "Wiedergabeliste löschen",
"please_select_playlist": "Bitte wählen Sie eine Wiedergabeliste",
"select_playlist": "Wählen Sie eine Wiedergabeliste",
"delete_playlist_confirm": "Diese Playlist löschen?",
"remove_from_playlist": "Aus Playlist entfernen",
"add_to_playlist": "Zur Playlist hinzufügen",
"create_playlist": "Playlist erstellen",
"delete_playlist_video_confirm": "Video aus Playlist entfernen?",
"delete_playlist": "Playlist löschen",
"please_select_playlist": "Bitte wähle eine Playlist",
"select_playlist": "Wähle eine Playlist",
"show_markers": "Markierungen auf dem Player anzeigen",
"delete_account": "Konto löschen",
"logout": "Von diesem Gerät abmelden",
"minimize_recommendations_default": "Empfehlungen standardmäßig minimieren",
"invalidate_session": "Von allen Geräte abmelden",
"invalidate_session": "Von allen Geräten abmelden",
"different_auth_instance": "Eine andere Instanz für die Authentifizierung verwenden",
"instance_auth_selection": "Auswahl der Autentifizierungsinstanz",
"clone_playlist": "Wiedergabeliste klonen",
"clone_playlist_success": "Erfolgreich geklont!",
"rename_playlist": "Wiedergabeliste umbenennen",
"new_playlist_name": "Neuer Name der Wiedergabeliste",
"clone_playlist": "Playlist duplizieren",
"clone_playlist_success": "Erfolgreich dupliziert!",
"rename_playlist": "Playlist umbenennen",
"new_playlist_name": "Neuer Name der Playlist",
"piped_link": "Piped-Link",
"download_as_txt": "Als .txt herunterladen",
"back_to_home": "Zurück zur Startseite",
@ -84,7 +84,7 @@
"with_timecode": "Mit Zeitstempel teilen",
"follow_link": "Link öffnen",
"copy_link": "Link kopieren",
"time_code": "Zeitstempel (in sekunden)",
"time_code": "Zeitstempel (in Sekunden)",
"reset_preferences": "Einstellungen zurücksetzen",
"confirm_reset_preferences": "Bist du sicher, dass du deine Einstellungen zurücksetzen möchtest?",
"backup_preferences": "Einstellungen sichern",
@ -92,11 +92,26 @@
"show_chapters": "Kapitel",
"source_code": "Quellcode",
"store_search_history": "Suchverlauf speichern",
"hide_watched": "Gesehene Videos im Feed ausblenden",
"hide_watched": "Gesehene Videos im Abo-Feed ausblenden",
"reply_count": "{count} Antworten",
"instance_donations": "Instanz-Spenden",
"documentation": "Dokumentation",
"status_page": "Status"
"status_page": "Status",
"minimize_chapters_default": "Kapitel standardmäßig minimieren",
"minimize_comments_default": "Kommentare standardmäßig minimieren",
"minimize_comments": "Kommentare minimieren",
"no_valid_playlists": "Die Datei enthält keine gültigen Playlists!",
"show_watch_on_youtube": "Schaltfläche „Auf YouTube ansehen“ anzeigen",
"with_playlist": "Mit Playlist teilen",
"playlist_bookmarked": "Markiert",
"bookmark_playlist": "Lesezeichen",
"skip_segment": "Abschnitt überspringen",
"skip_automatically": "Automatisch",
"min_segment_length": "Minimale Abschnittlänge (in Sekunden)",
"skip_button_only": "Überspringen-Schaltfläche anzeigen",
"show_less": "Weniger anzeigen",
"autoplay_next_countdown": "Anzahl der Sekunden bis das nächste Video automatisch startet",
"dismiss": "Ablehnen"
},
"player": {
"watch_on": "Auf {0} ansehen"
@ -104,25 +119,30 @@
"titles": {
"history": "Verlauf",
"preferences": "Einstellungen",
"feed": "Abonnements",
"feed": "Abos",
"register": "Registrieren",
"login": "Anmelden",
"trending": "Trends",
"subscriptions": "Abonnements",
"playlists": "Wiedergabelisten",
"subscriptions": "Abos",
"playlists": "Playlists",
"account": "Konto",
"player": "Player",
"instance": "Instanz"
"instance": "Instanz",
"livestreams": "Livestreams",
"channels": "Kanäle",
"bookmarks": "Lesezeichen"
},
"video": {
"sponsor_segments": "Sponsoren-Segmente",
"sponsor_segments": "Sponsoren-Abschnitte",
"watched": "Angesehen",
"views": "{views} Aufrufe",
"videos": "Videos",
"ratings_disabled": "Bewertungen deaktiviert",
"live": "{0} Live",
"chapters": "Kapitel",
"shorts": "Shorts"
"shorts": "Shorts",
"all": "Alle",
"category": "Kategorie"
},
"preferences": {
"ssl_score": "SSL-Bewertung",
@ -137,30 +157,33 @@
"pinned_by": "Angeheftet von {author}",
"user_disabled": "Kommentare wurden in den Einstellungen deaktiviert.",
"disabled": "Kommentare wurden vom Autor deaktiviert.",
"loading": "Kommentare werden geladen …"
"loading": "Kommentare werden geladen…"
},
"login": {
"password": "Passwort",
"username": "Anmeldename"
"username": "Benutzername"
},
"search": {
"did_you_mean": "Hast du gemeint: {0}?",
"all": "YouTube: Alle",
"videos": "YouTube: Videos",
"channels": "YouTube: Kanäle",
"playlists": "YouTube: Wiedergabelisten",
"playlists": "YouTube: Playlists",
"music_songs": "YT Music: Lieder",
"music_videos": "YT Music: Videos",
"music_albums": "YT Music: Alben",
"music_playlists": "YT Music: Wiedergabelisten"
"music_playlists": "YT Music: Playlists"
},
"subscriptions": {
"subscribed_channels_count": "Aboniert bei: {0}"
"subscribed_channels_count": "Anzahl Abos: {0}"
},
"info": {
"preferences_note": "Achtung: Einstellung werden lokal in deinem Browser gespeichert. Wenn du deine Browserdaten löschst werden sie auch gelöscht.",
"preferences_note": "Achtung: Die Einstellung werden lokal in deinem Browser gespeichert. Wenn du deine Browserdaten löschst, werden auch deine Einstellungen zurückgesetzt.",
"page_not_found": "Seite nicht gefunden",
"copied": "Kopiert!",
"cannot_copy": "Kopieren nicht möglich!"
"cannot_copy": "Kopieren nicht möglich!",
"local_storage": "Diese Aktion erfordert „localStorage“, sind Cookies aktiviert?",
"register_no_email_note": "Es wird nicht empfohlen, eine E-Mail als Benutzernamen zu verwenden. Trotzdem fortfahren?",
"next_video_countdown": "Nächstes Video startet in {0}s"
}
}

View file

@ -12,7 +12,8 @@
"instance": "Instance",
"player": "Player",
"livestreams": "Livestreams",
"channels": "Channels"
"channels": "Channels",
"bookmarks": "Bookmarks"
},
"player": {
"watch_on": "Watch on {0}"
@ -29,6 +30,8 @@
"back": "Back",
"uses_api_from": "Uses the API from ",
"enable_sponsorblock": "Enable Sponsorblock",
"skip_button_only": "Show skip button",
"skip_automatically": "Automatically",
"skip_sponsors": "Skip Sponsors",
"skip_intro": "Skip Intermission/Intro Animation",
"skip_outro": "Skip Endcards/Credits",
@ -39,11 +42,14 @@
"skip_highlight": "Skip Highlight",
"skip_filler_tangent": "Skip Filler Tangent",
"show_markers": "Show Markers on Player",
"min_segment_length": "Minimum Segment Length (in seconds)",
"skip_segment": "Skip Segment",
"theme": "Theme",
"auto": "Auto",
"dark": "Dark",
"light": "Light",
"autoplay_video": "Autoplay Video",
"autoplay_next_countdown": "Default Countdown until next video (in seconds)",
"audio_only": "Audio Only",
"default_quality": "Default Quality",
"buffering_goal": "Buffering Goal (in seconds)",
@ -73,7 +79,7 @@
"disable_lbry": "Disable LBRY for Streaming",
"enable_lbry_proxy": "Enable Proxy for LBRY",
"view_ssl_score": "View SSL Score",
"search": "Search",
"search": "Search (Ctrl+K)",
"filter": "Filter",
"loading": "Loading...",
"clear_history": "Clear History",
@ -114,13 +120,20 @@
"show_chapters": "Chapters",
"store_search_history": "Store Search history",
"hide_watched": "Hide watched videos in the feed",
"mark_as_watched": "Mark as Watched",
"mark_as_unwatched": "Mark as Unwatched",
"documentation": "Documentation",
"status_page": "Status",
"source_code": "Source code",
"instance_donations": "Instance donations",
"reply_count": "{count} replies",
"mark_as_watched": "Mark as Watched",
"mark_as_unwatched": "Mark as Unwatched"
"no_valid_playlists": "The file doesn't contain valid playlists!",
"with_playlist": "Share with playlist",
"bookmark_playlist": "Bookmark",
"playlist_bookmarked": "Bookmarked",
"dismiss": "Dismiss",
"show_more": "Show more",
"show_less": "Show less"
},
"comment": {
"pinned_by": "Pinned by {author}",
@ -149,7 +162,9 @@
"ratings_disabled": "Ratings Disabled",
"chapters": "Chapters",
"live": "{0} Live",
"shorts": "Shorts"
"shorts": "Shorts",
"all": "All",
"category": "Category"
},
"search": {
"did_you_mean": "Did you mean: {0}?",
@ -169,6 +184,9 @@
"preferences_note": "Note: preferences are saved in the local storage of your browser. Deleting your browser data will reset them.",
"page_not_found": "Page not found",
"copied": "Copied!",
"cannot_copy": "Can't copy!"
"cannot_copy": "Can't copy!",
"local_storage": "This action requires localStorage, are cookies enabled?",
"register_no_email_note": "Using an e-mail as username is not recommended. Proceed anyways?",
"next_video_countdown": "Playing next video in {0}s"
}
}

View file

@ -12,7 +12,8 @@
"player": "Ludilo",
"instance": "Nodo",
"channels": "Kanaloj",
"livestreams": "Tujelsendoj"
"livestreams": "Tujelsendoj",
"bookmarks": "Legosignoj"
},
"player": {
"watch_on": "Vidi en {0}"
@ -38,18 +39,18 @@
"light": "Hela",
"autoplay_video": "Aŭtomate Ludi Videon",
"audio_only": "Nur Sono",
"default_quality": "Defaŭlta Kvalito",
"default_quality": "Implicita Kvalito",
"country_selection": "Landa Elekto",
"default_homepage": "Defaŭlta Ĉefpaĝo",
"default_homepage": "Implicita Ĉefpaĝo",
"show_comments": "Montri Komentojn",
"language_selection": "Lingva Elekto",
"donations": "Donacoj por programado",
"show_more": "Montri Pli",
"show_more": "Montri pli",
"yes": "Jes",
"no": "Ne",
"show_chapters": "Sekcioj",
"filter": "Filtri",
"search": "Serĉi",
"search": "Serĉi (Ctrl+K)",
"hide_replies": "Kaŝi Respondojn",
"add_to_playlist": "Aldoni al ludlisto",
"delete_playlist": "Forigi Ludliston",
@ -113,11 +114,23 @@
"skip_interaction": "Preterpasi Interagan Memorigon (Aboni)",
"store_watch_history": "Konservi Vidhistorion",
"logout": "Elsaluti el ĉi tiu aparato",
"minimize_description_default": "Defaŭlte Plejetigi Priskribon",
"minimize_recommendations_default": "Defaŭlte Plejetigi Rekomendojn",
"minimize_comments_default": "Defaŭlte Plejetigi Komentojn",
"minimize_description_default": "Implicite Plejetigi Priskribon",
"minimize_recommendations_default": "Implicite Plejetigi Rekomendojn",
"minimize_comments_default": "Implicite Plejetigi Komentojn",
"minimize_comments": "Plejetigi Komentojn",
"show_watch_on_youtube": "Montri «Vidi en Youtube»-butonon"
"show_watch_on_youtube": "Montri «Vidi en Youtube»-butonon",
"minimize_chapters_default": "Implicite plejetigi ĉapitrojn",
"no_valid_playlists": "La dosiero ne enhavas validajn ludlistojn!",
"with_playlist": "Konigi kun ludlisto",
"playlist_bookmarked": "Legosignita",
"bookmark_playlist": "Legosigno",
"skip_automatically": "Aŭtomate",
"skip_button_only": "Montri preterpasi-butonon",
"min_segment_length": "Minimuma Segmenta Daŭro (en sekundoj)",
"skip_segment": "Preterpasi Segmenton",
"show_less": "Montri malpli",
"dismiss": "Nuligi",
"autoplay_next_countdown": "Implicita retronombrado ĝis sekva video (en sekundoj)"
},
"video": {
"chapters": "Sekcioj",
@ -127,7 +140,9 @@
"sponsor_segments": "Sponsoraj Segmentoj",
"watched": "Viditaj",
"ratings_disabled": "Taksadoj Malebligitaj",
"shorts": "Mallongaj"
"shorts": "Mallongaj",
"all": "Ĉiuj",
"category": "Kategorio"
},
"search": {
"music_albums": "YT Music: Albumoj",
@ -144,7 +159,10 @@
"copied": "Kopiita!",
"cannot_copy": "Ne povas kopii!",
"preferences_note": "Noto: la agordoj estas konservitaj en la loka memoro de via retumilo. Forigi la datumojn de via retumilo restarigos ilin.",
"page_not_found": "Paĝo ne trovita"
"page_not_found": "Paĝo ne trovita",
"local_storage": "Ĉi tiu ago postulas localStorage, ĉu kuketoj estas ebligitaj?",
"register_no_email_note": "Uzi retadreson kiel uzantnomon ne estas rekomendita. Ĉu daŭrigi ĉiuokaze?",
"next_video_countdown": "Oni ludos la sekvan videon post {0}s"
},
"login": {
"username": "Uzantnomo",

View file

@ -7,7 +7,9 @@
"ratings_disabled": "Valoraciones Desactivadas",
"chapters": "Capítulos",
"live": "{0} Directo",
"shorts": "Cortos"
"shorts": "Cortos",
"all": "Todos",
"category": "Categoría"
},
"preferences": {
"ssl_score": "Puntuación SSL",
@ -40,7 +42,7 @@
"instance_selection": "Selección de instancias",
"enabled_codecs": "Códecs habilitados (múltiples)",
"instances_list": "Lista de instancias",
"language_selection": "Selección de lenguajes",
"language_selection": "Selección de idioma",
"store_watch_history": "Recordar historial de visualización",
"minimize_description_default": "Minimizar la descripción por defecto",
"show_comments": "Mostrar comentarios",
@ -74,7 +76,7 @@
"subscribe": "Suscribirme - {count}",
"loading": "Cargando…",
"filter": "Filtrar",
"search": "Buscar",
"search": "Buscar (Ctrl+K)",
"view_ssl_score": "Ver la puntuación SSL",
"minimize_recommendations": "Minimizar recomendaciones",
"show_recommendations": "Mostrar recomendaciones",
@ -124,7 +126,19 @@
"reply_count": "{count} respuestas",
"minimize_comments_default": "Minimizar comentarios por defecto",
"minimize_comments": "Minimizar comentarios",
"show_watch_on_youtube": "Mostrar botón Ver en YouTube"
"show_watch_on_youtube": "Mostrar botón Ver en YouTube",
"minimize_chapters_default": "Minimiza capítulos por defecto",
"no_valid_playlists": "¡El archivo no contiene listas de reproducción válidas!",
"with_playlist": "Compartir con lista de reproducción",
"playlist_bookmarked": "Marcado",
"bookmark_playlist": "Marcador",
"skip_button_only": "Muestra botón de saltar",
"skip_automatically": "Automáticamente",
"min_segment_length": "Mínima Duración de Segmento (en segundos)",
"skip_segment": "Saltar Segmento",
"show_less": "Mostrar menos",
"autoplay_next_countdown": "Cuenta atrás predeterminada antes del siguiente vídeo (en segundos)",
"dismiss": "Cancelar"
},
"titles": {
"feed": "Fuente web",
@ -139,7 +153,8 @@
"instance": "Instancia",
"player": "Reproductor",
"livestreams": "Directos",
"channels": "Canales"
"channels": "Canales",
"bookmarks": "Marcadores"
},
"player": {
"watch_on": "Ver en {0}"
@ -166,6 +181,9 @@
"preferences_note": "Nota: las preferencias se guardan en el almacenamiento local de tu navegador. Al borrar los datos del navegador se restablecerán.",
"page_not_found": "Página no encontrada",
"copied": "¡Copiado!",
"cannot_copy": "¡No se puede copiar!"
"cannot_copy": "¡No se puede copiar!",
"local_storage": "Esta acción requiere «localStorage», ¿están activadas las «cookies»?",
"register_no_email_note": "No se recomienda usar un correo electrónico como nombre de usuario. ¿Continuar de todos modos?",
"next_video_countdown": "El próximo vídeo se reproducirá en {0}s"
}
}

View file

@ -89,7 +89,40 @@
"skip_highlight": "Ohita kohokohta",
"skip_filler_tangent": "Ohita epäolennainen",
"enabled_codecs": "Käytössä olevat koodekit (useita)",
"show_markers": "Näytä merkit soittimessa"
"show_markers": "Näytä merkit soittimessa",
"confirm_reset_preferences": "Oletko varma, että haluat palauttaa asetukset?",
"back_to_home": "Takaisin kotisivuun",
"minimize_recommendations_default": "Minimoi suositukset oletusarvoisesti",
"with_timecode": "Jaa aikakoodilla",
"documentation": "Dokumentaatio",
"piped_link": "Piped-linkki",
"store_search_history": "Tallenna hakuhistoria",
"minimize_chapters_default": "Minimoi luvut oletusarvoisesti",
"show_watch_on_youtube": "Näytä Katso YouTubessa -painike",
"different_auth_instance": "Käytä eri instanssia todennukseen",
"download_as_txt": "Lataa .txt-tiedostona",
"rename_playlist": "Nimeä soittolista uudelleen",
"show_chapters": "Luvut",
"minimize_comments": "Minimoi kommentit",
"minimize_comments_default": "Minimoi kommentit oletusarvoisesti",
"delete_account": "Poista tili",
"clone_playlist_success": "Onnistunut kloonaus!",
"reset_preferences": "Nollaa asetukset",
"copy_link": "Kopioi linkki",
"status_page": "Tila",
"source_code": "Lähdekoodi",
"instance_donations": "Instanssille lahjoitukset",
"no_valid_playlists": "Tiedosto ei sisällä kelvollisia soittolistoja!",
"share": "Jaa",
"reply_count": "{count} vastausta",
"hide_watched": "Piilota katsotut videot syötteessä",
"time_code": "Aikakoodi (sekunteina)",
"follow_link": "Avaa linkki",
"new_playlist_name": "Soittolistan uusi nimi",
"invalidate_session": "Kirjaudu ulos kaikista laitteista",
"logout": "Kirjaudu ulos tästä laitteesta",
"backup_preferences": "Varmuuskopiointiasetukset",
"restore_preferences": "Palauta asetukset"
},
"player": {
"watch_on": "Katso sivustolla {0}"
@ -102,7 +135,11 @@
"register": "Rekisteröidy",
"login": "Kirjaudu sisään",
"trending": "Nousussa",
"playlists": "Soittolistat"
"playlists": "Soittolistat",
"player": "Toistin",
"instance": "Instanssi",
"account": "Tili",
"channels": "Kanavat"
},
"search": {
"did_you_mean": "Tarkoititko: {0}?",
@ -116,6 +153,15 @@
"music_playlists": "YT Music: Soittolistat"
},
"comment": {
"pinned_by": "Kiinnitti {author}"
"pinned_by": "Kiinnitti {author}",
"loading": "Ladataan kommentteja…",
"user_disabled": "Kommentit on poistettu käytöstä asetuksista.",
"disabled": "Lataaja on poistanut kommentit käytöstä."
},
"info": {
"page_not_found": "Sivua ei löydy",
"copied": "Kopioitu!",
"cannot_copy": "Ei voi kopioida!",
"local_storage": "Tämä toiminto vaatii localStorage, ovatko evästeet käytössä?"
}
}

View file

@ -12,7 +12,8 @@
"instance": "Instance",
"player": "Lecteur",
"livestreams": "Diffusions en direct",
"channels": "Chaînes"
"channels": "Chaînes",
"bookmarks": "Marque-pages"
},
"actions": {
"subscribe": "S'abonner - {count}",
@ -114,7 +115,17 @@
"reply_count": "{count} réponses",
"minimize_comments_default": "Minimiser les commentaires par défaut",
"minimize_comments": "Minimiser les commentaires",
"show_watch_on_youtube": "Afficher le bouton Regarder sur YouTube"
"show_watch_on_youtube": "Afficher le bouton Regarder sur YouTube",
"minimize_chapters_default": "Minimiser les chapitres par défaut",
"no_valid_playlists": "Le fichier ne contient pas de listes de lecture valides !",
"bookmark_playlist": "Marque-page",
"playlist_bookmarked": "Dans les marque-pages",
"with_playlist": "Partager avec la liste de lecture",
"skip_button_only": "Afficher le bouton de saut",
"skip_automatically": "Automatiquement",
"min_segment_length": "Longueur minimale du segment (en secondes)",
"skip_segment": "Sauter le segment",
"show_less": "Afficher moins"
},
"player": {
"watch_on": "Regarder sur {0}"
@ -127,7 +138,9 @@
"ratings_disabled": "Évaluations désactivées",
"chapters": "Chapitres",
"live": "{0} en direct",
"shorts": "Courtes"
"shorts": "Courtes",
"all": "Tout",
"category": "Catégorie"
},
"preferences": {
"ssl_score": "Score SSL",
@ -169,6 +182,8 @@
"preferences_note": "Remarque : les préférences sont enregistrées dans la mémoire locale de votre navigateur. La suppression des données de votre navigateur les réinitialisera.",
"page_not_found": "Page non trouvée",
"copied": "Copié !",
"cannot_copy": "Impossible de copier !"
"cannot_copy": "Impossible de copier !",
"local_storage": "Cette action nécessite localStorage, les cookies sont-ils activés ?",
"register_no_email_note": "Il n'est pas recommandé d'utiliser une adresse courriel omme nom d'utilisateur. Continuer quand même ?"
}
}

View file

@ -12,7 +12,8 @@
"playlists": "רשימות נגינה",
"instance": "עותק",
"livestreams": "שידורים חיים",
"channels": "ערוצים"
"channels": "ערוצים",
"bookmarks": "סימניות"
},
"player": {
"watch_on": "לצפות ב־{0}"
@ -52,7 +53,7 @@
"instances_list": "רשימת עותקים",
"enabled_codecs": "מפענחים פעילים (מגוון)",
"instance_selection": "בחירת עותק",
"show_more": "להציג עוד",
"show_more": "להציג יותר",
"yes": "כן",
"no": "לא",
"export_to_json": "ייצוא ל־JSON",
@ -99,7 +100,7 @@
"disable_lbry": "השבתת הזרמה עם LBRY",
"enable_lbry_proxy": "הפעלת מתווך ל־LBRY",
"view_ssl_score": "הצגת דירוג SSL",
"search": "חיפוש",
"search": "חיפוש (Ctrl+K)",
"loop_this_video": "ניגון הסרטון בלולאה",
"minimize_recommendations": "מזעור המלצות",
"rename_playlist": "שינוי שם רשימת נגינה",
@ -116,7 +117,20 @@
"instance_donations": "תרומות להפעלה",
"reply_count": "{count} תגובות",
"minimize_comments_default": "צמצום הערות כברירת מחדל",
"minimize_comments": "צמצום הערות"
"minimize_comments": "צמצום הערות",
"minimize_chapters_default": "מזעור הפרקים כברירת מחדל",
"show_watch_on_youtube": "הצגת כפתור לצפייה ב־YouTube",
"no_valid_playlists": "הקובץ לא מכיל רשימות נגינה תקפות!",
"with_playlist": "שיתוף עם רשימת נגינה",
"playlist_bookmarked": "נוסף לסימניות",
"bookmark_playlist": "סימנייה",
"skip_button_only": "הצגת כפתור דילוג",
"min_segment_length": "אורך מקטע מזערי (בשניות)",
"skip_segment": "דילוג על מקטע",
"skip_automatically": "אוטומטית",
"show_less": "להציג פחות",
"autoplay_next_countdown": "ספירה לאחור כברירת מחדל עד לסרטון הבא (בשניות)",
"dismiss": "התעלמות"
},
"comment": {
"pinned_by": "ננעץ על ידי {author}",
@ -145,7 +159,9 @@
"ratings_disabled": "הדירוגים מושבתים",
"chapters": "פרקים",
"live": "{0} בשידור חי",
"shorts": "קצרצרים"
"shorts": "קצרצרים",
"all": "הכול",
"category": "קטגוריה"
},
"search": {
"did_you_mean": "האם התכוונת לביטוי {0}?",
@ -162,7 +178,10 @@
"preferences_note": "לתשומת לבך: ההעדפות נשמרות באחסון המקומי של הדפדפן שלך. מחיקת נתוני הדפדפן שלך תאפס אותם.",
"page_not_found": "העמוד לא נמצא",
"copied": "הועתק!",
"cannot_copy": "לא ניתן להעתיק!"
"cannot_copy": "לא ניתן להעתיק!",
"local_storage": "פעולה זו דורשת אחסון מקומי (localStorage), האם עוגיות פעילות?",
"register_no_email_note": "לא מומלץ להשתמש בכתובת דוא״ל כשם משתמש. להמשיך בכל זאת?",
"next_video_countdown": "הסרטון הבא יתנגן בעוד {0} שניות"
},
"subscriptions": {
"subscribed_channels_count": "נרשמת אל: {0}"

View file

@ -7,7 +7,11 @@
"preferences": "प्राथमिकताएँ",
"subscriptions": "सदस्यता",
"feed": "फ़ीड",
"playlists": "प्लेलिस्ट"
"playlists": "प्लेलिस्ट",
"livestreams": "लाइव स्ट्रीम",
"channels": "चैनल",
"player": "चालक",
"account": "खाता"
},
"actions": {
"subscribe": "सदस्यता लें - {count}",
@ -64,7 +68,11 @@
"create_playlist": "प्लेलिस्ट बनायें",
"select_playlist": "एक प्लेलिस्ट चुनें",
"please_select_playlist": "कृपया एक प्लेलिस्ट चुनें",
"delete_playlist": "प्लेलिस्ट हटाएं"
"delete_playlist": "प्लेलिस्ट हटाएं",
"enable_sponsorblock": "विज्ञापन प्रतिबंध करें",
"default_homepage": "स्वतः निर्धारित मुख्यपृष्ठ",
"sort_by": "वर्गीकरण:",
"skip_automatically": "स्वतः"
},
"video": {
"views": "{views} बार देखा गया",

View file

@ -4,10 +4,12 @@
"watched": "Gledano",
"views": "{views} gledanja",
"videos": "Videa",
"ratings_disabled": "Ocjenjivanje isključeno",
"ratings_disabled": "Ocjene su isključene",
"chapters": "Poglavlja",
"live": "{0} uživo",
"shorts": "Kratka videa"
"shorts": "Kratka videa",
"all": "Sva",
"category": "Kategorija"
},
"preferences": {
"ssl_score": "SSL ocjena",
@ -20,13 +22,13 @@
},
"comment": {
"pinned_by": "Prikvačio korisnik {author}",
"disabled": "Prijenosnik onemogućuje komentare.",
"loading": "Učitavanje komentara",
"disabled": "Prijenosnik je isključio komentare.",
"loading": "Učitavanje komentara...",
"user_disabled": "Komentari su isključeni u postavkama."
},
"actions": {
"enable_lbry_proxy": "Uključi proxy za LBRY",
"disable_lbry": "Onemogući LBRY za prijenos",
"disable_lbry": "Isključi LBRY za prijenos",
"minimize_description_default": "Standardno sakrij opis",
"minimize_description": "Sakrij opis",
"show_description": "Prikaži opis",
@ -41,7 +43,7 @@
"yes": "Da",
"show_more": "Prikaži više",
"instance_selection": "Izbor instance",
"enabled_codecs": "Uključeni kodeki (višestruki)",
"enabled_codecs": "Uključeni kodeki (moguće je odabrati nekoliko kodeka)",
"instances_list": "Popis instanci",
"language_selection": "Izbor jezika",
"store_watch_history": "Spremi povijest gledanja",
@ -69,28 +71,28 @@
"view_subscriptions": "Pogledaj pretplate",
"unsubscribe": "Otkaži pretplatu {count}",
"subscribe": "Pretplati se {count}",
"skip_interaction": "Preskoči podsjetnik za interakciju (zahtijeva pretplatu)",
"skip_interaction": "Preskoči podsjetnik za interakciju (pretplata)",
"skip_outro": "Preskoči odjavnu špicu",
"skip_intro": "Preskoči pauzu i uvodnu animaciju",
"skip_sponsors": "Preskoči sponzore",
"enable_sponsorblock": "Uključi blok sponsora",
"loading": "Učitavanje…",
"filter": "Filtar",
"search": "Pretraga",
"search": "Pretraga (Ctrl+K)",
"view_ssl_score": "Pogledaj SSL ocjenu",
"hide_replies": "Sakrij odgovore",
"load_more_replies": "Prikaži više odgovora",
"clear_history": "Obriši povijest",
"skip_highlight": "Preskoči isticanje",
"skip_filler_tangent": "Preskoči nebitne međudijelove",
"delete_playlist_confirm": "Izbrisati ovu playlistu?",
"remove_from_playlist": "Ukloni iz playliste",
"create_playlist": "Stvori playlistu",
"delete_playlist": "Izbriši playlistu",
"add_to_playlist": "Dodaj u playlistu",
"select_playlist": "Odaberi playlistu",
"please_select_playlist": "Odaberi playlistu",
"delete_playlist_video_confirm": "Ukloniti video iz playliste?",
"skip_filler_tangent": "Preskoči prazne umetke",
"delete_playlist_confirm": "Izbrisati ovaj popis snimaka?",
"remove_from_playlist": "Ukloni iz popisa snimaka",
"create_playlist": "Stvori popis snimaka",
"delete_playlist": "Izbriši popis snimaka",
"add_to_playlist": "Dodaj u popis snimaka",
"select_playlist": "Odaberi popis snimaka",
"please_select_playlist": "Odaberi popis snimaka",
"delete_playlist_video_confirm": "Ukloniti video iz popisa snimaka?",
"show_markers": "Prikaži oznake na playeru",
"delete_account": "Izbriši račun",
"logout": "Odjavi se s ovog uređaja",
@ -98,7 +100,7 @@
"invalidate_session": "Odjavi sve uređaje",
"different_auth_instance": "Koristi drugu instancu za autentifikaciju",
"instance_auth_selection": "Odabir instance autentifikacije",
"clone_playlist": "Dupliciraj playlistu",
"clone_playlist": "Dupliciraj popis snimaka",
"clone_playlist_success": "Dupliciranje uspjelo!",
"download_as_txt": "Preuzmi kao .txt",
"reset_preferences": "Resetiraj postavke",
@ -117,14 +119,26 @@
"show_chapters": "Poglavlja",
"documentation": "Dokumentacija",
"source_code": "Izvorni kod",
"instance_donations": "Donacije instace",
"instance_donations": "Donacije instance",
"store_search_history": "Spremi povijest pretrage",
"hide_watched": "Sakrij gledana videa u novostima",
"status_page": "Stanje",
"reply_count": "{count} odgovora",
"minimize_comments_default": "Standardno sakrij komentare",
"minimize_comments": "Sakrij komentare",
"show_watch_on_youtube": "Prikaži gumb „Gledaj na YouTubeu”"
"show_watch_on_youtube": "Prikaži gumb „Gledaj na YouTubeu”",
"minimize_chapters_default": "Standardno sakrij poglavlja",
"no_valid_playlists": "Datoteka ne sadrži ispravne popise snimaka!",
"with_playlist": "Dijeli s popisom snimaka",
"playlist_bookmarked": "Zabilježeno",
"bookmark_playlist": "Zabilježi",
"skip_button_only": "Prikaži gumb za preskakanje",
"skip_automatically": "Automatski",
"skip_segment": "Preskoči segment",
"min_segment_length": "Najmanja duljina segmenta (u sekundama)",
"show_less": "Prikaži manje",
"autoplay_next_countdown": "Standardno odbrojavanje do sljedećeg videa (u sekundama)",
"dismiss": "Odbaci"
},
"player": {
"watch_on": "Gledaj na {0}"
@ -137,12 +151,13 @@
"register": "Registracija",
"login": "Prijava",
"trending": "U trendu",
"playlists": "Playliste",
"playlists": "Popisi snimaka",
"account": "Račun",
"instance": "Instanca",
"player": "Player",
"channels": "Kanali",
"livestreams": "Prijenosi uživo"
"livestreams": "Prijenosi uživo",
"bookmarks": "Zabilješke"
},
"login": {
"password": "Lozinka",
@ -153,14 +168,14 @@
"all": "YouTube: Sve",
"videos": "YouTube: Videa",
"channels": "YouTube: Kanali",
"playlists": "YouTube: Playliste",
"playlists": "YouTube: Popisi snimaka",
"music_songs": "YT Music: Pjesme",
"music_videos": "YT Music: Videa",
"music_albums": "YT Music: Albumi",
"music_playlists": "YT Music: Playliste"
"music_playlists": "YT Music: Popisi snimaka"
},
"subscriptions": {
"subscribed_channels_count": "Pretplata na: {0}"
"subscribed_channels_count": "Broj pretplata: {0}"
},
"information": {
"preferences_note": "Napomena: postavke se spremaju u lokalno spremište preglednika. Brisanje podataka preglednika resetira postavke."
@ -169,6 +184,9 @@
"preferences_note": "Napomena: postavke se spremaju u lokalno spremište tvog preglednika. Brisanje podataka preglednika će ih resetirati.",
"page_not_found": "Stranica nije pronađena",
"copied": "Kopirano!",
"cannot_copy": "Nije moguće kopirati!"
"cannot_copy": "Nije moguće kopirati!",
"local_storage": "Ova radnja zahtijeva lokalno spremište. Jesu li kolačići uključeni?",
"register_no_email_note": "Korištenje e-mail adrese kao korisničkog imena se ne preporučuje. Svejedno nastaviti?",
"next_video_countdown": "Reprodukcija sljedećeg videa za {0} s"
}
}

View file

@ -8,7 +8,11 @@
"subscriptions": "Feliratkozások",
"playlists": "Lejátszási listák",
"trending": "Felkapott",
"account": "Fiók"
"account": "Fiók",
"player": "Lejátszó",
"instance": "Szerver",
"livestreams": "Élő adások",
"channels": "Csatornák"
},
"actions": {
"subscribe": "Feliratkozás - {count}",
@ -51,7 +55,7 @@
"instance_selection": "Példány kiválasztása",
"skip_filler_tangent": "Témától eltérő töltelék/viccek",
"loop_this_video": "Videó ismétlése",
"donations": "Támogatások",
"donations": "Fejlesztési támogatások",
"minimize_description": "Leírás minimalizálása",
"show_recommendations": "Javaslatok megjelenítése",
"enable_lbry_proxy": "Proxy engedélyezése a LBRY számára",
@ -85,7 +89,35 @@
"different_auth_instance": "Másik példány használata a hitelesítéshez",
"instance_auth_selection": "Autentikációs példány kiválasztása",
"clone_playlist": "Lejátszási lista klónozása",
"clone_playlist_success": "Sikeresen klónozva!"
"clone_playlist_success": "Sikeresen klónozva!",
"reset_preferences": "Alaphelyzetbe állítás",
"restore_preferences": "Beállítások betöltése fájlból",
"rename_playlist": "Átnevez",
"instance_donations": "Szerver adományozások",
"piped_link": "Piped link",
"time_code": "Idő kód (másodpercekben)",
"show_chapters": "Fejezetek",
"download_as_txt": "Letöltés szövegdokumentumként",
"source_code": "A szoftver kódja",
"reply_count": "{count} hozzászólások",
"documentation": "Dokumentáció",
"minimize_chapters_default": "Mindig tüntesd el a fejezeteket",
"hide_watched": "Ne mutassa a látott videókat a felíratkozásoknál",
"show_watch_on_youtube": "Mutasd a \"Lejátszás Youtube-on\" gombot",
"confirm_reset_preferences": "Biztos alaphelyzetbe állítod?",
"backup_preferences": "Beállítások mentése",
"new_playlist_name": "Új lejátszási lista név",
"share": "Megosztás",
"with_timecode": "Megosztás & videó kezdés ettől a ponttól",
"store_search_history": "Mentse a keresési előzményeket",
"follow_link": "Követések link",
"copy_link": "Link másolása",
"status_page": "Státusz",
"no_valid_playlists": "Nincs a fájlban egy valós lejátszási lista se!",
"with_playlist": "Megosztás lejátszási listával",
"minimize_comments_default": "Mindig tüntesd el a kommenteket",
"minimize_comments": "Kommentek eltüntetése",
"back_to_home": "Vissza a főoldalra"
},
"video": {
"ratings_disabled": "Értékelések Letiltva",
@ -129,5 +161,15 @@
"disabled": "A hozzászólásokat a feltöltő letiltotta.",
"user_disabled": "A beállításoknál a megjegyzések le vannak tiltva.",
"loading": "Kommentek betöltése..."
},
"subscriptions": {
"subscribed_channels_count": "Feliratkozva: {0} csatornára"
},
"info": {
"preferences_note": "Figyelem: A beállításaid a böngésződ tárhelyére vannak mentve. Ha törlöd őket el fognak tűnni a beállításaid.",
"copied": "Másolva!",
"local_storage": "Ennek a beállításnak szüksége van a \"lokális tárhely\" funkcióra, be vannak a sütik kapcsolva?",
"cannot_copy": "Nem lehet másolni!",
"page_not_found": "Oldnal nem található"
}
}

View file

@ -12,7 +12,8 @@
"account": "Akun",
"player": "Pemain",
"livestreams": "Siaran Langsung",
"channels": "Saluran"
"channels": "Saluran",
"bookmarks": "Markah"
},
"player": {
"watch_on": "Tonton di {0}"
@ -49,7 +50,7 @@
"instances_list": "Daftar Instansi",
"enabled_codecs": "Kodek yang Diaktifkan (Beberapa)",
"instance_selection": "Pemilihan Instansi",
"show_more": "Tampilkan Lebih Banyak",
"show_more": "Tampilkan lebih banyak",
"yes": "Iya",
"no": "Tidak",
"import_from_json": "Impor dari JSON/CSV",
@ -63,7 +64,7 @@
"disable_lbry": "Nonaktifkan LBRY untuk Streaming",
"enable_lbry_proxy": "Aktifkan Proksi untuk LBRY",
"view_ssl_score": "Tampilkan Skor SSL",
"search": "Telusuri",
"search": "Telusuri (Ctrl+K)",
"filter": "Saring",
"loading": "Memuat...",
"clear_history": "Hapus Riwayat",
@ -111,12 +112,25 @@
"store_search_history": "Simpan riwayat pencarian",
"documentation": "Dokumentasi",
"instance_donations": "Donasi instansi",
"hide_watched": "Sembunyikan video yang telah ditonton di umpan",
"hide_watched": "Sembunyikan video yang sudah ditonton dari umpan",
"status_page": "Status",
"source_code": "Kode sumber",
"reply_count": "{count} balasan",
"minimize_comments_default": "Kecilkan Komentar secara bawaan",
"minimize_comments": "Kecilkan Komentar"
"minimize_comments": "Kecilkan Komentar",
"show_watch_on_youtube": "Tampilkan tombol Tonton di YouTube",
"minimize_chapters_default": "Kecilkan Bab secara bawaan",
"no_valid_playlists": "Berkas ini tidak berisi daftar putar yang valid!",
"with_playlist": "Bagikan dengan daftar putar",
"playlist_bookmarked": "Dimarkahi",
"bookmark_playlist": "Markahi",
"skip_button_only": "Tampilkan tombol lewati",
"skip_automatically": "Secara otomatis",
"min_segment_length": "Panjang Segmen Minimum (dalam detik)",
"skip_segment": "Lewati Segmen",
"show_less": "Tampilkan lebih sedikit",
"autoplay_next_countdown": "Hitungan mundur bawaan sebelum video berikutnya (dalam detik)",
"dismiss": "Abaikan"
},
"comment": {
"pinned_by": "Dipasangi pin oleh {author}",
@ -128,7 +142,7 @@
"instance_name": "Nama Instansi",
"ssl_score": "Skor SSL",
"instance_locations": "Lokasi Instansi",
"has_cdn": "Mempunyai CDN?",
"has_cdn": "Memakai CDN?",
"up_to_date": "Sudah terkini?",
"version": "Versi",
"registered_users": "Pengguna Terdaftar"
@ -145,7 +159,9 @@
"ratings_disabled": "Penilaian Dinonaktifkan",
"chapters": "Bagian",
"live": "{0} Langsung",
"shorts": "Shorts"
"shorts": "Shorts",
"all": "Semua",
"category": "Kategori"
},
"search": {
"did_you_mean": "Apakah Anda bermaksud: {0}?",
@ -168,6 +184,9 @@
"page_not_found": "Laman tidak ditemukan",
"preferences_note": "Catatan: preferensi disimpan dalam penyimpanan lokal peramban Anda. Menghapus data peramban Anda akan mengatur ulang.",
"copied": "Disalin!",
"cannot_copy": "Tidak dapat menyalin!"
"cannot_copy": "Tidak dapat menyalin!",
"local_storage": "Tindakan ini membutuhkan localStorage, apakah kuki diaktifkan?",
"register_no_email_note": "Menggunakan surel sebagai nama pengguna tidak disarankan. Lanjut?",
"next_video_countdown": "Memutar video berikutnya dalam {0} detik"
}
}

View file

@ -10,7 +10,9 @@
"playlists": "Spilunarlistar",
"player": "Spilari",
"account": "Reikningur",
"instance": "Tilvik"
"instance": "Tilvik",
"livestreams": "Útsendingar í beinni",
"channels": "Rásir"
},
"actions": {
"sort_by": "Raða eftir:",
@ -111,7 +113,9 @@
"show_chapters": "Kaflar",
"reply_count": "{count} svör",
"minimize_comments_default": "Fela ummæli sjálfgefið",
"minimize_comments": "Fela ummæli"
"minimize_comments": "Fela ummæli",
"show_watch_on_youtube": "Sýna hnapp til að horfa á YouTube",
"minimize_chapters_default": "Lágmarka kafla sjálfgefið"
},
"player": {
"watch_on": "Horfa á {0}"

View file

@ -51,7 +51,7 @@
"instance_selection": "Selezione dell'istanza",
"loading": "Caricamento…",
"filter": "Filtra",
"search": "Cerca",
"search": "Cerca (Ctrl+K)",
"view_ssl_score": "Visualizza il punteggio SSL",
"clear_history": "Cancella la cronologia",
"load_more_replies": "Carica più risposte",
@ -94,7 +94,23 @@
"status_page": "Stato",
"documentation": "Documentazione",
"source_code": "Codice sorgente",
"reply_count": "{count} risposte"
"reply_count": "{count} risposte",
"hide_watched": "Nascondi i video guardati nel feed",
"show_watch_on_youtube": "Mostra il bottone guarda su Youtube",
"instance_donations": "Donazioni istanza",
"minimize_comments_default": "Minimizza i commenti per impostazione predefinita",
"minimize_comments": "Minimizza i commenti",
"minimize_chapters_default": "Minimizza i capitoli per impostazione predefinita",
"no_valid_playlists": "Il file non contiene playlist valide!",
"bookmark_playlist": "Segnalibro",
"with_playlist": "Condividi con la playlist",
"playlist_bookmarked": "Nei segnalibri",
"min_segment_length": "Lunghezza minima del segmento (in secondi)",
"skip_automatically": "Automaticamente",
"skip_button_only": "Mostra pulsante di salto",
"skip_segment": "Salta segmento",
"show_less": "Mostra meno",
"autoplay_next_countdown": "Conto alla rovescia predefinito prima del video successivo (in secondi)"
},
"player": {
"watch_on": "Guarda su {0}"
@ -112,7 +128,8 @@
"instance": "Istanza",
"player": "Riproduttore",
"livestreams": "Streaming live",
"channels": "Canali"
"channels": "Canali",
"bookmarks": "Segnalibri"
},
"video": {
"sponsor_segments": "Segmenti sponsor",
@ -122,7 +139,9 @@
"ratings_disabled": "Valutazioni disabilitate",
"live": "{0} Diretta",
"chapters": "Capitoli",
"shorts": "Short"
"shorts": "Short",
"all": "Tutti",
"category": "Categoria"
},
"preferences": {
"ssl_score": "Valutazione SSL",
@ -164,6 +183,9 @@
"page_not_found": "Pagina non trovata",
"preferences_note": "Nota: le preferenze sono salvate nella memoria locale del tuo browser. L'eliminazione dei dati del tuo browser le ripristinerà.",
"copied": "Copiato!",
"cannot_copy": "Impossibile copiare!"
"cannot_copy": "Impossibile copiare!",
"local_storage": "Questa azione richiede localStorage, i cookie sono abilitati?",
"register_no_email_note": "L'utilizzo di un indirizzo e-mail come nome utente è sconsigliato. Continuare comunque?",
"next_video_countdown": "Riproduzione prossimo video tra {0}s"
}
}

View file

@ -1,13 +1,19 @@
{
"titles": {
"trending": "トレンド",
"trending": "急上昇",
"login": "ログイン",
"register": "新規登録",
"feed": "フィード",
"preferences": "設定",
"history": "履歴",
"subscriptions": "サブスクリプション",
"playlists": "再生リスト"
"subscriptions": "登録チャンネル",
"playlists": "再生リスト",
"account": "アカウント",
"player": "プレイヤー",
"instance": "インスタンス",
"channels": "チャンネル",
"livestreams": "ライブ配信",
"bookmarks": "ブックマーク"
},
"player": {
"watch_on": "{0}で見る"
@ -15,35 +21,35 @@
"actions": {
"subscribe": "チャンネル登録 - {count}",
"unsubscribe": "登録解除 - {count}",
"view_subscriptions": "サブスクリプションを見る",
"view_subscriptions": "登録チャンネルを見る",
"sort_by": "表示順:",
"most_recent": "新しい順",
"least_recent": "古い順",
"channel_name_asc": "チャンネル名",
"channel_name_desc": "チャンネル名逆順",
"channel_name_asc": "チャンネル名 (AからZ)",
"channel_name_desc": "チャンネル名 (ZからA)",
"back": "戻る",
"uses_api_from": "API使用元 ",
"enable_sponsorblock": "広告ブロックをオン",
"enable_sponsorblock": "SponsorBlock を有効化",
"skip_sponsors": "広告をスキップ",
"skip_intro": "イントロ部分をスキップする",
"skip_outro": "クレジット部分をスキップする",
"skip_preview": "Skip プレビュー・要約をスキップ",
"skip_interaction": "自己宣伝シーンをスキップ",
"skip_self_promo": "プロモーションをスキップ",
"skip_non_music": "音楽以外のセクションをスキップ",
"skip_intro": "休止時間/導入アニメをスキップ",
"skip_outro": "終了シーン/クレジットをスキップ",
"skip_preview": "プレビュー/要約をスキップ",
"skip_interaction": "チャンネル登録など操作を求める自己宣伝をスキップ",
"skip_self_promo": "無報酬/自己の宣伝をスキップ",
"skip_non_music": "音楽: 非音楽部分をスキップ",
"theme": "テーマ",
"auto": "自動",
"dark": "ダークテーマ",
"light": "ライトテーマ",
"autoplay_video": "自動再生",
"dark": "ダーク",
"light": "ライト",
"autoplay_video": "動画を自動再生",
"audio_only": "音声のみ",
"default_quality": "デフォルトの画質",
"buffering_goal": "バッファリング目標値(秒単位)",
"default_quality": "標準の画質",
"buffering_goal": "バッファリング目標値 (秒)",
"country_selection": "国の選択",
"default_homepage": "デフォルトのホームページ",
"default_homepage": "ホームに表示するページ",
"show_comments": "コメントを表示",
"minimize_description_default": "デフォルトで詳細を最小化",
"store_watch_history": "視聴履歴を記録する",
"minimize_description_default": "最初から説明を最小化",
"store_watch_history": "再生履歴を保存する",
"language_selection": "言語の選択",
"instances_list": "インスタンス一覧",
"enabled_codecs": "コーデックの有効化 (複数選択)",
@ -51,70 +57,130 @@
"show_more": "もっと見る",
"yes": "はい",
"no": "いいえ",
"export_to_json": "JSONファイルに出力",
"import_from_json": "JSON/CSVファイルを読み込む",
"loop_this_video": "ループ再生",
"auto_play_next_video": "自動再生",
"donations": "寄付",
"minimize_description": "最小化",
"show_description": "詳細",
"export_to_json": "JSONに出力",
"import_from_json": "JSON/CSVを読み込む",
"loop_this_video": "この動画をループ再生",
"auto_play_next_video": "次の動画を自動再生",
"donations": "開発者に寄付",
"minimize_description": "説明を最小化",
"show_description": "説明を表示",
"minimize_recommendations": "おすすめを最小化",
"show_recommendations": "おすすめを見る",
"disable_lbry": "ストリーミングのLBRYを無効化",
"enable_lbry_proxy": "LBRYプロキシをオン",
"view_ssl_score": "SSLスコアを見る",
"search": "検索",
"view_ssl_score": "SSLの評価を表示",
"search": "検索 (Ctrl+K)",
"filter": "フィルター",
"loading": "読中…",
"clear_history": "履歴を消去",
"loading": "読み込み中…",
"clear_history": "再生履歴を削除",
"hide_replies": "返信を非表示",
"load_more_replies": "もっと見る",
"skip_filler_tangent": "無関係なコンテンツをスキップ",
"skip_highlight": "要点をスキップ",
"load_more_replies": "返信をもっと見る",
"skip_filler_tangent": "無関係な談話をスキップ",
"skip_highlight": "ハイライトをスキップ",
"add_to_playlist": "再生リストに追加",
"create_playlist": "新しい再生リストを作成",
"create_playlist": "再生リストを作成",
"remove_from_playlist": "再生リストから削除",
"delete_playlist_video_confirm": "再生リストからこの動画を削除してもよろしいですか?",
"delete_playlist_video_confirm": "再生リストからこの動画を削除しすか?",
"delete_playlist": "再生リストを削除",
"please_select_playlist": "再生リストを選択してください",
"show_markers": "プレーヤーにマーカーを表示",
"show_markers": "プレイヤーに目印の区切りを表示",
"select_playlist": "再生リストを選択",
"delete_playlist_confirm": "再生リストを削除してもよろしいですか?"
"delete_playlist_confirm": "再生リストを削除しますか?",
"delete_account": "アカウントを削除する",
"store_search_history": "検索履歴を保存する",
"show_chapters": "チャプター",
"status_page": "状態",
"source_code": "ソースコード",
"instance_donations": "インスタンスに寄付",
"minimize_comments": "コメントを最小化",
"share": "共有",
"with_timecode": "タイムコード付きで共有",
"different_auth_instance": "認証に別のインスタンスを使う",
"download_as_txt": ".txtでダウンロード",
"logout": "このデバイスでログアウト",
"minimize_recommendations_default": "最初からおすすめを最小化",
"hide_watched": "再生済みの動画をフィードに表示しない",
"minimize_chapters_default": "最初からチャプターを最小化",
"show_watch_on_youtube": "「YouTubeで見る」ボタンを表示する",
"invalidate_session": "すべてのデバイスでログアウトする",
"instance_auth_selection": "認証インスタンスの選択",
"clone_playlist_success": "複製に成功しました!",
"backup_preferences": "設定をバックアップ",
"restore_preferences": "設定を復元",
"back_to_home": "ホームに戻る",
"copy_link": "リンクをコピー",
"time_code": "タイムコード (秒)",
"documentation": "ドキュメント",
"reset_preferences": "設定を初期化",
"confirm_reset_preferences": "設定をリセットしますか?",
"rename_playlist": "再生リスト名を変更する",
"piped_link": "Pipedリンク",
"new_playlist_name": "新しい再生リスト名",
"follow_link": "リンクに従う",
"reply_count": "{count} 件の返信",
"clone_playlist": "再生リストを複製",
"minimize_comments_default": "最初からコメントを最小化",
"no_valid_playlists": "このファイルは有効な再生リストではありません!",
"playlist_bookmarked": "ブックマーク完了",
"bookmark_playlist": "ブックマーク",
"with_playlist": "再生リストで共有",
"skip_automatically": "自動",
"skip_button_only": "スキップボタン表示",
"skip_segment": "ここをスキップ",
"min_segment_length": "最小の区切りの長さ (秒)",
"show_less": "少なく見る"
},
"comment": {
"pinned_by": "固定されたコメント {author}"
"pinned_by": "{author} によって固定",
"loading": "コメントを読み込み中...",
"user_disabled": "コメントは設定で無効になっています。",
"disabled": "コメントは投稿者によって無効化されています。"
},
"preferences": {
"instance_name": "インスタンス名",
"instance_locations": "インスタンスの場所",
"has_cdn": "CDNの有無",
"ssl_score": "SSLスコア",
"registered_users": "登録済みユーザー",
"ssl_score": "SSLの評価",
"registered_users": "登録ユーザー",
"version": "バージョン",
"up_to_date": "最新か否か"
"up_to_date": "最新"
},
"login": {
"username": "ユーザー名",
"password": "パスワード"
},
"video": {
"videos": "ビデオ",
"views": "{views} 視聴",
"watched": "視聴済み",
"videos": "動画",
"views": "{views} 回再生",
"watched": "再生済み",
"sponsor_segments": "スポンサーによる広告",
"ratings_disabled": "評価は無効化されています",
"chapters": "チャプター",
"live": "{0}ライブ"
"live": "{0} ライブ配信",
"shorts": "ショート",
"all": "すべて",
"category": "分類"
},
"search": {
"did_you_mean": "もしかして: {0}?",
"all": "Youtube: 全て",
"videos": "Youtube: ビデオ",
"channels": "Youtube: チャンネル",
"playlists": "Youtube: 再生リスト",
"did_you_mean": "もしかして: {0}",
"all": "YouTube: すべて",
"videos": "YouTube: 動画",
"channels": "YouTube: チャンネル",
"playlists": "YouTube: 再生リスト",
"music_songs": "YT Music: 音楽",
"music_videos": "YT Music: ビデオ",
"music_videos": "YT Music: 動画",
"music_albums": "YT Music: アルバム",
"music_playlists": "YT Music: 再生リスト"
},
"info": {
"page_not_found": "ページが見つかりません",
"copied": "コピーしました!",
"cannot_copy": "コピーできません!",
"preferences_note": "注意: 設定は、お使いのブラウザの保存領域に保存されます。ブラウザのデータを削除すると初期化されます。",
"local_storage": "この操作にはlocalStorageが必要です。Cookieは有効ですか",
"register_no_email_note": "Eメールアドレスをユーザー名として使用することは推奨されていません。それでも続行しますか"
},
"subscriptions": {
"subscribed_channels_count": "チャンネル登録: {0}"
}
}

175
src/locales/kab.json Normal file
View file

@ -0,0 +1,175 @@
{
"titles": {
"login": "Aseqdac",
"register": "Jerred",
"preferences": "Ismenyifen",
"history": "Amazray",
"subscriptions": "Ijerriden",
"account": "Amiḍan",
"channels": "Ibuda",
"playlists": "Tabdert n tɣuri",
"instance": "Tummant",
"player": "Ameɣri",
"livestreams": "Azuzer usrid",
"bookmarks": "Ticraḍ",
"feed": "Amultaɣ",
"trending": "Tiddin"
},
"actions": {
"dark": "Ubrik",
"language_selection": "Afran n tutlayt",
"yes": "Ih",
"no": "Uhu",
"search": "Nadi",
"filter": "Imsizdeg",
"loading": "Asali...",
"delete_playlist_confirm": "Kkes tabdart-a n tɣuri?",
"share": "Bḍu",
"documentation": "Tasemlit",
"status_page": "État",
"least_recent": "N melmi kna",
"channel_name_desc": "Isem n ubadu (A-Z)",
"channel_name_asc": "Isem n ubadu (A-z)",
"back": "Tuɣalin",
"uses_api_from": "Isseqdac API n: ",
"enable_sponsorblock": "Rmed amsewḥal n udellel",
"skip_sponsors": "Zgel adellel",
"add_to_playlist": "Rnu ɣer tebdart n tɣuri",
"hide_replies": "Ffer tiririyin",
"load_more_replies": "Sali-d ugar n tririyin",
"remove_from_playlist": "Kkes seg tebdart n tɣuri",
"delete_playlist_video_confirm": "Kkes tavidyut seg tebdart n tɣuri?",
"create_playlist": "Rnu tabdart n tɣuri",
"subscribe": "Qqen - {count}",
"unsubscribe": "Sefsex tuqqna n - {count}",
"view_subscriptions": "Wali imuktaɣen",
"sort_by": "Semyizwer s:",
"most_recent": "Amaynut akk",
"theme": "Asentel",
"auto": "Awurman",
"delete_playlist": "Kkes tabdart n tɣuri",
"select_playlist": "Fren tabdart n tɣuri",
"please_select_playlist": "Ttxil-k·m fren tabdart n tɣuri",
"delete_account": "Kkes amiḍan",
"logout": "Ffeɣ seg yibenk-a",
"light": "D ameceɛlal",
"autoplay_video": "Taɣuri tawurmant n tvidyut",
"audio_only": "Ameslaw kan",
"default_quality": "Taɣara tuzwirt",
"auto_play_next_video": "Taɣuri tawurmant n tvidyut tuḍfirt",
"donations": "Tawsa i usnefli",
"disable_lbry": "Sens LBRY i usuddem",
"enable_lbry_proxy": "Rmed apṛuksi i LBRY",
"view_ssl_score": "Sken agmuḍ SSL",
"show_recommendations": "Sken iwellihen",
"clear_history": "Sfeḍ azray",
"copy_link": "Nɣel aseɣwen",
"time_code": "Tangalt n wakud (s tsinin)",
"show_more": "Sken ugar",
"export_to_json": "Sifeḍ ɣer JSON",
"minimize_comments": "Semẓi iwenniten",
"show_comments": "Sken iwenniten",
"minimize_description": "Semẓi aglam",
"show_description": "Sken aglam",
"minimize_recommendations": "Semẓi iwellihen",
"reply_count": "{count} tririyin",
"bookmark_playlist": "Tacreḍt",
"playlist_bookmarked": "Yettwacreḍ",
"instances_list": "Tabdart n tummanin",
"country_selection": "Afran n tmurt",
"default_homepage": "Asebter agejdan amezwer",
"instance_selection": "Afran n tummant",
"import_from_json": "Kter seg JSON/CSV",
"store_search_history": "Ḥrez azray n unadi",
"instance_donations": "Tawsa n tummant",
"source_code": "Tangalt taɣbalut",
"minimize_description_default": "Semẓi aglam s wudem amezwer",
"skip_highlight": "Zgel asebruraq",
"minimize_comments_default": "Semẓi iwenniten s wudem amezwer",
"store_watch_history": "Ḥrez azray n tmeẓriwt",
"loop_this_video": "Err tavidyut-a d taẓayert",
"minimize_recommendations_default": "Semẓi iwellihen s wudem amezwer",
"no_valid_playlists": "Afaylu ulac deg-s tibdarin n tɣuri timeɣta!",
"with_playlist": "Bḍu s tebdart n tɣuri",
"skip_preview": "Zgel Taskant/Agzul",
"skip_outro": "Zgel ismawen n taggara",
"minimize_chapters_default": "Semẓi ixfawen s wudem uzwir",
"confirm_reset_preferences": "D tidet tebɣiḍ ad twennzeḍ ismenyifen-ik•im?",
"backup_preferences": "Ḥrez ismenyifen",
"restore_preferences": "Err-d ismenyifen",
"back_to_home": "Uɣal ɣer ugejdan",
"rename_playlist": "Beddel isem i tebdart n tɣuri",
"follow_link": "Ḍfer aseɣwen",
"show_chapters": "Ixfawen",
"show_watch_on_youtube": "Sken taqeffalt Wali ɣef YouTube",
"reset_preferences": "Wennez ismenyifen",
"new_playlist_name": "Isem amaynut n tebdart n tɣuri",
"with_timecode": "Bḍu s tengalt n wakud",
"hide_watched": "Ffer tividyutin yemmeẓren deg usuddel",
"skip_interaction": "Zgel ismektiyen n umyigew (Multeɣ)",
"enabled_codecs": "Isettengal ttwaremden (aṭas)",
"buffering_goal": "Iswi n uḥraz akudan (s tsinin)",
"skip_non_music": "Zgel aẓawan: tafrant ur nelli d aẓawan",
"show_markers": "Sken ticraḍ ɣef umeɣri",
"instance_auth_selection": "Tafrant n tummant n usesteb",
"invalidate_session": "Ffeɣ seg meṛṛa ibenkan",
"different_auth_instance": "Seqdec tummant tayeḍ i usesteb",
"download_as_txt": "Sader am.txt",
"piped_link": "Aseɣwen n Piped",
"clone_playlist": "Tabdart n tɣuri yemtawan"
},
"preferences": {
"version": "Lqem",
"has_cdn": "Ɣur-s CDN?",
"registered_users": "Iseqdacen yettwajerrden",
"up_to_date": "D amaynut?",
"ssl_score": "Agmuḍ SSL",
"instance_name": "Isem n tummant",
"instance_locations": "Idgan n tummant"
},
"login": {
"username": "Nom d'utilisateur",
"password": "Awal n uɛeddi"
},
"video": {
"videos": "Tividyutin",
"views": "{views} tmeẓriwin",
"watched": "Yemẓer",
"shorts": "Tiwezlanin",
"live": "{0} srid",
"sponsor_segments": "Inegzumen n udellel",
"chapters": "Ixfawen",
"ratings_disabled": "Iktazalen ttwasensen"
},
"player": {
"watch_on": "Wali deg {0}"
},
"comment": {
"pinned_by": "Isenteḍ-itt {author}",
"loading": "Asali n yiwenniten...",
"disabled": "Ameskar issens iwenniten.",
"user_disabled": "Nsan yiwenniten deg Yiɣewwaren."
},
"search": {
"did_you_mean": "Tebɣiḍ ad d-tiniḍ: {0}?",
"music_playlists": "YT Music: Tibdarin n tɣuri",
"all": "YouTube: Akk",
"videos": "YouTube: Tividyutin",
"channels": "YouTube: Ibuda",
"music_videos": "YT Music: Tividyutin",
"playlists": "YouTube: Tibdarin n tɣuri",
"music_songs": "YT Music: Tizlatin",
"music_albums": "YT Music: Albumen"
},
"info": {
"copied": "Yettwanɣel!",
"page_not_found": "Ur yettwaf ara usebter",
"cannot_copy": "D awezɣi ad d-yettwanɣel!",
"register_no_email_note": "Aseqdec n yimayl am yisem n useqdac, ur yettusireg ara. Kemmel ɣas akken?",
"local_storage": "Tigawt-a aḥraz adigan, inagan n tuqqna remden?",
"preferences_note": "Tamawt: ismenyifen ttwaskelsen deg uklas adigan n yiminig-ik·im. Tukksa n yisefka yiminig-ik·im ad ten-iwennez."
},
"subscriptions": {
"subscribed_channels_count": "Imulteɣ ɣer: {0}"
}
}

View file

@ -73,12 +73,12 @@
"remove_from_playlist": "Pašalinti iš grojaraščio",
"confirm_reset_preferences": "Ar tikrai norite iš naujo nustatyti nuostatas?",
"reset_preferences": "Iš naujo nustatyti nuostatas",
"backup_preferences": "Atsarginės kopijos nuostatos",
"backup_preferences": "Atsarginė nuostatų kopija",
"source_code": "Pirminis kodas",
"documentation": "Dokumentacija",
"with_timecode": "Dalintis su laiko kodu",
"reply_count": "{count} atsakymų",
"show_chapters": "Skyriai",
"reply_count": "{count} atsakymai",
"show_chapters": "Skirsniai",
"piped_link": "Piped nuoroda",
"rename_playlist": "Pervardyti grojaraštį",
"follow_link": "Sekti nuorodą",
@ -99,7 +99,9 @@
"time_code": "Laiko kodas (sekundėmis)",
"minimize_comments_default": "Suskleisti komentarus automatiškai",
"minimize_comments": "Suskleisti komentarus",
"show_watch_on_youtube": "Rodyti mygtuką „Žiūrėti YouTube“"
"show_watch_on_youtube": "Rodyti mygtuką „Žiūrėti YouTube“",
"minimize_chapters_default": "Suskleisti skirsnius automatiškai",
"no_valid_playlists": "Faile nėra galiojančių grojaraščių!"
},
"player": {
"watch_on": "Žiūrėti per {0}"
@ -131,7 +133,7 @@
"comment": {
"pinned_by": "Prisegė {author}",
"loading": "Įkeliami komentarai...",
"disabled": "Komentarai yra išjungti įkėlėjo.",
"disabled": "Įkėlėjas išjungė komentarus.",
"user_disabled": "Komentarai yra išjungti nustatymuose."
},
"video": {
@ -140,7 +142,7 @@
"sponsor_segments": "Rėmėjų segmentai",
"watched": "Žiūrėta",
"ratings_disabled": "Įvertinimai išjungti",
"chapters": "Skyriai",
"chapters": "Skirsniai",
"live": "{0} tiesiogiai",
"shorts": "Trumpi filmukai"
},
@ -163,7 +165,8 @@
"copied": "Nukopijuota!",
"cannot_copy": "Negalima kopijuoti!",
"page_not_found": "Puslapis nerastas",
"preferences_note": "Pastaba: nuostatos išsaugomos vietinėje naršyklės atmintyje. Ištrynus naršyklės duomenis, jie bus nustatyti iš naujo."
"preferences_note": "Pastaba: nuostatos išsaugomos vietinėje naršyklės atmintyje. Ištrynus naršyklės duomenis, jos bus nustatytos iš naujo.",
"local_storage": "Šiam veiksmui reikia localStorage, ar slapukai įjungti?"
},
"subscriptions": {
"subscribed_channels_count": "Prenumeruojama: {0}"

View file

@ -1,7 +1,7 @@
{
"actions": {
"skip_sponsors": "Sponsors Overslaan",
"skip_outro": "Eindkaarten/Credits overslaan",
"skip_outro": "Eindkaarten/Credits Overslaan",
"add_to_playlist": "Aan Afspeellijst Toevoegen",
"sort_by": "Sorteer op:",
"buffering_goal": "Bufferdoel (in seconden)",
@ -20,7 +20,7 @@
"skip_self_promo": "Onbetaalde/Zelf-promotie Overslaan",
"skip_highlight": "Markering Overslaan",
"skip_interaction": "Interactieherinnering Overslaan (Abonneren)",
"show_more": "Toon Meer",
"show_more": "Toon meer",
"unsubscribe": "Afmelden - {count}",
"view_subscriptions": "Abonnementen Bekijken",
"enable_sponsorblock": "Sponsorblok Inschakelen",
@ -30,7 +30,7 @@
"light": "Licht",
"default_quality": "Standaard Kwaliteit",
"loop_this_video": "Deze Video Herhalen",
"donations": "Donaties",
"donations": "Ontwikkelingsdonaties",
"minimize_description": "Beschrijving Minimaliseren",
"show_description": "Toon Beschrijving",
"minimize_recommendations": "Aanbevelingen Minimaliseren",
@ -57,15 +57,59 @@
"auto_play_next_video": "Volgende Video Automatisch Afspelen",
"remove_from_playlist": "Uit Afspeellijst Verwijderen",
"select_playlist": "Selecteer een Afspeellijst",
"delete_playlist_confirm": "Weet u zeker dat u deze afspeellijst wilt verwijderen?",
"please_select_playlist": "Kies een afspeellijst a.u.b.",
"delete_playlist_confirm": "Deze afspeellijst verwijderen?",
"please_select_playlist": "Selecteer een afspeellijst alsjeblief",
"instance_selection": "Instantie Selectie",
"import_from_json": "Importeren uit JSON/CSV",
"clear_history": "Geschiedenis Wissen",
"load_more_replies": "Laad meer Antwoorden",
"delete_playlist_video_confirm": "Weet u zeker dat u deze video uit deze afspeellijst wilt verwijderen?",
"delete_playlist_video_confirm": "Video uit deze afspeellijst verwijderen?",
"create_playlist": "Afspeellijst Maken",
"delete_playlist": "Afspeellijst Verwijderen"
"delete_playlist": "Afspeellijst Verwijderen",
"show_markers": "Laat markeringen op speler zien",
"store_search_history": "Zoekgeschiedenis Opslaan",
"minimize_chapters_default": "Hoofdstukken Standaard Minimaliseren",
"show_watch_on_youtube": "Toon Bekijk op YouTube knop",
"restore_preferences": "Voorkeuren herstellen",
"with_timecode": "Delen met tijdcode",
"piped_link": "Piped link",
"follow_link": "Volg link",
"copy_link": "Link kopiëren",
"hide_watched": "Verberg bekeken video's in de feed",
"minimize_comments": "Opmerkingen minimaliseren",
"instance_auth_selection": "Selectie authenticatie-instantie",
"clone_playlist": "Afspeellijst dupliceren",
"download_as_txt": "Downloaden als .txt",
"rename_playlist": "Afspeellijst hernoemen",
"new_playlist_name": "Nieuwe afspeellijstnaam",
"share": "Delen",
"documentation": "Documentatie",
"status_page": "Status",
"time_code": "Tijdcode (in seconden)",
"show_chapters": "Hoofdstukken",
"source_code": "Broncode",
"instance_donations": "Instantie donaties",
"reply_count": "{count} antwoorden",
"no_valid_playlists": "Het bestand bevat geen geldige afspeellijsten!",
"clone_playlist_success": "Dupliceren gelukt!",
"reset_preferences": "Voorkeuren herstellen",
"back_to_home": "Terug naar de start",
"minimize_comments_default": "Opmerkingen Standaard Minimaliseren",
"delete_account": "Account Verwijderen",
"logout": "Uitloggen op dit apparaat",
"minimize_recommendations_default": "Aanbevelingen Standaard Minimaliseren",
"confirm_reset_preferences": "Weet u zeker dat u uw voorkeuren opnieuw wilt instellen?",
"backup_preferences": "Back-up voorkeuren",
"invalidate_session": "Uitloggen op alle apparaten",
"different_auth_instance": "Gebruik een andere instantie voor authenticatie",
"with_playlist": "Delen met afspeellijst",
"playlist_bookmarked": "Bladwijzer gemaakt",
"bookmark_playlist": "Bladwijzer",
"skip_automatically": "Automatisch",
"skip_button_only": "toon de overslaan knop",
"min_segment_length": "Minimale segmentlengte (in seconden)",
"skip_segment": "segment overslaan",
"show_less": "Toon minder"
},
"titles": {
"register": "Registreren",
@ -74,8 +118,14 @@
"preferences": "Voorkeuren",
"history": "Geschiedenis",
"subscriptions": "Abonnementen",
"trending": "Trending",
"playlists": "Afspeellijsten"
"trending": "populair",
"playlists": "Afspeellijsten",
"account": "profiel",
"instance": "Instantie",
"player": "Speler",
"livestreams": "Livestreams",
"channels": "Kanalen",
"bookmarks": "Bladwijzers"
},
"player": {
"watch_on": "Kijk op {0}"
@ -102,7 +152,10 @@
"watched": "Gekeken",
"sponsor_segments": "Sponsorsegmenten",
"ratings_disabled": "Beoordelingen Uitgeschakeld",
"live": "{0} Live"
"live": "{0} Live",
"shorts": "Shorts",
"category": "Categorie",
"all": "Alle"
},
"preferences": {
"has_cdn": "Heeft CDN?",
@ -114,6 +167,20 @@
"ssl_score": "SSL-score"
},
"comment": {
"pinned_by": "Vastgemaakt door {author}"
"pinned_by": "Vastgemaakt door {author}",
"user_disabled": "Opmerkingen zijn uitgeschakeld in de instellingen.",
"loading": "Opmerkingen laden...",
"disabled": "Reacties zijn uitgeschakeld door de uploader."
},
"info": {
"preferences_note": "Let op: voorkeuren worden opgeslagen in de lokale opslag van uw browser. Als u uw browsergegevens verwijdert, worden ze opnieuw ingesteld.",
"copied": "Gekopieerd!",
"cannot_copy": "Kan niet kopiëren!",
"page_not_found": "Pagina niet gevonden",
"local_storage": "Deze actie vereist lokale opslag, zijn cookies ingeschakeld?",
"register_no_email_note": "Een e-mailadres als gebruikersnaam gebruiken wordt afgeraden. Toch doorgaan?"
},
"subscriptions": {
"subscribed_channels_count": "Geabonneerd op: {0}"
}
}

189
src/locales/oc.json Normal file
View file

@ -0,0 +1,189 @@
{
"titles": {
"login": "Se connectar",
"feed": "Flux",
"preferences": "Preferéncias",
"history": "Istoric",
"account": "Compte",
"instance": "Instància",
"player": "Lector",
"livestreams": "Dirèctes",
"channels": "Canals",
"trending": "Tendéncias",
"register": "Sinscriure",
"subscriptions": "Abonaments",
"playlists": "Listas de lecturas",
"bookmarks": "Marcapaginas"
},
"player": {
"watch_on": "Agachar sus {0}"
},
"actions": {
"subscribe": "Sabonar - {count}",
"unsubscribe": "Se desabonar - {count}",
"view_subscriptions": "Veire los abonaments",
"sort_by": "Triar per:",
"most_recent": "Mai recents",
"least_recent": "Mens recents",
"channel_name_asc": "Nom del canal (A-Z)",
"channel_name_desc": "Nom del canal (Z-A)",
"back": "Tornar",
"skip_outro": "Passar los crèdits a la fin",
"skip_preview": "Passar lapercebut / resumit",
"uses_api_from": "Utiliza lAPI de ",
"skip_intro": "Passar lanimacion dentracte / Introduccion",
"enable_sponsorblock": "Activar Sponsorblock",
"skip_sponsors": "Passar las promocions",
"show_comments": "Mostrar los comentaris",
"minimize_description": "Minimizar la descripcion",
"show_description": "Mostrar la descripcion",
"show_recommendations": "Mostrar las recomandacions",
"minimize_recommendations": "Minimizar las recomandacions",
"enable_lbry_proxy": "Activar lo servidor mandatari per LBRY",
"view_ssl_score": "Mostrar lavaloracion SSL",
"search": "Recercar (Ctrl+K)",
"filter": "Filtrar",
"disable_lbry": "Desactivar LBRY per la difusion en dirècte",
"loading": "Cargament…",
"clear_history": "Netejar listoric",
"hide_replies": "Rescondre las responsas",
"load_more_replies": "Cargar mai de responsas",
"remove_from_playlist": "Levar de la lista de lectura",
"add_to_playlist": "Apondre a la listra de lectura",
"minimize_comments": "Minimizar los comentaris",
"theme": "Tèma",
"language_selection": "Seleccion de la lenga",
"loop_this_video": "Legir en bocla la vidèo",
"reset_preferences": "Restablir las preferéncias",
"auto": "Auto",
"dark": "Escur",
"light": "Clar",
"donations": "Don pel desvolopament",
"backup_preferences": "Salvagardar las preferéncias",
"share": "Partejar",
"documentation": "Documentacion",
"source_code": "Còdi font",
"restore_preferences": "Restablir las preferéncias",
"skip_interaction": "Ignorar los rapèls dinteraccion (Sabonar)",
"show_markers": "Mostrar los marcadors sul lector",
"default_quality": "Qualitat per defaut",
"buffering_goal": "Objectiu de mesa en memòria tampon (en segondas)",
"minimize_description_default": "Minimizar la descripcion per defaut",
"instances_list": "Lista dinstàncias",
"enabled_codecs": "Codecs activats (multiples)",
"yes": "Òc",
"no": "Non",
"export_to_json": "Exportar en JSON",
"import_from_json": "Importar dun JSON/CSV",
"auto_play_next_video": "Legir la vidèo seguenta automaticament",
"create_playlist": "Crear una lista de lectura",
"delete_playlist": "Levar de la lista de lectura",
"select_playlist": "Seleccionatz una lista de lectura",
"delete_playlist_confirm": "Suprimir aquesta lista de lectura ?",
"clone_playlist": "Clonar la lista de lectura",
"instance_auth_selection": "Seleccion de linstància dautentificacion",
"clone_playlist_success": "Clonatge capitat !",
"follow_link": "Dobrir lo ligam",
"skip_self_promo": "Sautar la promocion gratuita / autopromocion",
"skip_non_music": "Sautar la musica : seccion non musicala",
"skip_highlight": "Ignorar los tempses fòrts",
"skip_filler_tangent": "Sautar la tangenta demplenament",
"autoplay_video": "Legir automaticament la vidèo",
"audio_only": "Sonque àudio",
"minimize_comments_default": "Minimizar los comentaris per defaut",
"instance_selection": "Seleccion dinstàncias",
"please_select_playlist": "Seleccionatz una lista de lectura",
"show_watch_on_youtube": "Mostrar lo boton « Veire sus YouTube »",
"invalidate_session": "Se desconnectar de totes los aparelhs",
"different_auth_instance": "Utilizar una instància diferenta per lautentificacion",
"back_to_home": "Tornar a lacuèlh",
"rename_playlist": "Renomenar la lista de lectura",
"new_playlist_name": "Nom novèl de la lista de lectura",
"with_timecode": "Partejar amb còdi orari",
"piped_link": "Ligam cap a Piped",
"show_chapters": "Capítols",
"country_selection": "Seleccion del país",
"default_homepage": "Pagina dacuèlh per defaut",
"minimize_recommendations_default": "Minimizar las recomandacions per defaut",
"store_watch_history": "Servar listoric de visualizacion",
"show_more": "Ne mostrar mai",
"delete_playlist_video_confirm": "Levar aquesta vidèo de la lista de lectura ?",
"delete_account": "Suprimir lo compte",
"logout": "Se desconnectar daqueste aparelh",
"minimize_chapters_default": "Minimizar los capítols per defaut",
"download_as_txt": "Telecargar coma .txt",
"copy_link": "Copiar lo ligam",
"time_code": "Moment (en segondas)",
"store_search_history": "Gardar listoric de recèrca",
"confirm_reset_preferences": "Volètz vertadièrament reïnicializar las preferéncias ?",
"hide_watched": "Rescondre las vidèos vistas al flux",
"reply_count": "{count} responsas",
"with_playlist": "Partejar amb una lista de lectura",
"playlist_bookmarked": "Marcat",
"bookmark_playlist": "Marcapagina",
"status_page": "Estat",
"no_valid_playlists": "Lo fichièr conten pas cap de lista de lectura valida !",
"instance_donations": "Dons dinstància",
"skip_button_only": "Afichar lo boton per sautar",
"skip_automatically": "Automaticament",
"min_segment_length": "Durada minimum de segment (en segondas)",
"skip_segment": "Sautar lo segment",
"show_less": "Ne mostrar mens",
"autoplay_next_countdown": "Descompte per defaut abans de passar a la vidèo seguenta (en segondas)",
"dismiss": "Ignorar"
},
"preferences": {
"instance_locations": "Localizacion de linstància",
"registered_users": "Utilizaires inscriches",
"instance_name": "Nom de linstància",
"has_cdn": "A un CDN ?",
"version": "Version",
"up_to_date": "Actualizat ?",
"ssl_score": "Marca SSL"
},
"login": {
"username": "Nom dutilizaire",
"password": "Senhal"
},
"video": {
"views": "{views} visualizacions",
"watched": "Vista",
"ratings_disabled": "Avaloracions desactivadas",
"sponsor_segments": "Segments de sponsors",
"live": "{0} en dirècte",
"shorts": "Corts",
"chapters": "Capítols",
"videos": "Vidèos",
"all": "Totas",
"category": "Categoria"
},
"info": {
"preferences_note": "Nòta: las preferéncias son gardadas dins lespaci demmagazinatge del navegador. La supression de las donadas del navegador las restablirà.",
"copied": "Copiat !",
"page_not_found": "Pagina pas trobada",
"cannot_copy": "Còpia impossibla !",
"local_storage": "Aquesta accion requerís lo localStorage, son activats los cookies ?",
"register_no_email_note": "Es pas recomandat dutilizar una adreça electronica coma nom dutilizaire. Contunhar çaquelà ?",
"next_video_countdown": "La vidèo seguenta començarà daquí {0}s"
},
"comment": {
"disabled": "Lautor a desactivat los comentaris.",
"loading": "Cargament dels comentaris…",
"user_disabled": "Los comentaris son desactivats als paramètres.",
"pinned_by": "Penjat per {author}"
},
"search": {
"did_you_mean": "Voliatz dire : {0} ?",
"channels": "YouTube : Canals",
"videos": "YouTube : Vidèos",
"all": "YouTube : Tot",
"playlists": "YouTube : Listas de lectura",
"music_songs": "YT Music : Cançons",
"music_videos": "YT Music : Vidèos",
"music_albums": "YT Music : Albums",
"music_playlists": "YT Music : Listas de lectura"
},
"subscriptions": {
"subscribed_channels_count": "Abonat a : {0}"
}
}

View file

@ -1 +1,186 @@
{}
{
"titles": {
"login": "ଲଗ୍ ଇନ୍",
"feed": "ଫିଡ୍",
"trending": "ଟ୍ରେଣ୍ଡିଂ",
"register": "ପଂଜିକରଣ",
"preferences": "ପସନ୍ଦ",
"history": "ଇତିହାସ",
"subscriptions": "ଅନୁସୃତ ଗୁଡ଼ିକ",
"playlists": "ପ୍ଲେଲିଷ୍ଟ ଗୁଡ଼ିକ",
"instance": "ଉଦାହରଣ",
"account": "ଆକାଉଣ୍ଟ",
"player": "ପ୍ଲେୟାର",
"livestreams": "ସିଧାପ୍ରସାରଣ ଗୁଡ଼ିକ",
"channels": "ସ୍ରୋତ ଗୁଡ଼ିକ",
"bookmarks": "ବୁକମାର୍କଗୁଡିକ"
},
"player": {
"watch_on": "{0} ରେ ଦେଖନ୍ତୁ"
},
"actions": {
"subscribe": "ସଦସ୍ୟତା - {count}",
"unsubscribe": "ସଦସ୍ୟତା ରଦ୍ଦ କରନ୍ତୁ - {count}",
"view_subscriptions": "ସଦସ୍ୟତା ଗୁଡ଼ିକ ଦେଖନ୍ତୁ",
"sort_by": "ଏହି କ୍ରମରେ ସଜାନ୍ତୁ:",
"most_recent": "ସଦ୍ୟତମ",
"uses_api_from": "ରୁ API ବ୍ୟବହାର କରେ ",
"enable_sponsorblock": "ପ୍ରଯୋଜକ ବନ୍ଦ ସକ୍ଷମ କରନ୍ତୁ",
"skip_intro": "ଇଣ୍ଟରମିସନ୍ / ଇଣ୍ଟ୍ରୋ ଆନିମେସନ୍ ଛାଡିଦିଅ",
"default_homepage": "ଡିଫଲ୍ଟ ମୂଳପୃଷ୍ଠା",
"minimize_comments_default": "ଡିଫଲ୍ଟ ଭାବରେ ମନ୍ତବ୍ୟଗୁଡିକୁ କମ୍ କରନ୍ତୁ",
"show_description": "ବର୍ଣ୍ଣନା ଦେଖାନ୍ତୁ",
"minimize_recommendations": "ସୁପାରିଶକୁ କମ୍ କରନ୍ତୁ",
"show_recommendations": "ସୁପାରିଶଗୁଡିକ ଦେଖାନ୍ତୁ",
"disable_lbry": "ଷ୍ଟ୍ରିମିଂ ପାଇଁ LBRY ଅକ୍ଷମ କରନ୍ତୁ",
"search": "ସନ୍ଧାନ କରନ୍ତୁ (Ctrl+K)",
"rename_playlist": "ପ୍ଲେ ଲିଷ୍ଟର ନାମ ପରିବର୍ତ୍ତନ କରନ୍ତୁ",
"new_playlist_name": "ନୂତନ ପ୍ଲେଲିଷ୍ଟ ନାମ",
"channel_name_asc": "ସ୍ରୋତ ର ନାମ (A-Z)",
"least_recent": "ସର୍ବନିମ୍ନ ସାମ୍ପ୍ରତିକ",
"channel_name_desc": "ସ୍ରୋତ ର ନାମ (Z-A)",
"back": "ପଛକୁ ଯାଆନ୍ତୁ",
"skip_sponsors": "ପ୍ରଯୋଜକମାନଙ୍କୁ ଛାଡ଼ିଦିଅ",
"skip_outro": "ଏଣ୍ଡକାର୍ଡ / କ୍ରେଡିଟ୍ ଛାଡିଦିଅ",
"skip_preview": "ପୂର୍ବାବଲୋକନ / ପୁନଃପ୍ରକାଶକୁ ଛାଡିଦିଅ",
"restore_preferences": "ପସନ୍ଦଗୁଡିକ ପୁନରୁଦ୍ଧାର କରନ୍ତୁ",
"skip_interaction": "ପାରସ୍ପରିକ ସ୍ମାରକକୁ ଏଡ଼ାଇ ଦିଅନ୍ତୁ (ସବସ୍କ୍ରାଇବ କରନ୍ତୁ)",
"skip_self_promo": "ଅନାଦେୟ / ଆତ୍ମ ପଦୋନ୍ନତି ଛାଡିଦିଅ",
"skip_non_music": "ସଙ୍ଗୀତ ପରିତ୍ୟାଗ କରନ୍ତୁ: ଅଣ-ସଙ୍ଗୀତ ବିଭାଗ",
"skip_highlight": "ହାଇଲାଇଟ୍ କୁ ଛାଡିଦିଅ",
"auto": "ସ୍ଵତଃ",
"skip_filler_tangent": "ଫିଲର୍ ଟାଙ୍ଗେଣ୍ଟ୍ ଛାଡିଦିଅ",
"default_quality": "ଡିଫଲ୍ଟ ଗୁଣବତ୍ତା",
"show_markers": "ପ୍ଲେୟାରରେ ମାର୍କର୍ସ ଦେଖାନ୍ତୁ",
"theme": "ଥିମ୍",
"dark": "ଅନ୍ଧାର",
"light": "ଉଜ୍ଜଳ",
"autoplay_video": "ଅଟୋପ୍ଲେ ଭିଡିଓ",
"enabled_codecs": "ସକ୍ଷମ କୋଡେକସ୍ (ଏକାଧିକ)",
"audio_only": "କେବଳ ସ୍ୱର",
"language_selection": "ଭାଷା ଚୟନ",
"show_more": "ଅଧିକ ଦେଖାନ୍ତୁ",
"buffering_goal": "ବଫରିଂ ଲକ୍ଷ୍ୟ (ସେକେଣ୍ଡରେ)",
"country_selection": "ଦେଶ ଚୟନ",
"minimize_description_default": "ଡିଫଲ୍ଟ ଭାବରେ ବର୍ଣ୍ଣନାକୁ କମ୍ କରନ୍ତୁ",
"store_watch_history": "ଦେଖିଥିବା ଭିଡିଓ ଗୁଡ଼ିକର ଇତିହାସ ରଖନ୍ତୁ",
"instances_list": "ଉଦାହରଣ ତାଲିକା",
"instance_selection": "ଇନଷ୍ଟାନ୍ସ ଚୟନ",
"yes": "ହଁ",
"import_from_json": "JSON / CSV ରୁ ଆମଦାନୀ କରନ୍ତୁ",
"no": "ନାହିଁ",
"export_to_json": "JSON କୁ ରପ୍ତାନି କରନ୍ତୁ",
"loop_this_video": "ଏହି ଭିଡିଓକୁ ଲୁପ୍ କରନ୍ତୁ",
"auto_play_next_video": "ପରବର୍ତ୍ତୀ ଭିଡିଓ ସ୍ଵତଃ ଚଲାନ୍ତୁ",
"donations": "ବିକାଶ ପାଇଁ ଦାନ",
"minimize_comments": "ମନ୍ତବ୍ୟଗୁଡିକୁ କମ୍ କରନ୍ତୁ",
"show_comments": "ମନ୍ତବ୍ୟଗୁଡିକ ଦେଖାନ୍ତୁ",
"delete_playlist_video_confirm": "ପ୍ଲେ ଲିଷ୍ଟରୁ ଭିଡିଓ ଅପସାରଣ କରିବେ କି?",
"minimize_description": "ବର୍ଣ୍ଣନାକୁ କମ୍ କରନ୍ତୁ",
"view_ssl_score": "SSL ସ୍କୋର ଦେଖନ୍ତୁ",
"loading": "ଲୋଡ୍ ହେଉଛି...",
"enable_lbry_proxy": "LBRY ପାଇଁ ପ୍ରକ୍ସି ସକ୍ଷମ କରନ୍ତୁ",
"filter": "ଫିଲ୍ଟର୍ କରନ୍ତୁ",
"load_more_replies": "ଅଧିକ ଉତ୍ତର ଲୋଡ୍ କରନ୍ତୁ",
"clear_history": "ଇତିହାସ ସଫା କରନ୍ତୁ",
"hide_replies": "ଉତ୍ତରଗୁଡିକ ଲୁଚାନ୍ତୁ",
"remove_from_playlist": "ପ୍ଲେ ଲିଷ୍ଟରୁ ହଟାନ୍ତୁ",
"add_to_playlist": "ପ୍ଲେ ଲିଷ୍ଟରେ ଯୋଡନ୍ତୁ",
"create_playlist": "ପ୍ଲେଲିଷ୍ଟ ସୃଷ୍ଟି କରନ୍ତୁ",
"please_select_playlist": "ଦୟାକରି ଏକ ପ୍ଲେଲିଷ୍ଟ ଚୟନ କରନ୍ତୁ",
"delete_playlist": "ପ୍ଲେ ଲିଷ୍ଟ ଡିଲିଟ୍ କରନ୍ତୁ",
"show_watch_on_youtube": "ୟୁଟ୍ୟୁବ୍ ବଟନ୍ ରେ ୱାଚ୍ ଦେଖାନ୍ତୁ",
"reset_preferences": "ପସନ୍ଦଗୁଡିକ ପୁନଃସେଟ୍ କରନ୍ତୁ",
"share": "ଅଂଶୀଦାର କରନ୍ତୁ",
"select_playlist": "ଏକ ପ୍ଲେଲିଷ୍ଟ ଚୟନ କରନ୍ତୁ",
"delete_playlist_confirm": "ଏହି ପ୍ଲେଲିଷ୍ଟ ବିଲୋପ କରିବେ କି?",
"delete_account": "ଖାତା ବିଲୋପ କରନ୍ତୁ",
"logout": "ଏହି ଉପକରଣରୁ ଲଗଆଉଟ୍ କରନ୍ତୁ",
"minimize_recommendations_default": "ଡିଫଲ୍ଟ ଭାବରେ ସୁପାରିଶକୁ କମ୍ କରନ୍ତୁ",
"invalidate_session": "ସମସ୍ତ ଡିଭାଇସ୍ ରୁ ଲଗଆଉଟ୍ କରନ୍ତୁ",
"download_as_txt": ".Txt ଭାବରେ ଡାଉନଲୋଡ୍ କରନ୍ତୁ",
"instance_auth_selection": "ପ୍ରାମାଣିକିକରଣ ଇନଷ୍ଟାନ୍ସ ଚୟନ",
"confirm_reset_preferences": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ଆପଣଙ୍କର ପସନ୍ଦଗୁଡିକ ପୁନଃ ସେଟ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି?",
"status_page": "ସ୍ଥିତି",
"different_auth_instance": "ପ୍ରାମାଣିକିକରଣ ପାଇଁ ଏକ ଭିନ୍ନ ଉଦାହରଣ ବ୍ୟବହାର କରନ୍ତୁ",
"clone_playlist": "କ୍ଲୋନ୍ ପ୍ଲେଲିଷ୍ଟ୍",
"clone_playlist_success": "ସଫଳତାର ସହିତ କ୍ଲୋନ ହୋଇଛି!",
"backup_preferences": "ପସନ୍ଦ ଗୁଡ଼ିକର ନକଲ ସଂରକ୍ଷଣ କରନ୍ତୁ",
"back_to_home": "ଘରକୁ ଫେରନ୍ତୁ",
"show_chapters": "ଅଧ୍ୟାୟ ଗୁଡ଼ିକ",
"store_search_history": "ସନ୍ଧାନ ଇତିହାସ ଗଚ୍ଛିତ କରନ୍ତୁ",
"hide_watched": "ଫିଡରେ ଦେଖାଯାଇଥିବା ଭିଡିଓଗୁଡିକ ଲୁଚାନ୍ତୁ",
"follow_link": "ଲିଙ୍କ୍ ଅନୁସରଣ କରନ୍ତୁ",
"copy_link": "ଲିଙ୍କ୍ କପି କରନ୍ତୁ",
"with_timecode": "ଟାଇମ୍ କୋଡ୍ ସହିତ ଅଂଶୀଦାର କରନ୍ତୁ",
"piped_link": "ପାଇପ୍ ଲିଙ୍କ୍",
"time_code": "ସମୟ କୋଡ୍ (ସେକେଣ୍ଡରେ)",
"source_code": "ଉତ୍ସ କୋଡ୍",
"reply_count": "{count} ଉତ୍ତର",
"documentation": "ଡକ୍ୟୁମେଣ୍ଟେସନ୍",
"instance_donations": "ଇନଷ୍ଟାଣ୍ଟ ଦାନ ଗୁଡ଼ିକ",
"minimize_chapters_default": "ଡିଫଲ୍ଟ ଭାବରେ ଅଧ୍ୟାୟଗୁଡ଼ିକୁ କମ୍ କରନ୍ତୁ",
"no_valid_playlists": "ଫାଇଲ୍ ଟି ବୈଧ ପ୍ଲେଲିଷ୍ଟ ଧାରଣ କରେ ନାହିଁ!",
"with_playlist": "ପ୍ଲେଲିଷ୍ଟ ସହିତ ଅଂଶୀଦାର କରନ୍ତୁ",
"bookmark_playlist": "ବୁକମାର୍କ",
"playlist_bookmarked": "ବୁକମାର୍କ ହୋଇଛି",
"min_segment_length": "ସର୍ବନିମ୍ନ ସେଗମେଣ୍ଟ ଲମ୍ବ (ସେକେଣ୍ଡରେ)",
"skip_button_only": "ସ୍କିପ୍ ବଟନ୍ ଦେଖାନ୍ତୁ",
"skip_automatically": "ସ୍ୱୟଂଚାଳିତ ଭାବରେ",
"skip_segment": "ସେଗମେଣ୍ଟକୁ ଏଡ଼ାଇଦିଅ",
"show_less": "କମ୍ ଦେଖାନ୍ତୁ"
},
"comment": {
"loading": "ମନ୍ତବ୍ୟ ଲୋଡ୍ ହେଉଛି ...",
"user_disabled": "ମନ୍ତବ୍ୟଗୁଡିକ ସେଟିଂସମୂହରେ ଅକ୍ଷମ ହୋଇଛି ।",
"pinned_by": "{author} ଙ୍କ ଦ୍ୱାରା ପିନ୍ ହୋଇଛି",
"disabled": "ମନ୍ତବ୍ୟଗୁଡିକ ଅପଲୋଡର୍ ଦ୍ୱାରା ଅକ୍ଷମ ହୋଇଛି ।"
},
"video": {
"views": "{views} ଦୃଶ୍ୟ",
"watched": "ଦେଖାଯାଇଛି",
"sponsor_segments": "ପ୍ରାୟୋଜକ ଖଣ୍ଡଗୁଡିକ",
"shorts": "ସର୍ଟସ୍",
"videos": "ଭିଡିଓ ଗୁଡିକ",
"ratings_disabled": "ମୂଲ୍ୟାୟନ ଅକ୍ଷମ ହୋଇଛି",
"chapters": "ଅଧ୍ୟାୟ ଗୁଡ଼ିକ",
"live": "{0} ସିଧାପ୍ରସାରଣ",
"all": "ସମସ୍ତ",
"category": "ବର୍ଗ"
},
"search": {
"did_you_mean": "ଆପଣ କହିବାକୁ ଚାହୁଁଛନ୍ତି କି: {0}?",
"music_albums": "ୟୁଟିଉବ୍ ସଙ୍ଗୀତ: ଆଲବମ୍ ଗୁଡ଼ିକ",
"music_playlists": "ୟୁଟିଉବ୍ ସଙ୍ଗୀତ: ପ୍ଲେଲିଷ୍ଟଗୁଡିକ",
"music_videos": "ୟୁଟିଉବ୍ ସଙ୍ଗୀତ: ଭିଡିଓଗୁଡିକ",
"all": "ୟୁଟ୍ୟୁବ୍: ସମସ୍ତ",
"videos": "ୟୁଟ୍ୟୁବ୍: ଭିଡିଓଗୁଡିକ",
"channels": "ୟୁଟ୍ୟୁବ୍: ଚ୍ୟାନେଲଗୁଡିକ",
"music_songs": "ୟୁଟିଉବ୍ ସଙ୍ଗୀତ: ଗୀତ ଗୁଡ଼ିକ",
"playlists": "ୟୁଟ୍ୟୁବ୍: ପ୍ଲେଲିଷ୍ଟଗୁଡିକ"
},
"subscriptions": {
"subscribed_channels_count": "ସଦସ୍ୟତା: {0}"
},
"info": {
"preferences_note": "ଟିପନ୍ତୁ: ପସନ୍ଦଗୁଡିକ ଆପଣଙ୍କ ବ୍ରାଉଜରର ସ୍ଥାନୀୟ ଷ୍ଟୋରେଜ୍ ରେ ସେଭ୍ ହୋଇଛି । ଆପଣଙ୍କର ବ୍ରାଉଜର୍ ଡାଟା ଡିଲିଟ୍ କରିବା ସେଗୁଡ଼ିକୁ ପୁନଃସେଟ୍ କରିବ ।",
"copied": "କପି ହୋଇଛି!",
"cannot_copy": "କପି କରିପାରିବ ନାହିଁ!",
"page_not_found": "ପୃଷ୍ଠାଟି ମିଳିଲା ନାହିଁ",
"local_storage": "ଏହି କ୍ରିୟା ଲୋକାଲ୍ ଷ୍ଟୋରେଜ୍ ଆବଶ୍ୟକ କରେ, କୁକିଜ୍ ସକ୍ଷମ ଅଛି କି?",
"register_no_email_note": "ଉପଯୋଗକର୍ତ୍ତା ନାମ ଭାବରେ ଏକ ଇ-ମେଲ୍ ବ୍ୟବହାର କରିବା ସୁପାରିଶ କରାଯାଏ ନାହିଁ । ଯେକୌଣସି ପ୍ରକାରେ ଅଗ୍ରଗତି କରନ୍ତୁ?"
},
"preferences": {
"instance_name": "ଇନଷ୍ଟାନ୍ସ ନାମ",
"registered_users": "ପଞ୍ଜୀକୃତ ଉପଭୋକ୍ତା",
"version": "ସଂସ୍କରଣ",
"instance_locations": "ଇନଷ୍ଟାନ୍ସ ଅବସ୍ଥାନ",
"has_cdn": "CDN ଅଛି କି?",
"up_to_date": "ଅଦ୍ୟାବଧି?",
"ssl_score": "SSL ସ୍କୋର"
},
"login": {
"password": "ପାସୱାର୍ଡ",
"username": "ଉପଯୋଗକର୍ତ୍ତା ନାମ"
}
}

View file

@ -7,14 +7,20 @@
"preferences": "Ustawienia",
"history": "Historia",
"subscriptions": "Lista kanałów",
"playlists": "Playlisty"
"playlists": "Playlisty",
"player": "Odtwarzacz",
"account": "Konto",
"instance": "Instancja",
"livestreams": "Na żywo",
"channels": "Kanały",
"bookmarks": "Zakładki"
},
"player": {
"watch_on": "Obejrzyj na {0}"
},
"actions": {
"subscribe": "Subskrybuj - {count}",
"unsubscribe": "Anuluj subskrypcję - {count}",
"unsubscribe": "Odsubskrybuj - {count}",
"view_subscriptions": "Zarządzaj subskrybcjami",
"sort_by": "Sortuj:",
"most_recent": "Najnowsze",
@ -26,7 +32,7 @@
"enable_sponsorblock": "Włącz SponsorBlock",
"skip_sponsors": "Pomijaj segmenty sponsorowane",
"skip_intro": "Pomijaj czołówkę",
"skip_outro": "Pomijaj tyłówkę",
"skip_outro": "Pomiń karty końcowe / Podziękowania",
"skip_preview": "Pomijaj podgląd/podsumowanie",
"skip_interaction": "Pomijaj prośby o interakcję/subskrybcję",
"skip_self_promo": "Pomijaj autopromocję",
@ -34,7 +40,7 @@
"skip_highlight": "Przechodź do meritum filmu",
"skip_filler_tangent": "Pomijaj wstawki humorystyczne",
"theme": "Motyw",
"auto": "Automatyczny",
"auto": "Automatyczna",
"dark": "Ciemny",
"light": "Jasny",
"autoplay_video": "Autoodtwarzanie",
@ -43,9 +49,9 @@
"buffering_goal": "Cel buforowania (w sekundach)",
"country_selection": "Wybór kraju",
"default_homepage": "Domyślna strona główna",
"show_comments": "Pokazuj komentarze",
"minimize_description_default": "Zawsze chowaj opis",
"store_watch_history": "Zapisuj historię oglądania",
"show_comments": "Pokaż komentarze",
"minimize_description_default": "Ukryj opis",
"store_watch_history": "Zapamiętaj historię oglądania",
"language_selection": "Wybór języka",
"instances_list": "Lista instancji",
"enabled_codecs": "Włączone kodeki (lista wielokrotnego wyboru)",
@ -58,36 +64,85 @@
"loop_this_video": "Zapętlaj ten film",
"auto_play_next_video": "Autoodtwarzanie następnego filmu",
"donations": "Wsparcie",
"minimize_description": "Schowaj opis",
"minimize_description": "Ukryj opis",
"show_description": "Pokaż opis",
"minimize_recommendations": "Minimalizuj rekomendacje",
"show_recommendations": "Pokaż rekomendacje",
"disable_lbry": "Wyłącz LBRY dla streaming-u",
"minimize_recommendations": "Ukryj proponowane",
"show_recommendations": "Pokaż proponowane",
"disable_lbry": "Wyłącz LBRY dla przesyłania strumieniowego",
"enable_lbry_proxy": "Włącz proxy dla LBRY",
"view_ssl_score": "Pokaż ocenę SSL",
"search": "Szukaj",
"search": "Szukaj (Ctrl+K)",
"filter": "Filtruj",
"loading": "Ładowanie...",
"clear_history": "Wyczyść historię",
"hide_replies": "Schowaj odpowiedzi",
"hide_replies": "Ukryj odpowiedzi",
"load_more_replies": "Pokaż więcej odpowiedzi",
"add_to_playlist": "Dodaj do playlisty",
"remove_from_playlist": "Usuń z playlisty",
"delete_playlist_video_confirm": "Czy jesteś pewien, że chcesz usunąć ten film z tej playlisty?",
"delete_playlist_video_confirm": "Usunąć film z playlisty?",
"create_playlist": "Stwórz playlistę",
"delete_playlist": "Usuń playlistę",
"select_playlist": "Wybierz playlistę",
"delete_playlist_confirm": "Czy jesteś pewien, że chcesz usunąć tę playlistę?",
"please_select_playlist": "Musisz wybrać playlistę"
"delete_playlist_confirm": "Usunąć tę playlistę?",
"please_select_playlist": "Musisz wybrać playlistę",
"confirm_reset_preferences": "Zresetować ustawienia?",
"show_watch_on_youtube": "Przycisk „Oglądaj na YouTube”",
"restore_preferences": "Przywróć ustawienia z kopii zapasowej",
"clone_playlist_success": "Pomyślnie sklonowano!",
"copy_link": "Skopiuj link",
"documentation": "Dokumentacja",
"instance_donations": "Darowizny na rzecz instancji",
"back_to_home": "Idź do strony głównej",
"instance_auth_selection": "Wybrana instancja autoryzacyjna",
"time_code": "Kod czasowy (w sekundach)",
"show_markers": "Pokaż segmenty na odtwarzaczu",
"store_search_history": "Zapamiętaj historię wyszukiwania",
"hide_watched": "Ukryj obejrzane filmy",
"source_code": "Kod źródłowy",
"show_chapters": "Rozdziały",
"minimize_chapters_default": "Ukryj rozdziały",
"rename_playlist": "Zmień nazwę playlisty",
"follow_link": "Otwórz link",
"minimize_comments_default": "Ukryj sekcję komentarzy",
"minimize_comments": "Ukryj komentarze",
"delete_account": "Usuń konto",
"logout": "Wyloguj się z tego urządzenia",
"minimize_recommendations_default": "Ukryj proponowane filmy",
"invalidate_session": "Wyloguj się ze wszystkich urządzeń",
"different_auth_instance": "Użyj innej instancji do obsługi konta Piped",
"clone_playlist": "Sklonuj playlistę",
"backup_preferences": "Pobierz kopię zapasową ustawień",
"download_as_txt": "Pobierz jako .txt",
"reset_preferences": "Zresetuj ustawienia",
"new_playlist_name": "Nowa nazwa playlisty",
"share": "Udostępnij",
"with_timecode": "Udostępnij z kodem czasowym",
"piped_link": "Link Piped",
"status_page": "Status",
"reply_count": "{count} odpowiedzi",
"no_valid_playlists": "Ten plik nie zawiera poprawnych playlist!",
"with_playlist": "Udostępnij z playlistą",
"playlist_bookmarked": "Dodano do zakładek",
"bookmark_playlist": "Zakładka",
"skip_button_only": "Pokaż przycisk pomijania",
"skip_automatically": "Automatycznie",
"min_segment_length": "Minimalna długość segmentu (w sekundach)",
"skip_segment": "Pomiń segment",
"show_less": "Pokaż mniej",
"autoplay_next_countdown": "Domyślne odliczanie do następnego filmu (w sekundach)",
"dismiss": "Odrzuć"
},
"comment": {
"pinned_by": "Przypięty przez {author}"
"pinned_by": "Przypięty przez {author}",
"disabled": "Komentarze zostały wyłączone przez twórcę.",
"loading": "Wczytywanie komentarzy...",
"user_disabled": "Komentarze wyłączone w ustawieniach."
},
"preferences": {
"instance_name": "Nazwa instancji",
"instance_locations": "Lokalizacje instancji",
"has_cdn": "Używa CDN?",
"registered_users": "Il. zarej. uż.",
"registered_users": "Zarejestrowani użytkownicy",
"version": "Wersja",
"up_to_date": "Aktualna?",
"ssl_score": "Ocena SSL"
@ -103,7 +158,10 @@
"sponsor_segments": "Segmenty sponsorowane",
"ratings_disabled": "Ocenianie wyłączone",
"chapters": "Rozdziały",
"live": "{0} Na żywo"
"live": "{0} na żywo",
"shorts": "Krótkie wideo",
"all": "Wszystkie",
"category": "Kategoria"
},
"search": {
"did_you_mean": "Czy chodziło ci o: {0}?",
@ -115,5 +173,17 @@
"music_videos": "YT Music: Teledyski",
"music_albums": "YT Music: Albumy",
"music_playlists": "YT Music: Playlisty"
},
"info": {
"cannot_copy": "Nie można skopiować!",
"copied": "Skopiowano!",
"page_not_found": "Strona nie znaleziona",
"preferences_note": "Uwaga: ustawienia są zapisywane w lokalnej pamięci przeglądarki. Usunięcie danych przeglądarki spowoduje ich zresetowanie.",
"local_storage": "Ta akcja wymaga dostępu do lokalnej pamięci, czy pliki cookie są włączone?",
"register_no_email_note": "Użycie adresu email jako nazwy użytkownika jest niezalecane. Kontynuować mimo to?",
"next_video_countdown": "Odtwarzanie następnego filmu za {0} s"
},
"subscriptions": {
"subscribed_channels_count": "Licznik subskrybcji: {0}"
}
}

View file

@ -1,142 +1,189 @@
{
"titles": {
"trending": "Tendências",
"preferences": "Preferências",
"preferences": "Configurações",
"subscriptions": "Subscrições",
"login": "Entrar",
"login": "Iniciar Sessão",
"register": "Registar",
"history": "Histórico",
"feed": "Feed",
"playlists": "Listas de reprodução",
"feed": "Conteúdo",
"playlists": "Listas de Reprodução",
"account": "Conta",
"instance": "Instância",
"player": "Reprodutor"
"player": "Reprodutor",
"livestreams": "Transmissões ao vivo",
"channels": "Canais",
"bookmarks": "Marcadores"
},
"actions": {
"sort_by": "Ordenar por:",
"most_recent": "Mais recentes",
"least_recent": "Menos recentes",
"channel_name_asc": "Nome do canal (A-Z)",
"back": "Recuar",
"uses_api_from": "Utiliza a API de ",
"enable_sponsorblock": "Ativar 'Sponsorblock'",
"skip_intro": "Ignorar intervalo/animação de abertura",
"skip_outro": "Ignorar cartões finais/créditos",
"skip_preview": "Ignorar pré-visualização/resumo",
"most_recent": "Mais Recente",
"least_recent": "Menos Recente",
"channel_name_asc": "Nome do Canal (A-Z)",
"back": "Voltar",
"uses_api_from": "Utiliza a \"API\" de ",
"enable_sponsorblock": "Ativar \"SponsorBlock\"",
"skip_intro": "Saltar Intermissão/Animação de Introdução",
"skip_outro": "Saltar \"Endcards\"/Créditos",
"skip_preview": "Saltar Pré-Visualização/Recapitulação",
"auto": "Automático",
"dark": "Escuro",
"autoplay_video": "Reprodução automática",
"audio_only": "Apenas áudio",
"default_quality": "Qualidade padrão",
"country_selection": "Seleção de país",
"default_homepage": "Página inicial padrão",
"show_comments": "Mostrar comentários",
"minimize_description_default": "Minimizar descrição por definição",
"store_watch_history": "Guardar histórico de visualizações",
"instances_list": "Lista de instâncias",
"enabled_codecs": "Codificadores ativados (Vários)",
"instance_selection": "Seleção de instância",
"autoplay_video": "Reproduzir Vídeo Automaticamente",
"audio_only": "Apenas Áudio",
"default_quality": "Qualidade Padrão",
"country_selection": "Seleção de País",
"default_homepage": "Página Inicial Padrão",
"show_comments": "Mostrar Comentários",
"minimize_description_default": "Minimizar Descrição por defeito",
"store_watch_history": "Guardar Histórico de Visualizações",
"instances_list": "Lista de Instâncias",
"enabled_codecs": "\"Codecs\" Activados (Vários)",
"instance_selection": "Seleção de Instância",
"show_more": "Mostrar mais",
"import_from_json": "Importar de JSON/CSV",
"export_to_json": "Exportar para JSON",
"loop_this_video": "Repetir este vídeo",
"auto_play_next_video": "Reproduzir próximo vídeo automaticamente",
"donations": "Doações",
"minimize_description": "Minimizar descrição",
"show_description": "Mostrar descrição",
"show_recommendations": "Mostrar recomendações",
"disable_lbry": "Desativar LBRY para streaming",
"enable_lbry_proxy": "Ativar proxy para LBRY",
"view_ssl_score": "Ver valor SSL",
"search": "Pesquisa",
"loop_this_video": "Repetir este Vídeo",
"auto_play_next_video": "Reproduzir Automaticamente o próximo Vídeo",
"donations": "Doações de desenvolvimento",
"minimize_description": "Minimizar Descrição",
"show_description": "Mostrar Descrição",
"show_recommendations": "Mostrar Recomendações",
"disable_lbry": "Desactivar \"LBRY\" para Transmissão",
"enable_lbry_proxy": "Activar \"Proxy\" para \"LBRY\"",
"view_ssl_score": "Ver Pontuação \"SSL\"",
"search": "Pesquisa (Ctrl+K)",
"filter": "Filtrar",
"loading": "A carregar...",
"clear_history": "Limpar histórico",
"loading": "A Carregar...",
"clear_history": "Limpar Histórico",
"subscribe": "Subscrever - {count}",
"unsubscribe": "Cancelar subscrição - {count}",
"view_subscriptions": "Ver subscrições",
"channel_name_desc": "Nome do canal (Z-A)",
"skip_sponsors": "Ignorar publicidade",
"unsubscribe": "Anular subscrição - {count}",
"view_subscriptions": "Ver Subscrições",
"channel_name_desc": "Nome do Canal (Z-A)",
"skip_sponsors": "Saltar Patrocínios",
"yes": "Sim",
"skip_non_music": "Música: ignorar secção não musical",
"skip_non_music": "Saltar Música: Secção Não-Musical",
"no": "Não",
"theme": "Tema",
"language_selection": "Seleção de idioma",
"minimize_recommendations": "Minimizar recomendações",
"language_selection": "Seleção de Idioma",
"minimize_recommendations": "Minimizar Recomendações",
"light": "Claro",
"hide_replies": "Ocultar respostas",
"load_more_replies": "Carregar mais respostas",
"skip_highlight": "Ignorar destaques",
"skip_interaction": "Ignorar lembrete de Interação (Subscrição)",
"skip_self_promo": "Ignorar promoção não paga/autopromoção",
"buffering_goal": "Objetivo de memória (segundos)",
"skip_filler_tangent": "Ignorar cenas desnecessárias",
"hide_replies": "Ocultar Respostas",
"load_more_replies": "Carregar mais Respostas",
"skip_highlight": "Saltar Destaque",
"skip_interaction": "Saltar Lembrete de Interação (Subscreve)",
"skip_self_promo": "Saltar Promoção Não Paga/Auto-Promoção",
"buffering_goal": "Objetivo de \"Buffering\" (em segundos)",
"skip_filler_tangent": "Saltar Tangente \"Filler\"",
"add_to_playlist": "Adicionar à lista de reprodução",
"delete_playlist": "Eliminar lista de reprodução",
"select_playlist": "Seleccione uma lista de reprodução",
"delete_playlist_confirm": "Eliminar esta lista de reprodução?",
"please_select_playlist": "Selecione uma lista de reprodução",
"delete_playlist_video_confirm": "Remover vídeo da lista de reprodução?",
"delete_playlist": "Apagar Lista de Reprodução",
"select_playlist": "Selecionar uma Lista de Reprodução",
"delete_playlist_confirm": "Apagar esta lista de reprodução?",
"please_select_playlist": "Selecionar uma lista de reprodução se faz favor",
"delete_playlist_video_confirm": "Remover o vídeo da lista de reprodução?",
"remove_from_playlist": "Remover da lista de reprodução",
"create_playlist": "Criar lista de reprodução",
"create_playlist": "Criar Lista de Reprodução",
"clone_playlist_success": "Clonada com sucesso!",
"clone_playlist": "Clonar lista de reprodução",
"show_markers": "Mostrar marcas no reprodutor",
"delete_account": "Eliminar conta",
"logout": "Terminar sessão neste dispositivo",
"minimize_recommendations_default": "Minimizar recomendações por definição",
"invalidate_session": "Terminar sessão em todos os dispositivos",
"different_auth_instance": "Use uma instância diferente para autenticação",
"instance_auth_selection": "Seleção de instância de autenticação",
"confirm_reset_preferences": "Tem a certeza de que deseja restaurar as preferências originais?",
"download_as_txt": "Descarregar como txt",
"reset_preferences": "Repor definições originais",
"restore_preferences": "Restaurar preferências"
"clone_playlist": "Clonar Lista de Reprodução",
"show_markers": "Mostrar Marcadores no Leitor",
"delete_account": "Apagar Conta",
"logout": "Terminar sessão neste aparelho",
"minimize_recommendations_default": "Minimizar Recomendações por defeito",
"invalidate_session": "Terminar sessão em todos os aparelhos",
"different_auth_instance": "Usar uma instância diferente para autenticação",
"instance_auth_selection": "Selecção da Instância para Autenticação",
"confirm_reset_preferences": "Tem a certeza que quer redefinir as suas configurações?",
"download_as_txt": "Descarregar como .txt",
"reset_preferences": "Redefinir preferências",
"restore_preferences": "Restaurar configurações",
"follow_link": "Seguir ligação",
"piped_link": "Ligação do Piped",
"backup_preferences": "Exportar configurações",
"store_search_history": "Armazenar Histórico de Pesquisa",
"hide_watched": "Ocultar vídeos assistidos no feed",
"documentation": "Documentação",
"status_page": "Estado",
"source_code": "Código-fonte",
"instance_donations": "Doações de instâncias",
"minimize_chapters_default": "Minimizar Capítulos por padrão",
"show_watch_on_youtube": "Mostrar Botão Assistir no YouTube",
"new_playlist_name": "Novo nome da lista de reprodução",
"minimize_comments": "Minimizar Comentários",
"back_to_home": "Voltar ao início",
"rename_playlist": "Renomear",
"copy_link": "Copiar ligação",
"time_code": "Código de tempo (em segundos)",
"minimize_comments_default": "Minimizar Comentários por defeito",
"share": "Partilhar",
"with_timecode": "Partilhar com código de tempo",
"show_chapters": "Capítulos",
"reply_count": "{count} respostas",
"no_valid_playlists": "O ficheiro não contém listas de reprodução válidas!",
"with_playlist": "Partilhar com lista de reprodução",
"playlist_bookmarked": "Marcado",
"bookmark_playlist": "Marcador",
"skip_button_only": "Mostrar botão saltar",
"skip_automatically": "Automaticamente",
"min_segment_length": "Comprimento Mínimo do Segmento (em segundos)",
"skip_segment": "Saltar Segmento",
"show_less": "Mostrar menos",
"dismiss": "Ignorar",
"autoplay_next_countdown": "Predefinição Contagem decrescente até ao próximo vídeo (em segundos)"
},
"preferences": {
"instance_name": "Nome da instância",
"instance_locations": "Localizações da instância",
"ssl_score": "Valor SSL",
"has_cdn": "Tem CDN?",
"instance_name": "Nome da Instância",
"instance_locations": "Localizações da Instância",
"ssl_score": "Pontuação \"SSL\"",
"has_cdn": "Tem \"CDN\"?",
"version": "Versão",
"registered_users": "Utilizadores registados",
"up_to_date": "Atualizado?"
"registered_users": "Utilizadores Registados",
"up_to_date": "Atualizada?"
},
"login": {
"password": "Palavra-passe",
"username": "Utilizador"
"username": "Nome de utilizador"
},
"video": {
"videos": "Vídeos",
"views": "{views} visualizações",
"watched": "Assistido",
"sponsor_segments": "Segmentos de patrocínios",
"ratings_disabled": "Avaliações desativadas",
"watched": "Visto",
"sponsor_segments": "Segmentos Patrocinados",
"ratings_disabled": "Classificações Desactivadas",
"chapters": "Capítulos",
"live": "{0} em direto",
"shorts": "Curtos"
"live": "{0} em Direto",
"shorts": "\"Shorts\"",
"all": "Todos",
"category": "Categoria"
},
"search": {
"did_you_mean": "Será que queria dizer: {0}?",
"did_you_mean": "Será que querias dizer: {0}?",
"all": "YouTube: Tudo",
"videos": "YouTube: Vídeos",
"channels": "YouTube: Canais",
"music_songs": "YT Music: Músicas",
"music_videos": "YT Music: Vídeos",
"music_albums": "YT Music: Álbuns",
"music_playlists": "YT Music: Listas de reprodução",
"playlists": "YouTube: Listas de reprodução"
"music_playlists": "YT Music: Listas de Reprodução",
"playlists": "YouTube: Listas de Reprodução"
},
"player": {
"watch_on": "Ver em {0}"
},
"comment": {
"pinned_by": "Fixado por {author}",
"disabled": "Os comentários form desativados pelo publicador.",
"loading": "Carregando comentários...",
"user_disabled": "Os comentários estão desativados nas definições."
"pinned_by": "Afixado por {author}",
"disabled": "Os comentários estão desactivados pelo dono do canal.",
"loading": "A carregar comentários...",
"user_disabled": "Os comentários estão desactivados nas definições."
},
"subscriptions": {
"subscribed_channels_count": "{0} subscrito"
"subscribed_channels_count": "Subscrito a: {0}"
},
"info": {
"copied": "Copiada!",
"cannot_copy": "Não foi possível copiar!",
"page_not_found": "Página não encontrada",
"local_storage": "Esta ação requer localStorage, os cookies estão ativados?",
"preferences_note": "Nota: as configurações são guardadas no armazenamento local to seu navegador. Eliminar os dados de navegação irá redefini-las.",
"register_no_email_note": "Não recomendamos utilizar um endereço de email como nome de utilizador. Continuar?",
"next_video_countdown": "A reproduzir o vídeo seguinte em {0}s"
}
}

View file

@ -1,49 +1,49 @@
{
"actions": {
"view_subscriptions": "Ver inscrições",
"view_subscriptions": "Ver Inscrições",
"back": "Voltar",
"most_recent": "Mais recente",
"least_recent": "Menos recente",
"most_recent": "Mais Recente",
"least_recent": "Menos Recente",
"sort_by": "Ordenar por:",
"channel_name_asc": "Nome do Canal (A-Z)",
"channel_name_desc": "Nome do Canal (Z-A)",
"dark": "Escuro",
"light": "Claro",
"show_comments": "Mostrar comentários",
"show_comments": "Exibir Comentários",
"country_selection": "Seleção de País",
"default_homepage": "Página inicial (padrão)",
"default_quality": "Qualidade padrão",
"autoplay_video": "Reprodução automática",
"minimize_description_default": "Minimizar descrição por padrão",
"default_homepage": "Página Inicial Padrão",
"default_quality": "Qualidade Padrão",
"autoplay_video": "Reprodução Automática",
"minimize_description_default": "Minimizar Descrição por padrão",
"theme": "Tema",
"audio_only": "Apenas áudio",
"audio_only": "Apenas Áudio",
"subscribe": "Inscrever-se - {count}",
"unsubscribe": "Desinscrever-se - {count}",
"skip_sponsors": "Pular patrocinadores",
"skip_sponsors": "Pular Patrocinadores",
"auto": "Automático",
"uses_api_from": "Usa a API de ",
"enable_sponsorblock": "Habilitar Sponsorblock",
"enable_sponsorblock": "Ativar Sponsorblock",
"skip_interaction": "Pular Lembrete de Interação (Inscrever-se)",
"skip_self_promo": "Pular Promoção não paga/Autopromoção",
"show_markers": "Exibir marcadores no player",
"show_markers": "Exibir Marcadores no Player",
"skip_intro": "Pular Intervalo/Introdução Animada",
"skip_outro": "Pular Créditos/Cartões finais",
"skip_preview": "Pular Recapitulação",
"skip_highlight": "Pular destaque",
"buffering_goal": "Cache de buffer (em segundos)",
"skip_preview": "Pular Pré-Visualização/Recapitulação",
"skip_highlight": "Pular Destaque",
"buffering_goal": "Cache de Buffer (em segundos)",
"skip_non_music": "Pular Música: Seção não Musical",
"skip_filler_tangent": "Pular Enchimento Tangencial",
"enabled_codecs": "Habilitar Codecs (Múltiplos)",
"enabled_codecs": "Codecs Ativados (Múltiplos)",
"language_selection": "Seleção de Idioma",
"yes": "Sim",
"show_more": "Mostrar mais",
"export_to_json": "Exportar para JSON",
"donations": "Doações de desenvolvimento",
"minimize_recommendations": "Recolher recomendações",
"minimize_recommendations": "Minimizar Recomendações",
"loading": "Carregando...",
"hide_replies": "Esconder respostas",
"minimize_description": "Esconder descrição",
"load_more_replies": "Exibir mais respostas",
"hide_replies": "Ocultar Respostas",
"minimize_description": "Minimizar Descrição",
"load_more_replies": "Carregar mais Respostas",
"create_playlist": "Criar Playlist",
"delete_playlist": "Excluir Playlist",
"select_playlist": "Selecionar uma Playlist",
@ -53,20 +53,20 @@
"please_select_playlist": "Por favor, selecione uma playlist",
"remove_from_playlist": "Remover da playlist",
"view_ssl_score": "Ver Pontuação SSL",
"disable_lbry": "Desabilitar LBRY para Streaming",
"enable_lbry_proxy": "Habilitar proxy para LBRY",
"disable_lbry": "Desativar LBRY para Streaming",
"enable_lbry_proxy": "Ativar Proxy para LBRY",
"import_from_json": "Importar de JSON/CSV",
"loop_this_video": "Repetir este Vídeo",
"instances_list": "Lista de Instâncias",
"clear_history": "Limpar Histórico",
"search": "Pesquisar",
"search": "Pesquisar (Ctrl+K)",
"no": "Não",
"show_description": "Exibir descrição",
"show_description": "Exibir Descrição",
"instance_selection": "Seleção de Instância",
"auto_play_next_video": "Autoreproduzir vídeo seguinte",
"auto_play_next_video": "Autorreproduzir Próximo Vídeo",
"filter": "Filtro",
"store_watch_history": "Salvar Histórico de Exibição",
"show_recommendations": "Mostrar recomendações",
"show_recommendations": "Exibir Recomendações",
"minimize_comments_default": "Minimizar Comentários por padrão",
"minimize_comments": "Minimizar Comentários",
"different_auth_instance": "Use uma instância diferente para autenticação",
@ -81,7 +81,7 @@
"status_page": "Estado",
"source_code": "Código fonte",
"instance_donations": "Doações de instâncias",
"instance_auth_selection": "Seleção de iIstância de Autenticação",
"instance_auth_selection": "Seleção de Instância de Autenticação",
"clone_playlist_success": "Clonada com sucesso!",
"download_as_txt": "Baixar como .txt",
"restore_preferences": "Restaurar preferências",
@ -91,7 +91,7 @@
"new_playlist_name": "Novo nome da playlist",
"with_timecode": "Compartilhar com código de tempo",
"piped_link": "Link do Piped",
"follow_link": "Siguir link",
"follow_link": "Seguir link",
"time_code": "Código de tempo (em segundos)",
"show_chapters": "Capítulos",
"confirm_reset_preferences": "Tem certeza de que deseja redefinir suas preferências?",
@ -99,7 +99,19 @@
"documentation": "Documentação",
"reply_count": "{count} respostas",
"minimize_recommendations_default": "Minimizar Recomendações por padrão",
"show_watch_on_youtube": "Mostrar Botão Assistir no YouTube"
"show_watch_on_youtube": "Mostrar Botão Assistir no YouTube",
"minimize_chapters_default": "Minimizar Capítulos por padrão",
"no_valid_playlists": "O arquivo não contém playlists válidas!",
"with_playlist": "Compartilhar com playlist",
"bookmark_playlist": "Favorito",
"playlist_bookmarked": "Favoritado",
"skip_automatically": "Automaticamente",
"skip_segment": "Ignorar Segmento",
"min_segment_length": "Comprimento Mínimo do Segmento (em segundos)",
"skip_button_only": "Mostrar botão pular",
"show_less": "Mostrar menos",
"autoplay_next_countdown": "Contagem regressiva padrão até o próximo vídeo (em segundos)",
"dismiss": "Liberar"
},
"titles": {
"history": "Histórico",
@ -114,10 +126,11 @@
"player": "Player",
"account": "Conta",
"channels": "Canais",
"livestreams": "Transmissões ao vivo"
"livestreams": "Transmissões ao vivo",
"bookmarks": "Favoritos"
},
"player": {
"watch_on": "Assistir no"
"watch_on": "Assistir no {0}"
},
"comment": {
"pinned_by": "Fixado por {author}",
@ -126,7 +139,7 @@
"loading": "Carregando comentários..."
},
"preferences": {
"registered_users": "Usuários cadastrados",
"registered_users": "Usuários Registrados",
"version": "Versão",
"instance_name": "Nome da Instância",
"instance_locations": "Localizações da Instância",
@ -144,9 +157,11 @@
"chapters": "Capítulos",
"live": "{0} Ao vivo",
"watched": "Assistido",
"ratings_disabled": "Avaliações desabilitadas",
"sponsor_segments": "Segmentos de patrocinadores",
"shorts": "Shorts"
"ratings_disabled": "Avaliações Desativadas",
"sponsor_segments": "Segmentos de Patrocinadores",
"shorts": "Shorts",
"all": "Todos",
"category": "Categoria"
},
"search": {
"did_you_mean": "Você quis dizer: {0}?",
@ -163,7 +178,10 @@
"copied": "Copiado!",
"cannot_copy": "Não foi possível copiar!",
"preferences_note": "Nota: as preferências são salvas no armazenamento local do seu navegador. A exclusão dos dados do seu navegador irá redefini-los.",
"page_not_found": "página não encontrada"
"page_not_found": "página não encontrada",
"local_storage": "Esta ação requer localStorage, os cookies estão ativados?",
"register_no_email_note": "Usar um e-mail como nome de usuário não é recomendado. Continuar mesmo assim?",
"next_video_countdown": "Reproduzindo o próximo vídeo em {0}s"
},
"subscriptions": {
"subscribed_channels_count": "Inscrito em: {0}"

View file

@ -10,7 +10,10 @@
"playlists": "Listas de Reprodução",
"account": "Conta",
"instance": "Instância",
"player": "Reprodutor"
"player": "Reprodutor",
"livestreams": "Transmissões ao vivo",
"channels": "Canais",
"bookmarks": "Marcadores"
},
"actions": {
"view_subscriptions": "Ver Subscrições",
@ -43,17 +46,17 @@
"language_selection": "Seleção de Idioma",
"enabled_codecs": "\"Codecs\" Activados (Vários)",
"instance_selection": "Seleção de Instância",
"show_more": "Mostrar Mais",
"show_more": "Mostrar mais",
"import_from_json": "Importar de JSON/CSV",
"loop_this_video": "Repetir este Vídeo",
"auto_play_next_video": "Reproduzir Automaticamente o próximo Vídeo",
"donations": "Doações",
"donations": "Doações de desenvolvimento",
"minimize_description": "Minimizar Descrição",
"show_description": "Mostrar Descrição",
"minimize_recommendations": "Minimizar Recomendações",
"show_recommendations": "Mostrar Recomendações",
"view_ssl_score": "Ver Pontuação \"SSL\"",
"search": "Procurar",
"search": "Pesquisa (Ctrl+K)",
"hide_replies": "Ocultar Respostas",
"load_more_replies": "Carregar mais Respostas",
"unsubscribe": "Anular subscrição - {count}",
@ -69,7 +72,7 @@
"no": "Não",
"filter": "Filtrar",
"clear_history": "Limpar Histórico",
"disable_lbry": "Desactivar \"LBRY\" para Transmissão",
"disable_lbry": "Desactivar \"LBRY\" para \"Streaming\"",
"loading": "A Carregar...",
"please_select_playlist": "Selecionar uma lista de reprodução se faz favor",
"select_playlist": "Selecionar uma Lista de Reprodução",
@ -102,7 +105,29 @@
"reset_preferences": "Redefinir preferências",
"backup_preferences": "Exportar configurações",
"back_to_home": "Voltar ao início",
"minimize_comments_default": "Minimizar Comentários por defeito"
"minimize_comments_default": "Minimizar Comentários por defeito",
"store_search_history": "Armazenar Histórico de Pesquisa",
"minimize_chapters_default": "Minimizar Capítulos por padrão",
"show_watch_on_youtube": "Mostrar Botão Assistir no YouTube",
"show_chapters": "Capítulos",
"hide_watched": "Ocultar vídeos assistidos no feed",
"documentation": "Documentação",
"status_page": "Estado",
"minimize_comments": "Minimizar Comentários",
"reply_count": "{count} respostas",
"source_code": "Código-fonte",
"instance_donations": "Doações de instâncias",
"no_valid_playlists": "O ficheiro não contém listas de reprodução válidas!",
"bookmark_playlist": "Marcador",
"playlist_bookmarked": "Marcado",
"with_playlist": "Partilhar com lista de reprodução",
"skip_button_only": "Mostrar botão saltar",
"skip_automatically": "Automaticamente",
"min_segment_length": "Comprimento Mínimo do Segmento (em segundos)",
"skip_segment": "Saltar Segmento",
"show_less": "Mostrar menos",
"autoplay_next_countdown": "Predefinição Contagem decrescente até ao próximo vídeo (em segundos)",
"dismiss": "Ignorar"
},
"comment": {
"pinned_by": "Afixado por {author}",
@ -131,7 +156,9 @@
"ratings_disabled": "Classificações Desactivadas",
"chapters": "Capítulos",
"live": "{0} em Direto",
"shorts": "\"Shorts\""
"shorts": "\"Shorts\"",
"all": "Todos",
"category": "Categoria"
},
"search": {
"did_you_mean": "Será que querias dizer: {0}?",
@ -154,6 +181,9 @@
"preferences_note": "Nota: as configurações são guardadas no armazenamento local to seu navegador. Eliminar os dados de navegação irá redefini-las.",
"page_not_found": "Página não encontrada",
"copied": "Copiada!",
"cannot_copy": "Não foi possível copiar!"
"cannot_copy": "Não foi possível copiar!",
"local_storage": "Esta ação requer localStorage, os cookies estão ativados?",
"register_no_email_note": "A utilização de um e-mail como nome de utilizador não é recomendada. Proceder de qualquer forma?",
"next_video_countdown": "A reproduzir o vídeo seguinte em {0}s"
}
}

189
src/locales/ro.json Normal file
View file

@ -0,0 +1,189 @@
{
"actions": {
"back_to_home": "Înapoi acasă",
"store_search_history": "Rețineți istoricul de căutări",
"with_timecode": "Distribuiți cu timpul de cod",
"piped_link": "Link Piped",
"time_code": "Cod de timp (secunde)",
"show_chapters": "Capitole",
"search": "Căutare (Ctrl+K)",
"logout": "Deconectați-vă de pe acest dispozitiv",
"add_to_playlist": "Adăugare în playlist",
"remove_from_playlist": "Ștergere din playlist",
"create_playlist": "Creați playlist",
"delete_playlist": "Ștergeți playlist",
"delete_playlist_confirm": "Ștergeți acest playlist?",
"please_select_playlist": "Vă rugăm să alegeți un playlist",
"minimize_recommendations_default": "Minimizați recomandările în mod implicit",
"subscribe": "Abonare - {count}",
"least_recent": "Cele mai vechi",
"channel_name_asc": "Numele canalului (A-Z)",
"channel_name_desc": "Nume canalului (Z-A)",
"back": "Înapoi",
"uses_api_from": "Se folosește API-ul de la ",
"enable_sponsorblock": "Activați Sponsorblock",
"skip_intro": "Omitere pauze/animații de intro",
"skip_preview": "Omitere previzualizare/recapitulare",
"skip_self_promo": "Omitere promoție neplătită/autopromovare",
"skip_non_music": "Omitere muzică: Secțiune non-muzicală",
"skip_highlight": "Omitere evidențiere",
"show_markers": "Se afișează marcatori în player",
"dark": "Întunecat",
"auto": "Auto",
"audio_only": "Doar audio",
"default_quality": "Calitate implicită",
"country_selection": "Selecție țară",
"default_homepage": "Pagina principală implicită",
"minimize_comments_default": "Minimizați comentariile în mod implicit",
"minimize_description_default": "Minimizați descrierea în mod implicit",
"language_selection": "Selecție limbă",
"instances_list": "Listă de Instanțe",
"enabled_codecs": "Codecuri activate (multiple)",
"loop_this_video": "Repetare video",
"donations": "Donații pentru dezvoltare",
"show_recommendations": "Afișați recomandările",
"disable_lbry": "Dezactivați LBRY pentru streaming",
"enable_lbry_proxy": "Activați proxy pentru LBRY",
"view_ssl_score": "Vedeți scorul SSL",
"filter": "Filtru",
"loading": "Se încarcă...",
"clear_history": "Ștergeți istoricul",
"hide_replies": "Ascundeți răspunsurile",
"load_more_replies": "Mai multe răspunsuri",
"delete_playlist_video_confirm": "Ștergeți videoclipul din playlist?",
"select_playlist": "Selectați un playlist",
"delete_account": "Ștergeți-vă contul",
"show_watch_on_youtube": "Afișați butonul „Vizionați pe YouTube”",
"invalidate_session": "Deconectați toate dispozitivele",
"instance_auth_selection": "Selecție instanță de autentificare",
"clone_playlist_success": "Clonată cu succes!",
"reset_preferences": "Resetați preferințele",
"confirm_reset_preferences": "Sunteți sigur că doriți să vă resetați preferințele?",
"rename_playlist": "Redenumiți playlist-ul",
"new_playlist_name": "Numele playlist-ului nou",
"share": "Distribuiți",
"follow_link": "Urmați link-ul",
"copy_link": "Copiați link-ul",
"hide_watched": "Ascundeți videoclipurile vizionate din flux",
"documentation": "Documentație",
"status_page": "Status",
"source_code": "Cod sursă",
"instance_donations": "Donații instanță",
"reply_count": "{count} răspunsuri",
"minimize_chapters_default": "Minimizați capitolele în mod implicit",
"skip_sponsors": "Omitere sponsori",
"different_auth_instance": "Folosiți o instanță diferită pentru autentificare",
"clone_playlist": "Clonați lista de redare",
"backup_preferences": "Faceți backup la preferințe",
"unsubscribe": "Dezabonare - {count}",
"view_subscriptions": "Vedeți abonamentele",
"sort_by": "Sortare după:",
"download_as_txt": "Descărcați ca .txt",
"most_recent": "Cele mai recente",
"skip_outro": "Omitere carduri de sfârșit/mulțumiri",
"skip_interaction": "Omitere reamintiri de interacțiune (abonare)",
"light": "Luminat",
"restore_preferences": "Restaurați preferințele",
"skip_filler_tangent": "Omitere tangentă de umplere",
"theme": "Temă",
"autoplay_video": "Redare automată video",
"buffering_goal": "Obiectiv de tamponare (în secunde)",
"instance_selection": "Selecție instanță",
"store_watch_history": "Rețineți istoricul de vizionări",
"minimize_comments": "Minimizați comentariile",
"minimize_description": "Minimizați descrierea",
"show_more": "Mai mult",
"no": "Nu",
"export_to_json": "Exportați ca JSON",
"import_from_json": "Importați din JSON/CSV",
"auto_play_next_video": "Redați automat următorul video",
"minimize_recommendations": "Minimizați recomandările",
"yes": "Da",
"show_comments": "Afișați comentariile",
"show_description": "Afișați descrierea",
"bookmark_playlist": "Marcați",
"no_valid_playlists": "Fișierul nu conține playlist-uri valide!",
"skip_automatically": "Automat",
"min_segment_length": "Lungimea minimă a segmentului (în secunde)",
"skip_segment": "Omitere segment",
"skip_button_only": "Afișați butonul de omitere",
"with_playlist": "Distribuiți cu playlist",
"playlist_bookmarked": "Marcat",
"show_less": "Mai puțin",
"autoplay_next_countdown": "Numărătoarea inversă implicită până la următorul videoclip (în secunde)",
"dismiss": "Concediază"
},
"preferences": {
"ssl_score": "Scor SSL",
"version": "Versiune",
"up_to_date": "Actualizat?",
"instance_name": "Nume instanță",
"instance_locations": "Locațiile instanței",
"has_cdn": "Are CDN?",
"registered_users": "Utilizatori înregistrați"
},
"comment": {
"user_disabled": "Comentariile sunt dezactivate în setări.",
"pinned_by": "Fixat de {author}",
"disabled": "Comentariile sunt dezactivate de către autor.",
"loading": "Se încarcă comentariile..."
},
"video": {
"views": "{views} vizionări",
"chapters": "Capitole",
"shorts": "Shorts",
"watched": "Vizionat",
"sponsor_segments": "Segmente sponsori",
"ratings_disabled": "Evaluări dezactivate",
"live": "{0} în direct",
"videos": "Videoclipuri",
"category": "Categorie",
"all": "Tot"
},
"login": {
"username": "Nume de utilizator",
"password": "Parolă"
},
"search": {
"videos": "YouTube: Videoclipuri",
"music_playlists": "YT Music: Liste de redare",
"did_you_mean": "Vă refereați la: {0}?",
"all": "YouTube: Toate",
"channels": "YouTube: Canale",
"playlists": "YouTube: Liste de redare",
"music_songs": "YT Music: Muzică",
"music_videos": "YT Music: Videoclipuri",
"music_albums": "YT Music: Albume"
},
"info": {
"cannot_copy": "Nu s-a putut copia!",
"preferences_note": "Notă: preferințele sunt salvate în memoria locală a browserului dvs. Ștergerea datelor din browserul dvs. le va reseta.",
"page_not_found": "Pagina nu a fost găsită",
"copied": "Copiat!",
"register_no_email_note": "Utilizarea unui e-mail ca nume de utilizator nu este recomandată. Continuați oricum?",
"local_storage": "Această acțiune necesită localStorage, sunt activate cookie-urile?",
"next_video_countdown": "Redarea următorului videoclip în {0}s"
},
"subscriptions": {
"subscribed_channels_count": "Abonat la: {0}"
},
"titles": {
"register": "Înregistrare",
"history": "Istoric",
"subscriptions": "Abonamente",
"playlists": "Liste de redare",
"account": "Cont",
"instance": "Instanță",
"login": "Autentificare",
"feed": "Flux",
"trending": "Tendințe",
"livestreams": "Fluxuri live",
"channels": "Canale",
"preferences": "Preferințe",
"player": "Player-ul",
"bookmarks": "Marcaje"
},
"player": {
"watch_on": "Vizionați pe {0}"
}
}

View file

@ -5,12 +5,15 @@
"register": "Регистрация",
"feed": "Подписки",
"preferences": "Настройки",
"history": "История просмотров",
"subscriptions": "Ваши подписки",
"history": "История",
"subscriptions": "Подписки",
"playlists": "Плейлисты",
"account": "Аккаунт",
"player": "Плеер",
"instance": "Сервер"
"instance": "Сервер",
"livestreams": "Прямые трансляции",
"channels": "Каналы",
"bookmarks": "Закладки"
},
"player": {
"watch_on": "Смотреть на {0}"
@ -25,7 +28,7 @@
"channel_name_asc": "Имя канала (А-Я)",
"channel_name_desc": "Имя канала (Я-А)",
"back": "Назад",
"uses_api_from": "Использовать API, предоставляемое ",
"uses_api_from": "Использовать API ",
"enable_sponsorblock": "Включить Sponsorblock",
"skip_sponsors": "Пропускать спонсорскую рекламу",
"skip_intro": "Пропускать заставку/интро",
@ -64,9 +67,9 @@
"minimize_recommendations": "Свернуть рекомендации",
"show_recommendations": "Показать рекомендации",
"disable_lbry": "Отключить LBRY для стриминга",
"enable_lbry_proxy": "Проксировать видео с LBRY",
"enable_lbry_proxy": "Проксировать видео для LBRY",
"view_ssl_score": "Посмотреть настройки SSL",
"search": "Поиск",
"search": "Поиск (Ctrl+K)",
"filter": "Фильтр",
"loading": "Загрузка...",
"clear_history": "Очистить историю",
@ -82,49 +85,60 @@
"select_playlist": "Выбрать плейлист",
"delete_playlist_confirm": "Удалить этот плейлист?",
"delete_playlist_video_confirm": "Удалить видео из плейлиста?",
"show_markers": "Показать Mаркеры Hа Проигрывателе",
"show_markers": "Показать маркеры на проигрывателе",
"delete_account": "Удалить аккаунт",
"logout": "Выйти из этого устройства",
"download_as_txt": "Скачать как .txt",
"minimize_recommendations_default": "Скрыть Рекомендации по умолчанию",
"invalidate_session": "Выйти из всех устройств",
"different_auth_instance": "Использовать другие средства аутентификации",
"instance_auth_selection": "Выбор средств аутентификации",
"different_auth_instance": "Использовать другое зеркало для аутентификации",
"instance_auth_selection": "Выбор зеркала аутентификации",
"clone_playlist": "Клонировать плейлист",
"clone_playlist_success": "Клонирование прошло успешно!",
"show_chapters": "Части",
"clone_playlist_success": "Успешно клонировано!",
"show_chapters": "Главы",
"rename_playlist": "Переименовать плейлист",
"new_playlist_name": "Новое название плейлиста",
"share": "Поделиться",
"with_timecode": "Поделиться с отметкой времени",
"with_timecode": "Поделиться с таймкодом",
"piped_link": "Ссылка Piped",
"follow_link": "Ссылка подписки",
"follow_link": "Перейти по ссылке",
"copy_link": "Скопировать ссылку",
"time_code": "Тайм-код (в секундах)",
"time_code": "Таймкод (в секундах)",
"reset_preferences": "Сбросить настройки",
"confirm_reset_preferences": "Вы уверены, что хотите сбросить настройки?",
"backup_preferences": "Настройки бэкапов",
"backup_preferences": "Бэкап настроек",
"restore_preferences": "Восстановить настройки",
"back_to_home": "Вернутся на главную",
"back_to_home": "Назад на главную",
"store_search_history": "Хранить историю поиска",
"hide_watched": "Скрыть просмотренные видео в ленте",
"status_page": "Статус",
"source_code": "Исходный код",
"documentation": "Пожертвования сервера",
"instance_donations": "Пожертвования сервера",
"documentation": "Документация",
"instance_donations": "Пожертвования зеркала",
"reply_count": "{count} ответов",
"minimize_comments_default": "Сворачивать комментарии по умолчанию",
"minimize_comments": "Свернуть комментарии"
"minimize_comments": "Свернуть комментарии",
"show_watch_on_youtube": "Показать кнопку Смотреть на YouTube",
"minimize_chapters_default": "Скрывать главы по умолчанию",
"no_valid_playlists": "Файл не содержит действующих плейлистов!",
"with_playlist": "Поделиться с плейлистом",
"bookmark_playlist": "Закладка",
"playlist_bookmarked": "В закладках",
"skip_automatically": "Автоматически",
"min_segment_length": "Минимальная длина сегмента (в секундах)",
"skip_button_only": "Показать кнопку \"Пропустить\"",
"skip_segment": "Пропустить сегмент",
"show_less": "Показать меньше"
},
"comment": {
"pinned_by": "Прикреплено пользователем {author}",
"pinned_by": "Закреплено пользователем {author}",
"loading": "Загрузка комментариев...",
"user_disabled": "Комментарии отключены в настройках.",
"disabled": "Коментарии отключены автором."
"disabled": "Комментарии отключены автором."
},
"preferences": {
"instance_name": "Название",
"instance_locations": "Местоположение",
"instance_name": "Имя зеркала",
"instance_locations": "Местоположения зеркала",
"has_cdn": "Имеется CDN?",
"ssl_score": "Оценка настроек SSL",
"registered_users": "Зарегистрировано пользователей",
@ -132,7 +146,7 @@
"up_to_date": "Версия актуальна?"
},
"login": {
"username": "Аккаунт на Piped",
"username": "Имя пользователя",
"password": "Пароль"
},
"video": {
@ -143,7 +157,9 @@
"ratings_disabled": "Оценки отключены",
"live": "{0} В эфире",
"chapters": "Содержание",
"shorts": "Shorts"
"shorts": "Shorts",
"all": "Все",
"category": "Категория"
},
"search": {
"did_you_mean": "Может быть вы имели в виду: {0}?",
@ -160,9 +176,11 @@
"subscribed_channels_count": "Подписан на: {0}"
},
"info": {
"preferences_note": "Примечание: настройки сохранены в локальном хранилище браузера. При удалении данных браузера они будут удалены.",
"preferences_note": "Примечание: настройки сохранены в локальном хранилище браузера. Удаление данных вашего браузера сбросит их.",
"copied": "Скопировано!",
"cannot_copy": "Не получилось скопировать!",
"page_not_found": "Страница не найдена"
"cannot_copy": "Не удалось скопировать!",
"page_not_found": "Страница не найдена",
"local_storage": "Это действие требует разрешения localStorage, включены ли cookie-файлы?",
"register_no_email_note": "Использование электронной почты в качестве имени пользователя не рекомендуется. Продолжить?"
}
}

186
src/locales/si.json Normal file
View file

@ -0,0 +1,186 @@
{
"titles": {
"trending": "නැගී එන",
"login": "ඇතුළු වන්න",
"register": "ලියාපදිංචි වන්න",
"preferences": "සැකසුම්",
"history": "ඉතිහාසය",
"subscriptions": "දායකත්ව",
"account": "ගිණුම",
"player": "වාදකය",
"livestreams": "සජීවී ප්‍රවාහ",
"channels": "නාලිකා",
"playlists": "වාදන ලැයිස්තු",
"instance": "සේවාදායකය",
"bookmarks": "පොත් සලකුණු",
"feed": "නවතම"
},
"actions": {
"subscribe": "දායකවන්න - {count}",
"unsubscribe": "දායක නොවන්න - {count}",
"most_recent": "නවතම",
"least_recent": "පැරණිතම",
"channel_name_asc": "නාලිකාවේ නම (A-Z)",
"channel_name_desc": "නාලිකාවේ නම (Z-A)",
"back": "ආපසු",
"skip_sponsors": "අනුග්‍රහ මඟ හරින්න",
"skip_outro": "අවසන් කාඩ්පත/දායක ලැයිස්තුව මඟ හරින්න",
"skip_preview": "පෙරදසුන/සාරාංශය මඟ හරින්න",
"skip_self_promo": "නොගෙවූ/ස්වයං ප්‍රවර්ධන මඟ හරින්න",
"skip_filler_tangent": "අදාළ නොවන කොටස් මඟහරින්න",
"theme": "පෙනුම",
"dark": "අඳුරු",
"light": "එළිය",
"autoplay_video": "ස්වයංක්‍රීයව වීඩියෝව වාදනය කරන්න",
"auto": "ස්වයං තේරීම",
"default_quality": "පෙරනිමි ගුණත්වය",
"default_homepage": "පෙරනිමි මුල් පිටුව",
"show_markers": "වාදකයේ මාකර් පෙන්වන්න",
"buffering_goal": "බෆරින් ඉලක්කය (තත්පර වලින්)",
"enable_sponsorblock": "Sponsorblock සබල කරන්න",
"sort_by": "තේරීම:",
"skip_highlight": "ඉස්මතු කිරීම් මඟ හරින්න",
"language_selection": "භාෂා තේරීම",
"show_more": "තව පෙන්වන්න",
"yes": "ඔව්",
"no": "නැත",
"export_to_json": "JSON වෙත අපනයනය කරන්න",
"import_from_json": "JSON/CSV වෙතින් ආනයනය කරන්න",
"loop_this_video": "මෙම වීඩියෝව ලූප් කරන්න",
"auto_play_next_video": "මීළඟ වීඩියෝව ස්වයංව වාදනය කරන්න",
"donations": "සංවර්ධන පරිත්‍යාග",
"minimize_comments": "අදහස් සඟවන්න",
"show_comments": "අදහස් පෙන්වන්න",
"minimize_description": "විස්තරය සඟවන්න",
"show_description": "විස්තරය පෙන්වන්න",
"minimize_recommendations": "නිර්දේශ සඟවන්න",
"show_recommendations": "නිර්දේශ පෙන්වන්න",
"store_watch_history": "නැරඹීමේ ඉතිහාසය ගබඩා කරන්න",
"enabled_codecs": "සබල කර ඇති කෝඩෙක්ස් (බහු)",
"minimize_description_default": "පෙරනිමියෙන් විස්තරය සඟවන්න",
"instances_list": "සේවාදායක ලැයිස්තුව",
"instance_selection": "සේවාදායකය තේරීම",
"view_ssl_score": "SSL ලකුණු බලන්න",
"search": "සොයන්න (Ctrl+K)",
"loading": "පූරණය වෙමින්...",
"hide_replies": "පිළිතුරු සඟවන්න",
"load_more_replies": "තවත් පිළිතුරු පූරණය කරන්න",
"add_to_playlist": "වාදන ලැයිස්තුවට එක් කරන්න",
"create_playlist": "වාදන ලැයිස්තුව සාදන්න",
"delete_playlist": "වාදන ලැයිස්තුව මකන්න",
"select_playlist": "වාදන ලැයිස්තුවක් තෝරන්න",
"please_select_playlist": "කරුණාකර වාදන ලැයිස්තුවක් තෝරන්න",
"delete_account": "ගිණුම මකන්න",
"logout": "මෙම උපාංගයෙන් වරනය වන්න",
"minimize_recommendations_default": "පෙරනිමියෙන් නිර්දේශ සඟවන්න",
"invalidate_session": "සියලුම උපාංග වලින් වරනය වන්න",
"clone_playlist": "වාදන ලැයිස්තුව ක්ලෝනය කරන්න",
"download_as_txt": ".txt ලෙස බාගන්න",
"reset_preferences": "සැකසුම් නැවත සකසන්න",
"backup_preferences": "සැකසුම් උපස්ථ කරන්න",
"restore_preferences": "සැකසුම් නැවත පිහිටුවන්න",
"back_to_home": "ආපසු මුල් පිටුවට",
"rename_playlist": "වාදන ලැයිස්තුව නැවත නම් කරන්න",
"share": "බෙදාගන්න",
"with_timecode": "කාල කේතය සමඟ බෙදා ගන්න",
"piped_link": "පයිප්ඩ් සබැඳිය",
"copy_link": "සබැඳිය පිටපත් කරන්න",
"time_code": "කාල කේතය (තත්පර වලින්)",
"show_chapters": "පරිච්ඡේද",
"status_page": "තත්ත්වය",
"source_code": "ප්‍රභව කේතය",
"documentation": "ප්‍රලේඛනය",
"reply_count": "පිළිතුරු {count}",
"with_playlist": "වාදන ලැයිස්තුව සමඟ බෙදා ගන්න",
"bookmark_playlist": "පොත් සලකුණ",
"show_watch_on_youtube": "YouTube එකේ නරඹන්න බොත්තම පෙන්වන්න",
"filter": "පෙරහන",
"instance_donations": "සේවාදායක පරිත්යාග",
"instance_auth_selection": "සත්‍යතාව තහවුරු කිරීම සඳහා සේවාදායකයක් තේරීම",
"view_subscriptions": "දායකත්ව බලන්න",
"uses_api_from": "මොවුන්ගේ API භාවිතා වේ ",
"skip_intro": "විරාම/හඳුන්වාදීමේ සජීවිකරණය මඟ හරින්න",
"skip_interaction": "අන්තර් ක්‍රියා මතක් කිරීම මඟ හරින්න (දායක වන්න)",
"skip_non_music": "ගීත: ගීතය නොවන කොටස මඟ හරින්න",
"remove_from_playlist": "වාදන ලැයිස්තුවෙන් ඉවත් කරන්න",
"audio_only": "ශ්‍රව්‍ය පමණක්",
"country_selection": "රට තේරීම",
"minimize_comments_default": "පෙරනිමියෙන් අදහස් සඟවන්න",
"clear_history": "ඉතිහාසය හිස් කරන්න",
"disable_lbry": "ප්‍රවාහය සඳහා LBRY අබල කරන්න",
"delete_playlist_video_confirm": "වාදන ලැයිස්තුවෙන් වීඩියෝව ඉවත් කරන්නද?",
"delete_playlist_confirm": "මෙම වාදන ලැයිස්තුව මකන්නද?",
"minimize_chapters_default": "පෙරනිමියෙන් පරිච්ඡේද සඟවන්න",
"clone_playlist_success": "සාර්ථකව ක්ලෝන කරන ලදී!",
"confirm_reset_preferences": "ඔබට ඔබේ සැකසුම් යළි සැකසීමට අවශ්‍ය බව විශ්වාසද?",
"new_playlist_name": "නව වාදන ලැයිස්තුවේ නම",
"follow_link": "සබැඳිය අනුගමනය කරන්න",
"store_search_history": "සෙවුම් ඉතිහාසය ගබඩා කරන්න",
"no_valid_playlists": "ගොනුවේ වලංගු වාදන ලැයිස්තු අඩංගු නොවේ!",
"playlist_bookmarked": "පොත් සලකුණු කර ඇත",
"enable_lbry_proxy": "LBRY සඳහා Proxy සබල කරන්න",
"different_auth_instance": "සත්‍යතාව තහවුරු කිරීම සඳහා වෙනත් සේවාදායකයක් භාවිතා කරන්න",
"hide_watched": "නවතම කොටසෙහි නැරඹූ වීඩියෝ සඟවන්න",
"skip_button_only": "මඟ හරින්න බොත්තම පෙන්වන්න",
"skip_automatically": "ස්වයංක්‍රීයව",
"skip_segment": "කොටස මඟ හරින්න",
"min_segment_length": "අවම කොටස් දිග (තත්පර වලින්)",
"show_less": "අඩුවෙන් පෙන්වන්න"
},
"player": {
"watch_on": "{0} එකේ නරඹන්න"
},
"comment": {
"pinned_by": "{author} විසින් අමුණන ලදී",
"loading": "අදහස් පූරණය වෙමින්...",
"disabled": "උඩුගත කරන්නා විසින් අදහස් අබල කර ඇත.",
"user_disabled": "සැකසුම් තුළ අදහස් අබල කර ඇත."
},
"preferences": {
"has_cdn": "CDN තිබේද?",
"version": "නිකුතු අංකය",
"up_to_date": "යාවත්කාලීනද?",
"instance_name": "සේවාදායකයේ නම",
"registered_users": "ලියාපදිංචි පරිශීලකයන්",
"ssl_score": "SSL ලකුණු",
"instance_locations": "සේවාදායක ස්ථාන"
},
"login": {
"username": "පරිශීලක නාමය",
"password": "මුරපදය"
},
"video": {
"videos": "වීඩියෝ",
"views": "බැලීම් {views}",
"watched": "නැරඹුවා",
"sponsor_segments": "අනුග්‍රාහක අංශ",
"chapters": "පරිච්ඡේද",
"shorts": "කෙටි වීඩියෝ",
"ratings_disabled": "ශ්‍රේණිගත කිරීම් අබල කර ඇත",
"live": "{0} සජීවී",
"all": "සියල්ල",
"category": "කාණ්ඩය"
},
"search": {
"did_you_mean": "ඔබ අදහස් කළේ: {0}?",
"videos": "YouTube: වීඩියෝ",
"playlists": "YouTube: වාදන ලැයිස්තු",
"music_songs": "YT Music: ගීත",
"music_videos": "YT Music: වීඩියෝ",
"music_albums": "YT Music: ඇල්බම",
"music_playlists": "YT Music: වාදන ලැයිස්තු",
"channels": "YouTube: නාලිකා",
"all": "YouTube: සියල්ල"
},
"info": {
"page_not_found": "පිටුව හමු නොවීය",
"copied": "පිටපත් කළා!",
"cannot_copy": "පිටපත් කළ නොහැක!",
"local_storage": "මෙම ක්‍රියාවට localStorage අවශ්‍ය වේ, cookies සබල කර තිබේද?",
"register_no_email_note": "පරිශීලක නාමය ලෙස විද්‍යුත් තැපෑලක් භාවිතා කිරීම නිර්දේශ නොකරයි. කෙසේ හෝ ඉදිරියට යන්නද?",
"preferences_note": "සටහන: සැකසුම් ඔබගේ බ්‍රවුසරයේ දේශීය ගබඩාවේ සුරකිනු ලැබේ. ඔබගේ බ්‍රවුසර දත්ත මැකීමෙන් ඒවා නැවත සකසනු ඇත."
},
"subscriptions": {
"subscribed_channels_count": "දායක වූයේ: {0}"
}
}

View file

@ -7,7 +7,9 @@
"ratings_disabled": "Оцене су онемогућене",
"chapters": "Поглавља",
"live": "{0} Уживо",
"shorts": "Kratki video snimci"
"shorts": "Кратки видео снимци",
"all": "Све",
"category": "Категорија"
},
"actions": {
"view_ssl_score": "Погледај SSL скор/оцену",
@ -76,7 +78,7 @@
"add_to_playlist": "Додај у попис снимака",
"delete_playlist_confirm": "Избрисати овај попис снимака?",
"please_select_playlist": "Молим вас одаберите попис снимака",
"show_markers": "Prikaži obeleživače na plejeru",
"show_markers": "Прикажи обиљеживаче на покретнику",
"delete_account": "Обриши налог",
"logout": "Одјава са овог уређаја",
"minimize_recommendations_default": "Умањи Препоруке као Подразумевано",
@ -108,7 +110,18 @@
"new_playlist_name": "Ново име плејлисте",
"minimize_comments_default": "Подразумевано умањи коментаре",
"minimize_comments": "Умањи коментаре",
"reply_count": "{count} одговора"
"reply_count": "{count} одговора",
"minimize_chapters_default": "Умањи поглавља подразумевано",
"show_watch_on_youtube": "Прикажите \"Гредај на YouTube-у\" дугме",
"no_valid_playlists": "Датотека не садржи важеће пописе снимака!",
"with_playlist": "Делите са пописом снимака",
"playlist_bookmarked": "Обиљежено",
"bookmark_playlist": "Биљежак",
"show_less": "Прикажи мање",
"skip_button_only": "Прикажи дугме за прескакање",
"skip_automatically": "Аутоматски",
"min_segment_length": "Најмања дужина сегмента (у секундама)",
"skip_segment": "Прескочи сегмент"
},
"preferences": {
"instance_locations": "Локација инстанце",
@ -145,11 +158,14 @@
"playlists": "Пописи Снимака",
"account": "Рачун",
"instance": "Инстанца",
"player": "Плејер"
"player": "Покретник",
"livestreams": "Уживо преноси",
"channels": "Канали",
"bookmarks": "Биљешци"
},
"comment": {
"pinned_by": "Закачено од {author}",
"disabled": "Отпремалац је онемогућио коментаре.",
"disabled": "Преносник је онемогућио коментаре.",
"user_disabled": "Коментари су онемогућени у подешавањима.",
"loading": "Учитавање коментара..."
},
@ -163,6 +179,8 @@
"page_not_found": "Страница није пронађена",
"copied": "Копирано!",
"cannot_copy": "Није могуће копирати!",
"preferences_note": "Напомена: подешавања се чувају у локалној меморији вашег претраживача. Брисање података прегледача ће их ресетовати."
"preferences_note": "Напомена: подешавања се чувају у локалној меморији вашег претраживача. Брисање података прегледача ће их ресетовати.",
"local_storage": "Ова радња захтева локално складиште, да ли су колачићи омогућени ?",
"register_no_email_note": "Коришћење е-поруке као корисничког имена се не препоручује. Желите ли ипак наставити?"
}
}

View file

@ -142,5 +142,8 @@
},
"information": {
"preferences_note": "Observera: inställningar sparas i webbläsarens lokala lagring. Om du raderar dina webbläsardata återställs de."
},
"info": {
"register_no_email_note": "Det rekommenderas inte att använda e-post som användarnamn. Fortsätt ändå?"
}
}

View file

@ -7,7 +7,7 @@
"show_comments": "Yorumları Göster",
"default_homepage": "Öntanımlı Ana Sayfa",
"country_selection": "Ülke Seçimi",
"buffering_goal": "Arabelleğe Alma Hedefi (saniye cinsinden)",
"buffering_goal": "Arabelleğe Alma Hedefi (Saniye Cinsinden)",
"default_quality": "Öntanımlı Kalite",
"audio_only": "Yalnızca Ses",
"autoplay_video": "Videoyu Otomatik Oynat",
@ -31,25 +31,25 @@
"most_recent": "En Yeni",
"sort_by": "Sıralama ölçütü:",
"view_subscriptions": "Abonelikleri Görüntüle",
"unsubscribe": "Abonelikten çık - {count}",
"subscribe": "Abone ol - {count}",
"unsubscribe": "Abonelikten Çık - {count}",
"subscribe": "Abone Ol - {count}",
"enabled_codecs": "Etkin Çözücüler (Birden Çok)",
"enable_lbry_proxy": "LBRY için Vekil Sunucuyu Etkinleştir",
"disable_lbry": "Akış için LBRY'yi Devre Dışı Bırak",
"show_description": "Açıklamayı Göster",
"minimize_description": "Açıklamayı Küçült",
"donations": "Geliştirme bışları",
"donations": "Geliştirme Bışları",
"auto_play_next_video": "Sonraki Videoyu Otomatik Oynat",
"loop_this_video": "Bu Videoyu Döngüye Al",
"import_from_json": "JSON/CSV dosyasından içe aktar",
"import_from_json": "JSON/CSV Dosyasından İçe Aktar",
"export_to_json": "JSON Olarak Dışa Aktar",
"no": "Hayır",
"yes": "Evet",
"show_more": "Daha Fazla Göster",
"show_more": "Daha fazla göster",
"instance_selection": "Örnek Seçimi",
"loading": "Yükleniyor...",
"filter": "Filtrele",
"search": "Ara",
"search": "Ara (Ctrl+K)",
"view_ssl_score": "SSL Puanını Görüntüle",
"minimize_recommendations": "Önerileri Küçült",
"show_recommendations": "Önerileri Göster",
@ -60,55 +60,67 @@
"skip_filler_tangent": "Doldurma Sahnelerini Atla",
"delete_playlist_confirm": "Bu oynatma listesi silinsin mi?",
"delete_playlist_video_confirm": "Video oynatma listesinden kaldırılsın mı?",
"remove_from_playlist": "Oynatma listesinden kaldır",
"remove_from_playlist": "Oynatma Listesinden Kaldır",
"delete_playlist": "Oynatma Listesini Sil",
"add_to_playlist": "Oynatma listesine ekle",
"add_to_playlist": "Oynatma Listesine Ekle",
"create_playlist": "Oynatma Listesi Oluştur",
"select_playlist": "Oynatma Listesi Seç",
"please_select_playlist": "Lütfen bir oynatma listesi seçin",
"please_select_playlist": "Lütfen Bir Oynatma Listesi Seçin",
"show_markers": "Oynatıcıda İşaretçileri Göster",
"delete_account": "Hesabı Sil",
"logout": "Bu aygıttan oturumu kapat",
"logout": "Bu Aygıttan Oturumu Kapat",
"minimize_recommendations_default": "Önerileri Öntanımlı Olarak Küçült",
"different_auth_instance": "Kimlik doğrulama için farklı bir örnek kullan",
"invalidate_session": "Tüm aygıtlardan oturumu kapat",
"different_auth_instance": "Kimlik Doğrulama İçin Farklı Bir Örnek Kullan",
"invalidate_session": "Tüm Aygıtlardan Oturumu Kapat",
"instance_auth_selection": "Kimlik Doğrulama Örneği Seçimi",
"clone_playlist": "Oynatma Listesini Kopyala",
"clone_playlist_success": "Başarıyla kopyalandı!",
"download_as_txt": ".txt olarak indir",
"reset_preferences": "Tercihleri sıfırla",
"download_as_txt": ".txt Olarak İndir",
"reset_preferences": "Tercihleri Sıfırla",
"confirm_reset_preferences": "Tercihlerinizi sıfırlamak istediğinize emin misiniz?",
"backup_preferences": "Tercihleri yedekle",
"restore_preferences": "Tercihleri geri yükle",
"back_to_home": "Ana sayfaya dön",
"follow_link": "Bağlantıyı takip et",
"copy_link": "Bağlantıyı kopyala",
"time_code": "Zaman kodu (saniye cinsinden)",
"with_timecode": "Zaman koduyla paylaş",
"piped_link": "Piped bağlantısı",
"backup_preferences": "Tercihleri Yedekle",
"restore_preferences": "Tercihleri Geri Yükle",
"back_to_home": "Ana Sayfaya Dön",
"follow_link": "Bağlantıyı Takip Et",
"copy_link": "Bağlantıyı Kopyala",
"time_code": "Zaman Kodu (Saniye Cinsinden)",
"with_timecode": "Zaman Koduyla Paylaş",
"piped_link": "Piped Bağlantısı",
"share": "Paylaş",
"rename_playlist": "Oynatma listesini yeniden adlandır",
"new_playlist_name": "Yeni oynatma listesi adı",
"rename_playlist": "Oynatma Listesini Yeniden Adlandır",
"new_playlist_name": "Yeni Oynatma Listesi Adı",
"show_chapters": "Bölümler",
"store_search_history": "Arama geçmişini sakla",
"hide_watched": "Akışta izlenen videoları gizle",
"source_code": "Kaynak kodu",
"store_search_history": "Arama Geçmişini Sakla",
"hide_watched": "Akışta İzlenen Videoları Gizle",
"source_code": "Kaynak Kodu",
"documentation": "Belgelendirme",
"instance_donations": "Örnek bışları",
"instance_donations": "Örnek Bışları",
"status_page": "Durum",
"reply_count": "{count} yanıt",
"reply_count": "{count} Yanıt",
"minimize_comments": "Yorumları Küçült",
"minimize_comments_default": "Yorumları Öntanımlı Olarak Küçült",
"show_watch_on_youtube": "YouTube'da İzle düğmesini göster"
"show_watch_on_youtube": "YouTube'da İzle Düğmesini Göster",
"minimize_chapters_default": "Bölümleri Öntanımlı Olarak Küçült",
"no_valid_playlists": "Dosya geçerli oynatma listeleri içermiyor!",
"with_playlist": "Oynatma listesiyle paylaş",
"bookmark_playlist": "Yer imlerine ekle",
"playlist_bookmarked": "Yer imlerine eklendi",
"min_segment_length": "En Küçük Bölüm Uzunluğu (saniye cinsinden)",
"skip_segment": "Bölümü Atla",
"skip_button_only": "Atla düğmesini göster",
"skip_automatically": "Otomatik olarak",
"show_less": "Daha az göster",
"dismiss": "Kapat",
"autoplay_next_countdown": "Bir sonraki videoya kadar öntanımlı geri sayım (saniye cinsinden)"
},
"player": {
"watch_on": "{0} üzerinde izle"
"watch_on": "{0} Üzerinde İzle"
},
"titles": {
"history": "Geçmiş",
"preferences": "Tercihler",
"feed": "Akış",
"register": "Kaydol",
"register": "Kayıt Ol",
"login": "Oturum Aç",
"trending": "Öne Çıkanlar",
"subscriptions": "Abonelikler",
@ -117,17 +129,20 @@
"instance": "Örnek",
"player": "Oynatıcı",
"livestreams": "Canlı Yayınlar",
"channels": "Kanallar"
"channels": "Kanallar",
"bookmarks": "Yer İmleri"
},
"video": {
"sponsor_segments": "Sponsorlar Bölümleri",
"watched": "İzlendi",
"views": "{views} izlenme",
"videos": "Videolar",
"views": "{views} İzlenme",
"videos": "Video",
"ratings_disabled": "Derecelendirmeler Devre Dışı",
"chapters": "Bölümler",
"live": "{0} Canlı",
"shorts": "Kısa çekimler"
"shorts": "Kısa çekimler",
"all": "Tümü",
"category": "Kategori"
},
"preferences": {
"ssl_score": "SSL Puanı",
@ -136,17 +151,17 @@
"instance_name": "Örnek Adı",
"registered_users": "Kayıtlı Kullanıcılar",
"version": "Sürüm",
"up_to_date": "Güncel mi?"
"up_to_date": "Güncel Mi?"
},
"comment": {
"pinned_by": "Şunun tarafından sabitlendi {author}",
"pinned_by": "Şunun Tarafından Sabitlendi {author}",
"loading": "Yorumlar yükleniyor...",
"user_disabled": "Yorumlar ayarlarda devre dışı bırakıldı.",
"disabled": "Yorumlar yükleyen tarafından devre dışı bırakıldı."
},
"login": {
"password": "Parola",
"username": "Kullanıcı adı"
"username": "Kullanıcı Adı"
},
"search": {
"did_you_mean": "Bunu mu demek istediniz: {0}?",
@ -160,15 +175,18 @@
"music_albums": "YT Müzik: Albümler"
},
"subscriptions": {
"subscribed_channels_count": "Abone olunan: {0}"
"subscribed_channels_count": "Abone Olunan: {0}"
},
"information": {
"preferences_note": "Not: Tercihler tarayıcınızın yerel depolama alanına kaydedilir. Tarayıcı verilerinizi silmek onları sıfırlayacaktır."
},
"info": {
"preferences_note": "Not: Tercihler tarayıcınızın yerel depolama alanına kaydedilir. Tarayıcı verilerinizi silmek onları sıfırlayacaktır.",
"page_not_found": "Sayfa bulunamadı",
"page_not_found": "Sayfa Bulunamadı",
"copied": "Kopyalandı!",
"cannot_copy": "Kopyalanamıyor!"
"cannot_copy": "Kopyalanamıyor!",
"local_storage": "Bu eylem yerel depolama gerektirir, çerezler etkin mi?",
"register_no_email_note": "Kullanıcı adı olarak e-posta kullanılması tavsiye edilmez. Yine de devam edilsin mi?",
"next_video_countdown": "Sonraki video {0}s içinde oynatılıyor"
}
}

View file

@ -3,98 +3,153 @@
"watch_on": "Дивитися на {0}"
},
"login": {
"username": "Назва аккаунта Piped",
"username": "Ім'я користувача",
"password": "Пароль"
},
"actions": {
"unsubscribe": "Відписатись - {count}",
"unsubscribe": "Відписатися - {count}",
"back": "Назад",
"skip_intro": "Пропускати заставку/інтро",
"skip_intro": "Пропускати паузу/заставку",
"dark": "Темна",
"view_subscriptions": родивитися підписки",
"channel_name_asc": "Назва каналу (A-Z)",
"uses_api_from": "Використовувати API з ",
"view_subscriptions": ереглянути Підписки",
"channel_name_asc": "Назвою каналу (А)",
"uses_api_from": "Використовує API від ",
"enable_sponsorblock": "Увімкнути Sponsorblock",
"skip_outro": "Пропускати кінцівку/титри",
"skip_preview": "Пропускати короткий вміст поточного епізода або повтор частини минулого",
"skip_self_promo": "Пропускати саморекламу",
"autoplay_video": "Автоматичний програш відео",
"audio_only": "Лише звук",
"default_homepage": "За замовчуванням відкривати",
"show_comments": "Показувати коментарі",
"store_watch_history": "Зберігати історию переглянутих відео",
"skip_outro": "Пропускати кінцеву заставку/титри",
"skip_preview": "Пропускати попередній перегляд/короткий зміст",
"skip_self_promo": "Пропускати саморекламу/рекомендацію",
"autoplay_video": "Автоматичне відтворення відео",
"audio_only": "Лише аудіо",
"default_homepage": "Домашня сторінка за замовчуванням",
"show_comments": "Показати коментарі",
"store_watch_history": "Зберігати історію перегляду",
"language_selection": "Вибір мови",
"instance_selection": "Вибір копії сервіса Piped",
"instance_selection": "Вибір екземпляра",
"show_more": "Показати більше",
"no": "Ні",
"export_to_json": "Експорт в JSON",
"export_to_json": "Експортувати в JSON",
"minimize_description": "Згорнути опис",
"show_recommendations": "Показати рекомендації",
"enable_lbry_proxy": "Проксувати відео з LBRY",
"search": "Пошук",
"enable_lbry_proxy": "Увімкнути проксі для LBRY",
"search": "Пошук (Ctrl+K)",
"clear_history": "Очистити історію перегляду",
"load_more_replies": "Завантажити більше відповідей",
"subscribe": "Підписатись - {count}",
"sort_by": "Відсортувати по:",
"most_recent": "Найновіші",
"channel_name_desc": "Назва каналу (Z-A)",
"least_recent": "Найстарші",
"subscribe": "Підписатися - {count}",
"sort_by": "Сортувати за:",
"most_recent": "Найновішими",
"channel_name_desc": "Назвою каналу (Я-А)",
"least_recent": "Найстарішими",
"minimize_recommendations": "Згорнути рекомендації",
"skip_sponsors": "Пропускати спонсорську рекламу",
"skip_interaction": "Пропускати прохання підписатися",
"skip_non_music": "Пропускати тишу в музикальних відео",
"skip_interaction": "Пропускати нагадування про взаємодію (підписка)",
"skip_non_music": "Пропускати сегменти без музики в музикальних відео",
"theme": "Тема",
"auto": "Авто",
"light": "Світла",
"buffering_goal": "Розмір буфера відео (в секундах)",
"instances_list": "Список копій сервіса Piped",
"enabled_codecs": "Увімкнені кодеки (Можно вибрати декілька)",
"buffering_goal": "Розмір буфера відео (у секундах)",
"instances_list": "Список екземплярів",
"enabled_codecs": "Увімкнені кодеки (можна вибрати декілька)",
"default_quality": "Якість за замовчуванням",
"country_selection": "Вибір країни (для трендів)",
"minimize_description_default": "Не розгортати опис за замовчуванням",
"yes": "Так",
"import_from_json": "Імпорт з JSON/CSV",
"loop_this_video": "Повтор поточного відео",
"auto_play_next_video": "Одразу програвати наступне рекомендоване відео",
"donations": "Пожертвування",
"import_from_json": "Імпортувати з JSON/CSV",
"loop_this_video": "Зациклити це відео",
"auto_play_next_video": "Автоматичне відтворення наступного відео",
"donations": "Пожертвування на розробку",
"show_description": "Показати опис",
"disable_lbry": "Вимкнути LBRY для стримінгу",
"filter": "Фільтр",
"view_ssl_score": родивитися оцінку SSL",
"view_ssl_score": ереглянути оцінку SSL",
"loading": "Завантаження...",
"hide_replies": "Сховати відповіді",
"skip_highlight": "Пропустити Хайлайт",
"remove_from_playlist": "Видалити з плейлісту",
"add_to_playlist": "Додати до плейлісту",
"create_playlist": "Створити Плейліст",
"delete_playlist_confirm": "Чи ви певні що хочете видалити цей плейліст?",
"skip_filler_tangent": "Пропускати Нерелевантне",
"delete_playlist_video_confirm": "Чи певні ви що хочете видалити це відео з плейлісту?",
"delete_playlist": "Видалити Плейліст",
"select_playlist": "Вибрати Плейліст",
"please_select_playlist": "Будь ласка виберіть плейліст"
"skip_highlight": "Пропускати основне",
"remove_from_playlist": "Видалити зі списку відтворення",
"add_to_playlist": "Додати до списку відтворення",
"create_playlist": "Створити список відтворення",
"delete_playlist_confirm": "Видалити цей список відтворення?",
"skip_filler_tangent": "Пропускати дотичне наповнення/жарти",
"delete_playlist_video_confirm": "Видалити відео зі списку відтворення?",
"delete_playlist": "Видалити список відтворення",
"select_playlist": "Вибрати список відтворення",
"please_select_playlist": "Будь ласка, виберіть список відтворення",
"confirm_reset_preferences": "Ви впевнені, що бажаєте скинути свої налаштування?",
"show_markers": "Показувати маркери на Програвачі",
"minimize_recommendations_default": "Згортати рекомендації за замовчуванням",
"logout": "Вийти з цього пристрою",
"backup_preferences": "Налаштування резервного копіювання",
"download_as_txt": "Завантажити як .txt",
"rename_playlist": "Перейменувати список відтворення",
"show_chapters": "Розділи",
"invalidate_session": "Вийти з усіх пристроїв",
"clone_playlist": "Клонувати список відтворення",
"reset_preferences": "Скинути налаштування",
"back_to_home": "Повернутися на головну",
"share": "Поділитися",
"with_timecode": "Поділитися з відміткою часу",
"piped_link": "Посилання Piped",
"follow_link": "Перейти за посиланням",
"instance_donations": "Пожертвування екземпляра",
"copy_link": "Копіювати посилання",
"store_search_history": "Зберігати історію пошуку",
"documentation": "Документація",
"instance_auth_selection": "Вибір екземпляра для автентифікації",
"minimize_chapters_default": "Згортати розділи за замовчуванням",
"show_watch_on_youtube": "Показати кнопку Дивитися на YouTube",
"restore_preferences": "Відновити налаштування",
"different_auth_instance": "Використовувати інший екземпляр для автентифікації",
"clone_playlist_success": "Успішно клоновано!",
"hide_watched": "Сховати переглянуті відео в стрічці",
"status_page": "Статус",
"source_code": "Вихідний код",
"new_playlist_name": "Нова назва списку відтворення",
"time_code": "Відмітка часу (у секундах)",
"reply_count": "{count} відповідей",
"minimize_comments_default": "Згортати коментарі за замовчуванням",
"minimize_comments": "Згорнути коментарі",
"delete_account": "Видалити обліковий запис",
"no_valid_playlists": "Файл не містить дійсних списків відтворення!",
"bookmark_playlist": "Закладка",
"playlist_bookmarked": "Додано в закладки",
"with_playlist": "Поділитися зі списком відтворення",
"skip_button_only": "Показати кнопку пропуску",
"skip_segment": "Пропустити сегмент",
"skip_automatically": "Автоматично",
"min_segment_length": "Мінімальна довжина сегмента (у секундах)",
"show_less": "Показати менше",
"dismiss": "Відхилити",
"autoplay_next_countdown": "Зворотний відлік до наступного відео (у секундах)"
},
"titles": {
"register": "Реєстрація",
"feed": "Підписки",
"preferences": "Налаштування",
"history": "Історія переглядів",
"history": "Історія перегляду",
"subscriptions": "Канали, на які ви підписані",
"trending": "Тренди",
"login": "Логін",
"playlists": "Плейлісти"
"playlists": "Списки відтворення",
"instance": "Екземпляр",
"player": "Програвач",
"account": "Обліковий запис",
"livestreams": "Наживо",
"channels": "Канали",
"bookmarks": "Закладки"
},
"comment": {
"pinned_by": "Прикріплено користувачем {author}"
"pinned_by": "Прикріплено користувачем {author}",
"loading": "Завантаження коментарів...",
"disabled": "Коментарі вимкнені автором.",
"user_disabled": "Коментарі вимкнені в налаштуваннях."
},
"preferences": {
"instance_locations": "Місцезнаходження копії сервісу",
"instance_locations": "Місцезнаходження екземпляру",
"ssl_score": "Оцінка SSL",
"instance_name": "Назва копії сервісу",
"instance_name": "Назва екземпляру",
"has_cdn": "Використовує CDN?",
"version": "Версія",
"up_to_date": "Версія Актуальна?",
"registered_users": "Зареєстровано Користувачей"
"up_to_date": "Версія актуальна?",
"registered_users": "Зареєстровано користувачей"
},
"video": {
"videos": "Відео",
@ -103,17 +158,32 @@
"sponsor_segments": "Рекламні сегменти",
"ratings_disabled": "Оцінки вимкнені",
"chapters": "Розділи",
"live": "{0} Наживо"
"live": "{0} Наживо",
"shorts": "Shorts",
"all": "Усі",
"category": "Категорія"
},
"search": {
"did_you_mean": "Можливо, ви мали на увазі: {0}?",
"music_playlists": "YT Music: Плейлісти",
"music_playlists": "YT Music: Списки відтворення",
"all": "YouTube: Все",
"videos": "YouTube: Відео",
"channels": "YouTube: Канали",
"music_songs": "YT Music: Пісні",
"music_videos": "TY Music: Відео",
"playlists": "YouTube: Плейлісти",
"playlists": "YouTube: Списки відтворення",
"music_albums": "YT Music: Альбоми"
},
"subscriptions": {
"subscribed_channels_count": "Підписано на: {0}"
},
"info": {
"copied": "Скопійовано!",
"cannot_copy": "Не вийшло скопіювати!",
"page_not_found": "Сторінка не знайдена",
"preferences_note": "Примітка: налаштування зберігаються в локальній пам'яті вашого браузера. Видалення даних браузера призведе до їх скидання.",
"local_storage": "Ця дія потребує localStorage, чи ввімкнуті файли cookie?",
"register_no_email_note": "Використання електронної пошти як імені користувача не рекомендується. Все одно продовжити?",
"next_video_countdown": "Наступне відео через {0} секунд"
}
}

View file

@ -15,11 +15,11 @@
"auto": "Tự động",
"buffering_goal": "Bộ nhớ đệm (tính bằng giây)",
"least_recent": "Ít nhất gần đây",
"skip_intro": "Bỏ qua gián đoạn/Giới thiệu hoạt ảnh",
"skip_outro": "Bỏ qua màn hình kết thúc/Tín dụng",
"skip_intro": "Bỏ qua gián đoạn/hoạt hình intro",
"skip_outro": "Bỏ qua màn hình kết thúc/danh đề",
"skip_interaction": "Bỏ qua lời nhắc tương tác (Đăng ký)",
"skip_preview": "Bỏ qua xem trước/Tóm tắt",
"skip_self_promo": "Bỏ qua thanh toán/Tự quảng cáo",
"skip_preview": "Bỏ qua xem trước/tóm tắt",
"skip_self_promo": "Bỏ qua thanh toán/quảng cáo cho bản thân",
"skip_non_music": "Bỏ qua âm nhạc: Phần không phải âm nhạc",
"skip_highlight": "Bỏ qua phần đánh dấu",
"skip_filler_tangent": "Bỏ qua những đoạn không liên quan",
@ -28,7 +28,7 @@
"show_comments": "Hiển thị bình luận",
"store_watch_history": "Lịch sử xem trên cửa hàng",
"language_selection": "Lựa chọn ngôn ngữ",
"instances_list": "Danh sách phiên bản",
"instances_list": "Danh sách instance",
"show_more": "Hiện thị nhiều hơn",
"import_from_json": "Nhập từ JSON/CSV",
"loop_this_video": "Lặp lại video này",
@ -41,7 +41,7 @@
"minimize_recommendations": "Giảm thiểu các đề xuất",
"show_recommendations": "Hiển thị các đề xuất",
"disable_lbry": "Tắt LBRY để phát trực tuyến",
"enable_lbry_proxy": "Bật Proxy cho LBRY",
"enable_lbry_proxy": "Bật proxy cho LBRY",
"view_ssl_score": "Hiện thị điểm số SSL",
"search": "Tìm kiếm",
"filter": "Bộ lọc",
@ -53,34 +53,65 @@
"light": "Sáng",
"audio_only": "Chỉ có âm thanh",
"minimize_description_default": "Thu nhỏ mô tả theo mặc định",
"instance_selection": "Lựa chọn phiên bản",
"instance_selection": "Lựa chọn instance",
"yes": "Có",
"enabled_codecs": "Mã hóa được kích hoạt (Nhiều)",
"enabled_codecs": "Các codec được bật (Nhiều)",
"export_to_json": "Xuất định dạng JSON",
"no": "Không"
"no": "Không",
"remove_from_playlist": "Xóa khỏi danh sách phát",
"add_to_playlist": "Thêm vào danh sách phát",
"delete_playlist_video_confirm": "Bạn có chắc là muốn xóa video khỏi danh sách phát này không?",
"delete_playlist_confirm": "Bạn có chắc là bạn muốn xóa danh sách phát này không?",
"create_playlist": "Tạo danh sách phát",
"delete_playlist": "Xóa danh sách phát",
"select_playlist": "Chọn một danh sách phát",
"please_select_playlist": "Hãy chọn một danh sách phát",
"copy_link": "Sao chép liên kết",
"source_code": "Mã nguồn",
"piped_link": "Liên kết Piped",
"share": "Chia sẻ",
"minimize_comments_default": "Thu nhỏ bình luận theo mặc định",
"download_as_txt": "Tải về dưới định dạng .txt",
"delete_account": "Xóa tài khoản",
"minimize_recommendations_default": "Thu nhỏ đề xuất theo mặc định",
"logout": "Đăng xuất khỏi thiết bị này",
"minimize_comments": "Thu nhỏ bình luận",
"reply_count": "{count} phản hồi",
"status_page": "Trạng thái",
"new_playlist_name": "Tên danh sách phát mới",
"skip_automatically": "Tự động"
},
"titles": {
"register": "Đăng ký",
"preferences": "Sở thích",
"preferences": "Cài đặt",
"history": "Lịch sử",
"subscriptions": "Đăng ký",
"trending": "Xu hướng",
"login": "Đăng nhập",
"feed": "Mới nhất"
"feed": "Mới nhất",
"playlists": "Danh sách phát",
"account": "Tài khoản",
"channels": "Kênh",
"instance": "Instance",
"player": "Trình phát video",
"livestreams": "Phát sóng trực tiếp"
},
"player": {
"watch_on": "Xem trên {0}"
},
"comment": {
"pinned_by": "Được ghim bởi {author}"
"pinned_by": "Được ghim bởi {author}",
"loading": "Đang tải bình luận...",
"user_disabled": "Bình luận đã được tắt trong cài đặt.",
"disabled": "Bình luận đã bị tắt bởi người đăng video."
},
"preferences": {
"instance_name": "Tên phiên bản",
"instance_locations": "Vị trí phiên bản",
"instance_name": "Tên instance",
"instance_locations": "Vị trí instance",
"has_cdn": "Có CDN?",
"registered_users": "Người dùng đã đăng ký",
"version": "Phiên bản",
"up_to_date": "Cập nhật?",
"up_to_date": "Đã được cập nhật?",
"ssl_score": "Điểm SSL"
},
"login": {
@ -94,7 +125,9 @@
"ratings_disabled": "Xếp hạng đã tắt",
"live": "{0} Trực tiếp",
"chapters": "Chương",
"videos": "Video"
"videos": "Video",
"shorts": "Shorts",
"all": "Tất cả"
},
"search": {
"did_you_mean": "Ý của bạn là: {0}?",
@ -106,5 +139,13 @@
"videos": "YouTube: Video",
"music_videos": "YT Music: Video",
"music_albums": "YT Music: Album"
},
"info": {
"copied": "Đã sao chép!",
"cannot_copy": "Không thể sao chép!",
"page_not_found": "Không tìm thấy trang"
},
"subscriptions": {
"subscribed_channels_count": "Đã đăng ký cho: {0}"
}
}

View file

@ -44,12 +44,12 @@
"least_recent": "最早的",
"most_recent": "最新的",
"sort_by": "排序:",
"view_subscriptions": "查看订阅",
"view_subscriptions": "查看订阅列表",
"unsubscribe": "取消订阅 - {count}",
"subscribe": "订 - {count}",
"subscribe": "订 - {count}",
"loading": "正在加载...",
"filter": "筛选",
"search": "搜索",
"search": "搜索 (Ctrl+K)",
"view_ssl_score": "查看 SSL 得分",
"minimize_recommendations": "最小化建议",
"show_recommendations": "显示推荐",
@ -99,7 +99,17 @@
"reply_count": "{count} 条回复",
"minimize_comments": "最小化评论",
"minimize_comments_default": "默认最小化评论",
"show_watch_on_youtube": "显示“在 YouTube 上观看”按钮"
"show_watch_on_youtube": "显示“在 YouTube 上观看”按钮",
"minimize_chapters_default": "默认最小化章节",
"no_valid_playlists": "此文件不包含有效的播放列表!",
"with_playlist": "分享播放列表",
"playlist_bookmarked": "已加入书签",
"bookmark_playlist": "书签",
"skip_automatically": "自动",
"min_segment_length": "最小分段长度(以秒为单位)",
"skip_segment": "跳过分段",
"skip_button_only": "显示跳过按钮",
"show_less": "显示更少"
},
"video": {
"sponsor_segments": "赞助商部分",
@ -109,7 +119,9 @@
"live": "{0} 直播",
"chapters": "章节",
"ratings_disabled": "已禁用评价",
"shorts": "短视频"
"shorts": "短视频",
"all": "全部",
"category": "类别"
},
"preferences": {
"ssl_score": "SSL 分数",
@ -130,8 +142,8 @@
"watch_on": "在 {0} 观看"
},
"titles": {
"feed": "RSS 订阅源",
"subscriptions": "订阅",
"feed": "订阅流",
"subscriptions": "订阅列表",
"history": "历史",
"preferences": "设置",
"register": "注册",
@ -142,7 +154,8 @@
"instance": "实例",
"player": "播放器",
"livestreams": "直播",
"channels": "频道"
"channels": "频道",
"bookmarks": "书签"
},
"login": {
"password": "密码",
@ -169,6 +182,8 @@
"preferences_note": "注意:首选项保存在浏览器的本地存储中。删除浏览器数据会重置它们。",
"page_not_found": "未找到页面",
"copied": "已复制!",
"cannot_copy": "无法复制!"
"cannot_copy": "无法复制!",
"local_storage": "此操作需要本地存储是否启用了Cookie",
"register_no_email_note": "不建议使用电子邮件作为用户名。仍要继续吗?"
}
}

View file

@ -21,6 +21,7 @@ import {
faBook,
faServer,
faDonate,
faBookmark,
} from "@fortawesome/free-solid-svg-icons";
import { faGithub, faBitcoin, faYoutube } from "@fortawesome/free-brands-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
@ -48,6 +49,7 @@ library.add(
faBook,
faServer,
faDonate,
faBookmark,
);
import router from "@/router/router.js";
@ -120,8 +122,12 @@ const mixin = {
purifyHTML(original) {
return DOMPurify.sanitize(original);
},
setPreference(key, value) {
if (localStorage) localStorage.setItem(key, value);
setPreference(key, value, disableAlert = false) {
try {
localStorage.setItem(key, value);
} catch {
if (!disableAlert) alert(this.$t("info.local_storage"));
}
},
getPreferenceBoolean(key, defaultVal) {
var value;
@ -155,7 +161,17 @@ const mixin = {
(value = new URLSearchParams(window.location.search).get(key)) !== null ||
(this.testLocalStorage && (value = localStorage.getItem(key)) !== null)
) {
return Number(value);
const num = Number(value);
return isNaN(num) ? defaultVal : num;
} else return defaultVal;
},
getPreferenceJSON(key, defaultVal) {
var value;
if (
(value = new URLSearchParams(window.location.search).get(key)) !== null ||
(this.testLocalStorage && (value = localStorage.getItem(key)) !== null)
) {
return JSON.parse(value);
} else return defaultVal;
},
apiUrl() {
@ -192,19 +208,26 @@ const mixin = {
});
},
async updateWatched(videos) {
if (window.db) {
if (window.db && this.getPreferenceBoolean("watchHistory", false)) {
var tx = window.db.transaction("watch_history", "readonly");
var store = tx.objectStore("watch_history");
videos.map(async video => {
var request = store.get(video.url.substr(-11));
request.onsuccess = function (event) {
video.watched = Boolean(event.target.result);
if (event.target.result) {
video.watched = true;
video.currentTime = event.target.result.currentTime;
}
};
});
}
},
getLocalSubscriptions() {
return JSON.parse(localStorage.getItem("localSubscriptions"));
try {
return JSON.parse(localStorage.getItem("localSubscriptions"));
} catch {
return [];
}
},
isSubscribedLocally(channelId) {
const localSubscriptions = this.getLocalSubscriptions();
@ -214,19 +237,25 @@ const mixin = {
handleLocalSubscriptions(channelId) {
var localSubscriptions = this.getLocalSubscriptions() ?? [];
if (localSubscriptions.includes(channelId))
localSubscriptions.splice(localSubscriptions.indexOf(channelId));
localSubscriptions.splice(localSubscriptions.indexOf(channelId), 1);
else localSubscriptions.push(channelId);
// Sort for better cache hits
localSubscriptions.sort();
localStorage.setItem("localSubscriptions", JSON.stringify(localSubscriptions));
try {
localStorage.setItem("localSubscriptions", JSON.stringify(localSubscriptions));
return true;
} catch {
alert(this.$t("info.local_storage"));
}
return false;
},
getUnauthenticatedChannels() {
const localSubscriptions = this.getLocalSubscriptions() ?? [];
return localSubscriptions.join(",");
},
/* generate a temporary file and ask the user to download it */
download(text, filename, type) {
var file = new Blob([text], { type: type });
download(text, filename, mimeType) {
var file = new Blob([text], { type: mimeType });
const elem = document.createElement("a");
@ -235,6 +264,15 @@ const mixin = {
elem.click();
elem.remove();
},
rewriteDescription(text) {
return this.urlify(text)
.replaceAll(/(?:http(?:s)?:\/\/)?(?:www\.)?youtube\.com(\/[/a-zA-Z0-9_?=&-]*)/gm, "$1")
.replaceAll(
/(?:http(?:s)?:\/\/)?(?:www\.)?youtu\.be\/(?:watch\?v=)?([/a-zA-Z0-9_?=&-]*)/gm,
"/watch?v=$1",
)
.replaceAll("\n", "<br>");
},
},
computed: {
authenticated(_this) {

View file

@ -27,7 +27,7 @@ const routes = [
component: () => import("../components/PlaylistPage.vue"),
},
{
path: "/:path(v|w|embed|shorts|watch)/:v?",
path: "/:path(v|w|embed|live|shorts|watch)/:v?",
name: "WatchVideo",
component: () => import("../components/WatchVideo.vue"),
},
@ -41,6 +41,11 @@ const routes = [
name: "Channel",
component: () => import("../components/ChannelPage.vue"),
},
{
path: "/@:channelId",
name: "Channel handle",
component: () => import("../components/ChannelPage.vue"),
},
{
path: "/login",
name: "Login",

View file

@ -169,6 +169,7 @@
{ "code": "SR", "name": "Suriname" },
{ "code": "SY", "name": "Syrien" },
{ "code": "TJ", "name": "Tadschikistan" },
{ "code": "TW", "name": "Taiwan" },
{ "code": "TZ", "name": "Tansania" },
{ "code": "TH", "name": "Thailand" },
{ "code": "TG", "name": "Togo" },

View file

@ -176,6 +176,7 @@
{ "code": "TH", "name": "Ταϊλάνδη" },
{ "code": "TZ", "name": "Τανζανία" },
{ "code": "TJ", "name": "Τατζικιστάν" },
{ "code": "TW", "name": "Ταϊβάν" },
{ "code": "JM", "name": "Τζαμάικα" },
{ "code": "DJ", "name": "Τζιμπουτί" },
{ "code": "TO", "name": "Τόγκα" },

View file

@ -169,6 +169,7 @@
{ "code": "SE", "name": "Sweden" },
{ "code": "CH", "name": "Switzerland" },
{ "code": "SY", "name": "Syrian Arab Republic" },
{ "code": "TW", "name": "Taiwan" },
{ "code": "TJ", "name": "Tajikistan" },
{ "code": "TZ", "name": "Tanzania, United Republic of" },
{ "code": "TH", "name": "Thailand" },

View file

@ -172,6 +172,7 @@
{ "code": "CH", "name": "Suiza" },
{ "code": "SR", "name": "Surinam" },
{ "code": "TH", "name": "Tailandia" },
{ "code": "TW", "name": "Taiwán" },
{ "code": "TZ", "name": "Tanzania" },
{ "code": "TJ", "name": "Tayikistán" },
{ "code": "TL", "name": "Timor Oriental" },

View file

@ -174,6 +174,7 @@
{ "code": "SY", "name": "Syrie" },
{ "code": "TJ", "name": "Tadjikistan" },
{ "code": "TZ", "name": "Tanzanie" },
{ "code": "TW", "name": "Taïwan" },
{ "code": "TD", "name": "Tchad" },
{ "code": "CZ", "name": "Tchéquie" },
{ "code": "TH", "name": "Thaïlande" },

View file

@ -172,6 +172,7 @@
{ "code": "CH", "name": "Svizzera" },
{ "code": "SZ", "name": "eSwatini" },
{ "code": "TJ", "name": "Tagikistan" },
{ "code": "TW", "name": "Taiwan" },
{ "code": "TZ", "name": "Tanzania" },
{ "code": "TH", "name": "Thailandia" },
{ "code": "TL", "name": "Timor Est" },

View file

@ -170,6 +170,7 @@
{ "code": "TJ", "name": "Tadžikija" },
{ "code": "TZ", "name": "Tanzanija" },
{ "code": "TH", "name": "Tailandas" },
{ "code": "TW", "name": "Taivanas" },
{ "code": "TL", "name": "Rytų Timoras" },
{ "code": "TG", "name": "Togas" },
{ "code": "TO", "name": "Tonga" },

View file

@ -167,6 +167,7 @@
{ "code": "SE", "name": "Szwecja" },
{ "code": "TJ", "name": "Tadżykistan" },
{ "code": "TH", "name": "Tajlandia" },
{ "code": "TW", "name": "Tajwan" },
{ "code": "TZ", "name": "Tanzania" },
{ "code": "TL", "name": "Timor Wschodni" },
{ "code": "TG", "name": "Togo" },

View file

@ -173,6 +173,7 @@
{ "code": "CH", "name": "Švajčiarsko" },
{ "code": "SE", "name": "Švédsko" },
{ "code": "TJ", "name": "Tadžikistan" },
{ "code": "TW", "name": "Taiwan" },
{ "code": "IT", "name": "Taliansko" },
{ "code": "TZ", "name": "Tanzánia" },
{ "code": "TH", "name": "Thajsko" },

View file

@ -171,6 +171,7 @@
{ "code": "SY", "name": "Сиријска Арапска Република" },
{ "code": "TJ", "name": "Таџикистан" },
{ "code": "TZ", "name": "Танзанија" },
{ "code": "TW", "name": "Тајван" },
{ "code": "TH", "name": "Тајланд" },
{ "code": "TL", "name": "Тимор-Лесте" },
{ "code": "TG", "name": "Того" },

View file

@ -180,6 +180,7 @@
{ "code": "TR", "name": "土耳其" },
{ "code": "TM", "name": "土庫曼" },
{ "code": "TV", "name": "吐瓦魯" },
{ "code": "TW", "name": "台灣" },
{ "code": "UG", "name": "烏干達" },
{ "code": "UA", "name": "烏克蘭" },
{ "code": "AE", "name": "阿聯" },

View file

@ -4,187 +4,204 @@ import { Buffer } from "buffer";
window.Buffer = Buffer;
import { json2xml } from "xml-js";
const DashUtils = {
generate_dash_file_from_formats(VideoFormats, VideoLength) {
const generatedJSON = this.generate_xmljs_json_from_data(VideoFormats, VideoLength);
return json2xml(generatedJSON);
},
generate_xmljs_json_from_data(VideoFormatArray, VideoLength) {
const convertJSON = {
declaration: {
attributes: {
version: "1.0",
encoding: "utf-8",
},
export function generate_dash_file_from_formats(VideoFormats, VideoLength) {
const generatedJSON = generate_xmljs_json_from_data(VideoFormats, VideoLength);
return json2xml(generatedJSON);
}
function generate_xmljs_json_from_data(VideoFormatArray, VideoLength) {
const convertJSON = {
declaration: {
attributes: {
version: "1.0",
encoding: "utf-8",
},
elements: [
{
type: "element",
name: "MPD",
attributes: {
xmlns: "urn:mpeg:dash:schema:mpd:2011",
profiles: "urn:mpeg:dash:profile:full:2011",
minBufferTime: "PT1.5S",
type: "static",
mediaPresentationDuration: `PT${VideoLength}S`,
},
elements: [
{
type: "element",
name: "Period",
elements: this.generate_adaptation_set(VideoFormatArray),
},
],
},
elements: [
{
type: "element",
name: "MPD",
attributes: {
xmlns: "urn:mpeg:dash:schema:mpd:2011",
profiles: "urn:mpeg:dash:profile:full:2011",
minBufferTime: "PT1.5S",
type: "static",
mediaPresentationDuration: `PT${VideoLength}S`,
},
],
};
return convertJSON;
},
generate_adaptation_set(VideoFormatArray) {
const adaptationSets = [];
const mimeTypes = [];
const mimeObjects = [[]];
// sort the formats by mime types
VideoFormatArray.forEach(videoFormat => {
// the dual formats should not be used
if (videoFormat.mimeType.indexOf("video") != -1 && !videoFormat.videoOnly) {
elements: [
{
type: "element",
name: "Period",
elements: generate_adaptation_set(VideoFormatArray),
},
],
},
],
};
return convertJSON;
}
function generate_adaptation_set(VideoFormatArray) {
const adaptationSets = [];
let mimeAudioObjs = [];
VideoFormatArray.forEach(videoFormat => {
// the dual formats should not be used
if (
(videoFormat.mimeType.includes("video") && !videoFormat.videoOnly) ||
videoFormat.mimeType.includes("application")
) {
return;
}
const audioTrackId = videoFormat.audioTrackId;
const mimeType = videoFormat.mimeType;
for (let i = 0; i < mimeAudioObjs.length; i++) {
const mimeAudioObj = mimeAudioObjs[i];
if (mimeAudioObj.audioTrackId == audioTrackId && mimeAudioObj.mimeType == mimeType) {
mimeAudioObj.videoFormats.push(videoFormat);
return;
}
// if these properties are not available, then we skip it because we cannot set these properties
//if (!(videoFormat.hasOwnProperty('initRange') && videoFormat.hasOwnProperty('indexRange'))) {
// return
//}
const mimeType = videoFormat.mimeType;
const mimeTypeIndex = mimeTypes.indexOf(mimeType);
if (mimeTypeIndex > -1) {
mimeObjects[mimeTypeIndex].push(videoFormat);
} else {
mimeTypes.push(mimeType);
mimeObjects.push([]);
mimeObjects[mimeTypes.length - 1].push(videoFormat);
}
});
// for each MimeType generate a new Adaptation set with Representations as sub elements
for (let i = 0; i < mimeTypes.length; i++) {
let isVideoFormat = false;
const adapSet = {
type: "element",
name: "AdaptationSet",
attributes: {
id: i,
mimeType: mimeTypes[i],
startWithSAP: "1",
subsegmentAlignment: "true",
},
elements: [],
};
if (!mimeTypes[i].includes("audio")) {
adapSet.attributes.scanType = "progressive";
isVideoFormat = true;
}
mimeObjects[i].forEach(format => {
if (isVideoFormat) {
adapSet.elements.push(this.generate_representation_video(format));
} else {
adapSet.elements.push(this.generate_representation_audio(format));
}
});
adaptationSets.push(adapSet);
}
return adaptationSets;
},
generate_representation_audio(Format) {
const representation = {
type: "element",
name: "Representation",
attributes: {
id: Format.itag,
codecs: Format.codec,
bandwidth: Format.bitrate,
},
elements: [
{
type: "element",
name: "AudioChannelConfiguration",
attributes: {
schemeIdUri: "urn:mpeg:dash:23003:3:audio_channel_configuration:2011",
value: "2",
},
},
{
type: "element",
name: "BaseURL",
elements: [
{
type: "text",
text: Format.url,
},
],
},
{
type: "element",
name: "SegmentBase",
attributes: {
indexRange: `${Format.indexStart}-${Format.indexEnd}`,
},
elements: [
{
type: "element",
name: "Initialization",
attributes: {
range: `${Format.initStart}-${Format.initEnd}`,
},
},
],
},
],
};
return representation;
},
generate_representation_video(Format) {
const representation = {
type: "element",
name: "Representation",
attributes: {
id: Format.itag,
codecs: Format.codec,
bandwidth: Format.bitrate,
width: Format.width,
height: Format.height,
maxPlayoutRate: "1",
frameRate: Format.fps,
},
elements: [
{
type: "element",
name: "BaseURL",
elements: [
{
type: "text",
text: Format.url,
},
],
},
{
type: "element",
name: "SegmentBase",
attributes: {
indexRange: `${Format.indexStart}-${Format.indexEnd}`,
},
elements: [
{
type: "element",
name: "Initialization",
attributes: {
range: `${Format.initStart}-${Format.initEnd}`,
},
},
],
},
],
};
return representation;
},
};
export default DashUtils;
mimeAudioObjs.push({
audioTrackId,
mimeType,
videoFormats: [videoFormat],
});
});
mimeAudioObjs.forEach(mimeAudioObj => {
const adapSet = {
type: "element",
name: "AdaptationSet",
attributes: {
id: mimeAudioObj.audioTrackId,
lang: mimeAudioObj.audioTrackId?.substr(0, 2),
mimeType: mimeAudioObj.mimeType,
startWithSAP: "1",
subsegmentAlignment: "true",
},
elements: [],
};
let isVideoFormat = false;
if (mimeAudioObj.mimeType.includes("video")) {
isVideoFormat = true;
}
if (isVideoFormat) {
adapSet.attributes.scanType = "progressive";
}
for (var i = 0; i < mimeAudioObj.videoFormats.length; i++) {
const videoFormat = mimeAudioObj.videoFormats[i];
if (isVideoFormat) {
adapSet.elements.push(generate_representation_video(videoFormat));
} else {
adapSet.elements.push(generate_representation_audio(videoFormat));
}
}
adaptationSets.push(adapSet);
});
return adaptationSets;
}
function generate_representation_audio(Format) {
const representation = {
type: "element",
name: "Representation",
attributes: {
id: Format.itag,
codecs: Format.codec,
bandwidth: Format.bitrate,
},
elements: [
{
type: "element",
name: "AudioChannelConfiguration",
attributes: {
schemeIdUri: "urn:mpeg:dash:23003:3:audio_channel_configuration:2011",
value: "2",
},
},
{
type: "element",
name: "BaseURL",
elements: [
{
type: "text",
text: Format.url,
},
],
},
{
type: "element",
name: "SegmentBase",
attributes: {
indexRange: `${Format.indexStart}-${Format.indexEnd}`,
},
elements: [
{
type: "element",
name: "Initialization",
attributes: {
range: `${Format.initStart}-${Format.initEnd}`,
},
},
],
},
],
};
return representation;
}
function generate_representation_video(Format) {
const representation = {
type: "element",
name: "Representation",
attributes: {
id: Format.itag,
codecs: Format.codec,
bandwidth: Format.bitrate,
width: Format.width,
height: Format.height,
maxPlayoutRate: "1",
frameRate: Format.fps,
},
elements: [
{
type: "element",
name: "BaseURL",
elements: [
{
type: "text",
text: Format.url,
},
],
},
{
type: "element",
name: "SegmentBase",
attributes: {
indexRange: `${Format.indexStart}-${Format.indexEnd}`,
},
elements: [
{
type: "element",
name: "Initialization",
attributes: {
range: `${Format.initStart}-${Format.initEnd}`,
},
},
],
},
],
};
return representation;
}

8
src/utils/Misc.js Normal file
View file

@ -0,0 +1,8 @@
export const isEmail = input => {
// Taken from https://emailregex.com
const result = input.match(
//eslint-disable-next-line
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
);
return result;
};

View file

@ -1,5 +1,12 @@
{
"github": {
"silent": true
}
},
"routes": [
{
"src": "/[^.]+",
"dest": "/",
"status": 200
}
]
}

View file

@ -22,7 +22,7 @@ export default defineConfig({
registerType: "autoUpdate",
workbox: {
globPatterns: ["**/*.{js,css,html,ico,svg,png}", "manifest.webmanifest"],
globIgnores: ["**/*legacy.*.js"],
globIgnores: ["**/*-legacy-*.js"],
runtimeCaching: [
{
urlPattern: /https:\/\/[a-zA-Z./0-9_]*\.(?:otf|ttf)/i,

3407
yarn.lock

File diff suppressed because it is too large Load diff