Merge pull request #2934 from dragos-efy/efy

Big Piped + EFY Update
This commit is contained in:
Kavin 2023-09-13 13:43:14 +01:00 committed by GitHub
commit a911ffc746
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
115 changed files with 12065 additions and 6616 deletions

7
.eslintrc.cjs Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: ["plugin:vue/vue3-recommended", "eslint:recommended", "@unocss", "plugin:prettier/recommended"],
};

View File

@ -22,7 +22,7 @@ body:
label: Official Instance label: Official Instance
description: Can the bug be reproduced on the official instance? description: Can the bug be reproduced on the official instance?
options: options:
- label: The bug is reproducable on the [official hosted instance](http://piped.video/) or is API related. - label: The bug is reproducible on the [official hosted instance](http://piped.video/), or is API-related.
- type: textarea - type: textarea
attributes: attributes:
@ -58,7 +58,7 @@ body:
description: | description: |
If applicable, add logs from the JavaScript console and/or backend server. If applicable, add logs from the JavaScript console and/or backend server.
validations: validations:
required: true required: false
- type: textarea - type: textarea
attributes: attributes:
@ -67,6 +67,8 @@ body:
If applicable, add browser, and OS with version. If applicable, add browser, and OS with version.
placeholder: >- placeholder: >-
Brave 1.x.x on Arch Linux. Brave 1.x.x on Arch Linux.
validations:
required: true
- type: textarea - type: textarea
attributes: attributes:

View File

@ -8,13 +8,21 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: 'true'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: latest
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
cache: "yarn" cache: "pnpm"
- run: yarn install --prefer-offline - run: pnpm install
- run: yarn build - run: pnpm build
- run: yarn lint --no-fix - uses: actions/upload-artifact@v3
with:
name: build
path: dist
- run: pnpm lint --no-fix

65
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: "CodeQL"
on:
push:
branches: [ 'master' ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ 'master' ]
schedule:
- cron: '42 11 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Build and Deploy Job name: Build and Deploy Job
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Build And Deploy - name: Build And Deploy

View File

@ -11,13 +11,17 @@ jobs:
build-docker-image: build-docker-image:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: latest
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
cache: "yarn" cache: "pnpm"
- run: yarn install --prefer-offline - run: pnpm install
- run: yarn build && ./localizefonts.sh && mv dist/ dist-ci/ - run: pnpm build && ./localizefonts.sh && mv dist/ dist-ci/
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v2
with: with:
@ -33,7 +37,7 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }} username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push - name: Build and push
uses: docker/build-push-action@v3 uses: docker/build-push-action@v4
with: with:
context: . context: .
file: ./Dockerfile.ci file: ./Dockerfile.ci

View File

@ -1,31 +1,35 @@
name: Build and Deploy name: Build and Deploy
on: on:
push: push:
paths-ignore: paths-ignore:
- "**.md" - "**.md"
branches: branches:
- master - master
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup Node.js - name: Setup pnpm
uses: actions/setup-node@v3 uses: pnpm/action-setup@v2
with: with:
cache: "yarn" version: latest
- run: yarn install --prefer-offline - name: Setup Node.js
- run: yarn build && ./localizefonts.sh && cp dist/index.html dist/ipfs-404.html uses: actions/setup-node@v3
- uses: aquiladev/ipfs-action@v0.3.1-alpha.2 with:
id: ipfs-add cache: "pnpm"
with: - run: pnpm install
path: ./dist - run: pnpm build && ./localizefonts.sh && cp dist/index.html dist/ipfs-404.html
service: infura - uses: aquiladev/ipfs-action@v0.3.1-alpha.2
infuraProjectId: ${{ secrets.INFURA_PROJECT_ID }} id: ipfs-add
infuraProjectSecret: ${{ secrets.INFURA_PROJECT_SECRET }} with:
- name: Update DNSLink path: ./dist
run: npx dnslink-cloudflare -d kavin.rocks -l /ipfs/${{ steps.ipfs-add.outputs.hash }} -r _dnslink.piped-ipfs service: infura
env: infuraProjectId: ${{ secrets.INFURA_PROJECT_ID }}
CF_API_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} infuraProjectSecret: ${{ secrets.INFURA_PROJECT_SECRET }}
- name: Update DNSLink
run: npx dnslink-cloudflare -d kavin.rocks -l /ipfs/${{ steps.ipfs-add.outputs.hash }} -r _dnslink.piped-ipfs
env:
CF_API_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }}

25
.github/workflows/reviewdog.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: reviewdog
on: [pull_request]
jobs:
eslint:
name: runner / eslint
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: latest
- name: Setup Node.js
uses: actions/setup-node@v3
with:
cache: "pnpm"
- run: pnpm install
- uses: reviewdog/action-eslint@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-review
eslint_flags: "--ignore-path .gitignore --ext .js,.vue ."

View File

@ -8,6 +8,12 @@ jobs:
merge: merge:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4
- 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 - name: AutoMerge Weblate translations
if: github.event.pull_request.user.login == 'weblate' if: github.event.pull_request.user.login == 'weblate'
run: gh pr merge --auto --delete-branch --merge "$PR_URL" run: gh pr merge --auto --delete-branch --merge "$PR_URL"

106
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,106 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior include but are not limited to:
- The usage of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, threats, and personal or political attacks
- Harassment of any form
- Publishing others' private information, such as a physical or electronic address, without explicit permission from the individual
- Derailling conversations unnecessarily in a way that is not constructive, such as repeatedly posting off-topic comments whilest not in an off-topic channel
- Other conduct which could reasonably be considered inappropriate in a professional setting
- Tagging maintainers or project members without being one yourself
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at the contact section of the project README, or alternatively any admin of an official medium community. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private or public, written warning from community leaders, providing
clarity when necessary around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A written warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Kick / Temporary Ban
**Community Impact**: A serious or repeated violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A kick or temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View File

@ -8,10 +8,12 @@ RUN --mount=type=cache,target=/var/cache/apk \
COPY . . COPY . .
RUN --mount=type=cache,target=/root/.cache/yarn \ RUN corepack enable && corepack prepare pnpm@latest --activate
RUN --mount=type=cache,target=/root/.local/share/pnpm \
--mount=type=cache,target=/app/node_modules \ --mount=type=cache,target=/app/node_modules \
yarn install --prefer-offline && \ pnpm install --prefer-offline && \
yarn build && ./localizefonts.sh pnpm build && ./localizefonts.sh
FROM nginx:alpine FROM nginx:alpine

View File

@ -2,6 +2,7 @@
[![AGPL v3](https://shields.io/badge/License-AGPL%20v3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.en.html) [![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) [![Matrix](https://img.shields.io/matrix/piped:matrix.org)](https://matrix.to/#/#piped:matrix.org)
[![Lemmy](https://img.shields.io/lemmy/piped%40feddit.rocks)](https://feddit.rocks/c/piped)
[![Registered Users](https://pipedapi.kavin.rocks/registered/badge)](https://piped.video/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/) [![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 Repo stars](https://img.shields.io/github/stars/TeamPiped/Piped-Frontend?style=social)](https://github.com/TeamPiped/Piped/stargazers)
@ -61,6 +62,10 @@ By using Piped, you can freely watch and listen to content without the fear of p
- You can join us via Matrix at [#piped](https://matrix.to/#/#piped:matrix.org). - You can join us via Matrix at [#piped](https://matrix.to/#/#piped:matrix.org).
- You can also join us at the libera.chat IRC network which is bridged to the Matrix room at [#piped](https://web.libera.chat/#piped). - You can also join us at the libera.chat IRC network which is bridged to the Matrix room at [#piped](https://web.libera.chat/#piped).
## Public Communities
- You can join us on Lemmy on the [!piped@feddit.rocks](https://feddit.rocks/c/piped) community.
## Self-Hosting ## Self-Hosting
See https://docs.piped.video/docs/self-hosting/ for more details. See https://docs.piped.video/docs/self-hosting/ for more details.
@ -103,13 +108,13 @@ You can help by translating the project to a language you speak at https://hoste
### Development Setup ### Development Setup
``` ```
yarn install pnpm install
``` ```
### Compiles and hot-reloads for development ### Compiles and hot-reloads for development
``` ```
yarn serve pnpm serve
``` ```
You can now make changes and view then in realtime! You can now make changes and view then in realtime!
@ -140,7 +145,15 @@ Contributions in any other form are also welcomed.
- [Yattee](https://github.com/yattee/yattee) - an alternative frontend for YouTube, for IOS. - [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. - [LibreTube](https://github.com/Libre-tube/LibreTube) - an alternative frontend for YouTube, for Android.
- [Racoon](https://github.com/shailendramaurya/racoon) - A web based minimal YouTube downloader.
- [Hyperpipe](https://codeberg.org/Hyperpipe/Hyperpipe) - an alternative privacy respecting frontend for YouTube Music. - [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.
- [YTDLnis](https://github.com/deniscerri/ytdlnis) - Video and audio downloader for Android that uses Piped to update formats.
- [DeskVideo](https://github.com/malisipi/DeskVideo) - A desktop styled, customizable alternative front-end for YouTube.
## YourKit ## YourKit

View File

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

View File

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

View File

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

View File

@ -6,56 +6,54 @@
"serve": "vite", "serve": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"format": "prettier -w --ignore-path .gitignore **/**.{js,vue,json}",
"lint": "eslint --fix --color --ignore-path .gitignore --ext .js,.vue ." "lint": "eslint --fix --color --ignore-path .gitignore --ext .js,.vue ."
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "6.2.1", "@fortawesome/fontawesome-svg-core": "6.4.2",
"@fortawesome/free-brands-svg-icons": "6.2.1", "@fortawesome/free-brands-svg-icons": "6.4.2",
"@fortawesome/free-solid-svg-icons": "6.2.1", "@fortawesome/free-solid-svg-icons": "6.4.2",
"@fortawesome/vue-fontawesome": "3.0.2", "@fortawesome/vue-fontawesome": "3.0.3",
"buffer": "6.0.3", "buffer": "6.0.3",
"dompurify": "2.4.2", "dompurify": "3.0.5",
"hotkeys-js": "3.10.1", "hotkeys-js": "3.12.0",
"javascript-time-ago": "2.5.9", "javascript-time-ago": "2.5.9",
"mux.js": "6.2.0", "linkify-html": "4.1.1",
"shaka-player": "4.3.2", "linkifyjs": "4.1.1",
"mux.js": "6.3.0",
"qrcode": "^1.5.3",
"shaka-player": "4.4.0",
"stream-browserify": "3.0.0", "stream-browserify": "3.0.0",
"vue": "3.2.45", "vue": "3.3.4",
"vue-i18n": "9.2.2", "vue-i18n": "9.2.2",
"vue-router": "4.1.6", "vue-router": "4.2.4",
"xml-js": "1.6.11" "xml-js": "1.6.11"
}, },
"devDependencies": { "devDependencies": {
"@iconify/json": "2.2.3", "@iconify-json/fa6-brands": "1.1.13",
"@intlify/vite-plugin-vue-i18n": "6.0.3", "@iconify-json/fa6-solid": "1.1.15",
"@unocss/preset-icons": "0.48.2", "@intlify/unplugin-vue-i18n": "0.13.0",
"@unocss/preset-web-fonts": "0.48.2", "@unocss/eslint-config": "0.55.6",
"@unocss/transformer-directives": "0.48.2", "@unocss/preset-icons": "0.55.6",
"@unocss/transformer-variant-group": "0.48.2", "@unocss/preset-uno": "0.55.6",
"@vitejs/plugin-legacy": "3.0.1", "@unocss/preset-web-fonts": "0.55.6",
"@vitejs/plugin-vue": "4.0.0", "@unocss/reset": "0.55.6",
"@vue/compiler-sfc": "3.2.45", "@unocss/transformer-directives": "0.55.6",
"eslint": "8.31.0", "@unocss/transformer-variant-group": "0.55.6",
"eslint-config-prettier": "8.6.0", "@vitejs/plugin-legacy": "4.1.1",
"eslint-plugin-prettier": "4.2.1", "@vitejs/plugin-vue": "4.3.4",
"eslint-plugin-vue": "9.8.0", "@vue/compiler-sfc": "3.3.4",
"prettier": "2.8.1", "eslint": "8.48.0",
"unocss": "0.48.2", "eslint-config-prettier": "9.0.0",
"vite": "3.2.5", "eslint-plugin-prettier": "5.0.0",
"eslint-plugin-vue": "9.17.0",
"lightningcss": "1.21.7",
"prettier": "3.0.3",
"unocss": "0.55.6",
"vite": "4.4.9",
"vite-plugin-eslint": "1.8.1", "vite-plugin-eslint": "1.8.1",
"vite-plugin-pwa": "0.14.1" "vite-plugin-pwa": "0.16.4",
}, "workbox-window": "7.0.0"
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"plugin:prettier/recommended",
"eslint:recommended"
],
"rules": {}
}, },
"browserslist": [ "browserslist": [
"last 1 chrome version", "last 1 chrome version",

5397
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

@ -1 +1 @@
Subproject commit 79329007824820dedf4234200f105b9d4caac577 Subproject commit 1e7e82b64b9f8354641f0665f61c7983c9dfed27

View File

@ -1,21 +1,17 @@
{ {
"$schema": "https://docs.renovatebot.com/renovate-schema.json", "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [ "extends": ["config:base", "group:recommended"],
"config:base", "ignorePresets": [":prHourlyLimit2"],
"group:recommended" "packageRules": [
], {
"ignorePresets": [ "matchPackagePrefixes": ["@unocss/"],
":prHourlyLimit2" "matchPackageNames": ["unocss"],
], "groupName": "unocss"
"packageRules": [ }
{ ],
"matchPackagePrefixes": [ "lockFileMaintenance": {
"@unocss/" "enabled": true,
], "automerge": true
"matchPackageNames": [ },
"unocss" "platformAutomerge": true
],
"groupName": "unocss"
}
]
} }

View File

@ -1,18 +1,22 @@
<template> <template>
<div> <NavBar />
<NavBar /> <div class="flex-1">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<keep-alive :max="5"> <keep-alive :max="5">
<component :is="Component" :key="$route.fullPath" /> <component :is="Component" :key="$route.fullPath" />
</keep-alive> </keep-alive>
</router-view> </router-view>
<FooterComponent />
</div> </div>
<FooterComponent />
</template> </template>
<style> <style>
#app {
min-height: calc(var(--efy_100vh) - var(--efy_gap2));
display: flex;
flex-direction: column;
}
/*Radius*/ /*Radius*/
input, input,
.btn, .btn,
@ -32,9 +36,8 @@ video {
border-radius: var(--efy_radius) !important; border-radius: var(--efy_radius) !important;
} }
/*Radius 0*/ .video-card .thumbnail {
.video-grid img { border-radius: var(--efy_radius) var(--efy_radius) 0 0;
border-radius: var(--efy_radius0);
} }
/*Radius 2*/ /*Radius 2*/
@ -61,14 +64,17 @@ video {
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
margin: 0 0 5rem 0; padding: 0 10rem 5rem 10rem;
line-height: 22rem; line-height: 22rem;
max-height: 43rem;
overflow: hidden;
word-break: break-word;
} }
.pp-video-card-buttons { .pp-video-card-buttons {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: var(--efy_gap0); gap: var(--efy_gap0);
margin: 5rem 0 0; margin: 5rem 10rem 10rem 10rem;
} }
.pp-video-card-buttons :is(a, button) { .pp-video-card-buttons :is(a, button) {
padding: 4rem 8rem; padding: 4rem 8rem;
@ -85,6 +91,8 @@ video {
-webkit-text-fill-color: var(--efy_text2); -webkit-text-fill-color: var(--efy_text2);
border: 0; border: 0;
padding: 6rem 10rem; padding: 6rem 10rem;
}
.pp-video-card-buttons :is(.pp-color, .btn, button) {
height: 35rem; height: 35rem;
} }
.pp-video-card-channel > .pp-text { .pp-video-card-channel > .pp-text {
@ -113,8 +121,9 @@ video {
gap: var(--efy_gap0); gap: var(--efy_gap0);
place-items: center; place-items: center;
background: transparent; background: transparent;
margin: var(--efy_gap0) 0 0; margin: var(--efy_gap0) 0 var(--efy_gap0) var(--efy_gap0);
width: fit-content; width: fit-content;
border-radius: var(--efy_radius);
} }
.pp-video-card-channel > a { .pp-video-card-channel > a {
height: 36rem; height: 36rem;
@ -141,38 +150,46 @@ export default {
theme: "dark", theme: "dark",
}; };
}, },
methods: {
setTheme() {
let themePref = this.getPreferenceString("theme", "dark");
if (themePref == "auto") this.theme = darkModePreference.matches ? "dark" : "light";
else this.theme = themePref;
},
},
mounted() { mounted() {
this.setTheme(); this.setTheme();
darkModePreference.addEventListener("change", () => { darkModePreference.addEventListener("change", () => {
this.setTheme(); this.setTheme();
}); });
if (this.getPreferenceBoolean("watchHistory", false))
if ("indexedDB" in window) { if ("indexedDB" in window) {
const request = indexedDB.open("piped-db", 2); const request = indexedDB.open("piped-db", 5);
request.onupgradeneeded = ev => { request.onupgradeneeded = ev => {
const db = request.result; const db = request.result;
console.log("Upgrading object store."); console.log("Upgrading object store.");
if (!db.objectStoreNames.contains("watch_history")) { if (!db.objectStoreNames.contains("watch_history")) {
const store = db.createObjectStore("watch_history", { keyPath: "videoId" }); const store = db.createObjectStore("watch_history", { keyPath: "videoId" });
store.createIndex("video_id_idx", "videoId", { unique: true }); store.createIndex("video_id_idx", "videoId", { unique: true });
store.createIndex("id_idx", "id", { unique: true, autoIncrement: true }); store.createIndex("id_idx", "id", { unique: true, autoIncrement: true });
} }
if (ev.oldVersion < 2) { if (ev.oldVersion < 2) {
const store = request.transaction.objectStore("watch_history"); const store = request.transaction.objectStore("watch_history");
store.createIndex("watchedAt", "watchedAt", { unique: false }); store.createIndex("watchedAt", "watchedAt", { unique: false });
} }
}; if (!db.objectStoreNames.contains("playlist_bookmarks")) {
request.onsuccess = e => { const store = db.createObjectStore("playlist_bookmarks", { keyPath: "playlistId" });
window.db = e.target.result; store.createIndex("playlist_id_idx", "playlistId", { unique: true });
}; store.createIndex("id_idx", "id", { unique: true, autoIncrement: true });
} else console.log("This browser doesn't support IndexedDB"); }
if (!db.objectStoreNames.contains("channel_groups")) {
const store = db.createObjectStore("channel_groups", { keyPath: "groupName" });
store.createIndex("groupName", "groupName", { unique: true });
}
if (!db.objectStoreNames.contains("playlists")) {
const playlistStore = db.createObjectStore("playlists", { keyPath: "playlistId" });
playlistStore.createIndex("playlistId", "playlistId", { unique: true });
const playlistVideosStore = db.createObjectStore("playlist_videos", { keyPath: "videoId" });
playlistVideosStore.createIndex("videoId", "videoId", { unique: true });
}
};
request.onsuccess = e => {
window.db = e.target.result;
};
} else console.log("This browser doesn't support IndexedDB");
const App = this; const App = this;
@ -197,5 +214,24 @@ export default {
} }
})(); })();
}, },
methods: {
setTheme() {
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");
},
},
}; };
</script> </script>

View File

@ -1,34 +1,35 @@
<template> <template>
<div> <div class="flex flex-col efy_trans_filter efy_shadow_trans">
<router-link :to="props.item.url"> <router-link
<div class="relative"> :to="props.item.url"
<img class="w-full" :src="props.item.thumbnail" loading="lazy" /> class="flex items-center p-[10rem] gap-[10rem]"
style="border-bottom: var(--efy_border)"
>
<img
class="efy_shadow_trans"
style="border-radius: var(--efy_radius); width: 40rem; aspect-ratio: 1"
:src="props.item.thumbnail"
loading="lazy"
width="40"
height="40"
/>
<div class="flex items-center overflow-hidden pp-text">
<p v-text="props.item.name" class="pp-video-card-title p-0!" />
<font-awesome-icon v-if="props.item.verified" class="ml-1.5" icon="check" />
</div> </div>
<p>
<span v-text="props.item.name" />
<font-awesome-icon class="ml-1.5" v-if="props.item.verified" icon="check" />
</p>
</router-link> </router-link>
<p v-if="props.item.description" v-text="props.item.description" /> <div style="padding: 10rem">
<router-link v-if="props.item.uploaderUrl" class="link" :to="props.item.uploaderUrl"> <p v-if="props.item.description" v-text="props.item.description" />
<p> <div v-if="props.item.videos >= 0" v-text="`${props.item.videos} ${$t('video.videos')}`" />
<span v-text="props.item.uploader" /> </div>
<font-awesome-icon class="ml-1.5" v-if="props.item.uploaderVerified" icon="check" />
</p>
</router-link>
<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')}`" />
</template>
<br />
</div> </div>
</template> </template>
<script setup> <script setup>
const props = defineProps({ const props = defineProps({
item: Object, item: {
type: Object,
required: true,
},
}); });
</script> </script>

View File

@ -1,88 +1,94 @@
<template> <template>
<ErrorHandler v-if="channel && channel.error" :message="channel.message" :error="channel.error" /> <ErrorHandler v-if="channel && channel.error" :message="channel.message" :error="channel.error" />
<div v-if="channel" v-show="!channel.error" class="mt-[15rem]">
<LoadingIndicatorPage :show-content="channel != null && !channel.error">
<img v-if="channel.bannerUrl" :src="channel.bannerUrl" class="w-full efy_shadow_trans" loading="lazy" />
<div class="pp-channel-page-author flex">
<img height="48" width="48" class="efy_shadow_trans" :src="channel.avatarUrl" />
<h5 v-text="channel.name" />
<font-awesome-icon v-if="channel.verified" class="ml-1.5" icon="check" />
</div>
<p v-text="channel.description" style="margin: 10rem 0 0 0" />
<div v-if="channel" v-show="!channel.error"> <div class="pp-channel-tabs">
<img v-if="channel.bannerUrl" :src="channel.bannerUrl" class="w-full pb-1.5" loading="lazy" /> <button
<div class="pp-channel-page-author flex place-items-center"> v-t="{
<img height="48" width="48" class="m-1" :src="channel.avatarUrl" /> path: `actions.${subscribed ? 'unsubscribe' : 'subscribe'}`,
<h5 v-text="channel.name" /> args: { count: numberFormat(channel.subscriberCount) },
<font-awesome-icon class="ml-1.5" v-if="channel.verified" icon="check" /> }"
</div> class="pp-subscribe"
<!-- eslint-disable-next-line vue/no-v-html --> @click="subscribeHandler"
<p class="whitespace-pre-wrap mt-2"> ></button>
<span v-html="purifyHTML(urlify(channel.description))" />
</p>
<div class="flex mt-4 mb-2 pp-channel-tabs"> <!-- RSS Feed button -->
<button <a
class="btn pp-subscribe" v-if="channel.id"
@click="subscribeHandler" aria-label="RSS feed"
v-t="{ title="RSS feed"
path: `actions.${subscribed ? 'unsubscribe' : 'subscribe'}`, role="button"
args: { count: numberFormat(channel.subscriberCount) }, :href="`${apiUrl()}/feed/unauthenticated/rss?channels=${channel.id}`"
}" target="_blank"
></button> class="pp-square"
style="display: inline; float: unset"
>
<font-awesome-icon icon="rss" />
</a>
<WatchOnButton :link="`https://youtube.com/channel/${channel.id}`" />
<p style="place-self: center">|</p>
<button
v-for="(tab, index) in tabs"
:key="tab.name"
:class="{ active: selectedTab == index }"
@click="loadTab(index)"
>
<span v-text="tab.translatedName"></span>
</button>
</div>
<!-- RSS Feed button --> <hr />
<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"
style="display: inline; float: unset; margin-left: var(--efy_gap0)"
>
<font-awesome-icon icon="rss" />
</a>
<WatchOnYouTubeButton :link="`https://youtube.com/channel/${this.channel.id}`" />
<p>|</p>
<button
v-for="(tab, index) in tabs"
:key="tab.name"
style="margin-right: var(--efy_gap0)"
@click="loadTab(index)"
:class="{ active: selectedTab == index }"
>
<span v-text="tab.translatedName"></span>
</button>
</div>
<hr /> <div class="video-grid">
<ContentItem
<div class="video-grid"> v-for="item in contentItems"
<ContentItem :key="item.url"
v-for="item in contentItems" :item="item"
:key="item.url" height="94"
:item="item" width="168"
height="94" hide-channel
width="168" class="efy_trans_filter"
hide-channel />
class="efy_trans_filter" </div>
/> </LoadingIndicatorPage>
</div>
</div> </div>
</template> </template>
<style> <style>
.pp-channel-tabs > p { .pp-channel-tabs {
place-self: center; display: flex;
padding: 0 10rem; flex-wrap: wrap;
-webkit-text-fill-color: var(--efy_text_trans2); margin: 15rem 0;
gap: var(--efy_gap0);
}
.pp-channel-tabs :is(button, [role="button"]) {
margin: 0;
border: 0;
} }
</style> </style>
<script> <script>
import ErrorHandler from "./ErrorHandler.vue"; import ErrorHandler from "./ErrorHandler.vue";
import ContentItem from "./ContentItem.vue"; import ContentItem from "./ContentItem.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; import WatchOnButton from "./WatchOnButton.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
// import CollapsableText from "./CollapsableText.vue";
export default { export default {
components: { components: {
ErrorHandler, ErrorHandler,
ContentItem, ContentItem,
WatchOnYouTubeButton, WatchOnButton,
LoadingIndicatorPage,
// CollapsableText,
}, },
data() { data() {
return { return {
@ -130,7 +136,9 @@ export default {
}); });
}, },
async fetchChannel() { 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); return await this.fetchJson(url);
}, },
async getChannelData() { async getChannelData() {
@ -142,13 +150,16 @@ export default {
this.contentItems = this.channel.relatedStreams; this.contentItems = this.channel.relatedStreams;
this.fetchSubscribedStatus(); this.fetchSubscribedStatus();
this.updateWatched(this.channel.relatedStreams); this.updateWatched(this.channel.relatedStreams);
this.fetchDeArrowContent(this.channel.relatedStreams);
this.tabs.push({ this.tabs.push({
translatedName: this.$t("video.videos"), translatedName: this.$t("video.videos"),
}); });
const tabQuery = this.$route.query.tab;
for (let i = 0; i < this.channel.tabs.length; i++) { for (let i = 0; i < this.channel.tabs.length; i++) {
let tab = this.channel.tabs[i]; let tab = this.channel.tabs[i];
tab.translatedName = this.getTranslatedTabName(tab.name); tab.translatedName = this.getTranslatedTabName(tab.name);
this.tabs.push(tab); this.tabs.push(tab);
if (tab.name === tabQuery) this.loadTab(i + 1);
} }
} }
}); });
@ -178,6 +189,7 @@ export default {
this.loading = false; this.loading = false;
this.updateWatched(json.relatedStreams); this.updateWatched(json.relatedStreams);
json.relatedStreams.map(stream => this.contentItems.push(stream)); json.relatedStreams.map(stream => this.contentItems.push(stream));
this.fetchDeArrowContent(this.contentItems);
}); });
}, },
fetchChannelTabNextPage() { fetchChannelTabNextPage() {
@ -188,6 +200,7 @@ export default {
this.tabs[this.selectedTab].tabNextPage = json.nextpage; this.tabs[this.selectedTab].tabNextPage = json.nextpage;
this.loading = false; this.loading = false;
json.content.map(item => this.contentItems.push(item)); json.content.map(item => this.contentItems.push(item));
this.fetchDeArrowContent(this.contentItems);
this.tabs[this.selectedTab].content = this.contentItems; this.tabs[this.selectedTab].content = this.contentItems;
}); });
}, },
@ -231,10 +244,17 @@ export default {
}, },
loadTab(index) { loadTab(index) {
this.selectedTab = 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) { if (index == 0) {
this.contentItems = this.channel.relatedStreams; this.contentItems = this.channel.relatedStreams;
return; return;
} }
if (this.tabs[index].content) { if (this.tabs[index].content) {
this.contentItems = this.tabs[index].content; this.contentItems = this.tabs[index].content;
return; return;
@ -243,6 +263,7 @@ export default {
data: this.tabs[index].data, data: this.tabs[index].data,
}).then(tab => { }).then(tab => {
this.contentItems = this.tabs[index].content = tab.content; this.contentItems = this.tabs[index].content = tab.content;
this.fetchDeArrowContent(this.contentItems);
this.tabs[this.selectedTab].tabNextPage = tab.nextpage; this.tabs[this.selectedTab].tabNextPage = tab.nextpage;
}); });
}, },

View File

@ -1,15 +1,16 @@
<template> <template>
<!-- desktop view --> <!-- desktop view -->
<div v-if="!mobileLayout" class="pp-chapters flex-col overflow-y-scroll max-h-75vh min-h-64 lt-lg:hidden"> <div v-if="!mobileLayout" class="pp-chapters flex-col max-h-75vh min-h-64 lt-lg:hidden">
<h6 aria-label="chapters" title="chapters" class="efy_trans_filter"> <h6 aria-label="chapters" title="chapters" class="efy_trans_filter efy_shadow_trans">
{{ $t("video.chapters") }} - {{ chapters.length }} {{ $t("video.chapters") }} - {{ chapters.length }}
</h6> </h6>
<div <div
:key="chapter.start"
v-for="(chapter, index) in chapters" v-for="(chapter, index) in chapters"
:key="chapter.start"
class="chapter efy_anim_pulse"
:class="isCurrentChapter(index) ? 'pp-chapter-active' : 'efy_shadow_trans efy_trans_filter'"
:role="isCurrentChapter(index) ? 'button' : ''"
@click="$emit('seek', chapter.start)" @click="$emit('seek', chapter.start)"
class="chapter efy_anim_pulse efy_trans_filter"
:class="{ 'pp-chapter-active': isCurrentChapter(index) }"
> >
<div class="flex"> <div class="flex">
<img :src="chapter.image" :alt="chapter.title" /> <img :src="chapter.image" :alt="chapter.title" />
@ -23,11 +24,11 @@
<!-- mobile view --> <!-- mobile view -->
<div v-else class="pp-chapters pp-mobile flex overflow-x-auto"> <div v-else class="pp-chapters pp-mobile flex overflow-x-auto">
<div <div
:key="chapter.start"
v-for="(chapter, index) in chapters" v-for="(chapter, index) in chapters"
@click="$emit('seek', chapter.start)" :key="chapter.start"
class="chapter efy_anim_pulse efy_trans_filter" class="chapter efy_anim_pulse efy_trans_filter"
:class="{ 'pp-chapter-active': isCurrentChapter(index) }" :class="{ 'pp-chapter-active': isCurrentChapter(index) }"
@click="$emit('seek', chapter.start)"
> >
<img :src="chapter.image" :alt="chapter.title" /> <img :src="chapter.image" :alt="chapter.title" />
<div class="m-1 flex"> <div class="m-1 flex">
@ -38,24 +39,12 @@
</div> </div>
</template> </template>
<style>
.chapter {
@apply cursor-pointer self-center p-2.5;
}
.pp-mobile .chapter img {
@apply w-full h-full;
}
.chapter img {
@apply w-3/10 h-3/10;
}
.text-truncate {
@apply truncate overflow-hidden inline-block w-10em;
}
</style>
<script setup> <script setup>
const props = defineProps({ const props = defineProps({
chapters: Object, chapters: {
type: Object,
default: () => null,
},
mobileLayout: { mobileLayout: {
type: Boolean, type: Boolean,
default: () => true, default: () => true,
@ -75,3 +64,51 @@ const isCurrentChapter = index => {
defineEmits(["seek"]); defineEmits(["seek"]);
</script> </script>
<style>
.chapter {
@apply cursor-pointer self-center p-2.5;
}
.pp-mobile .chapter img {
@apply w-full h-full;
}
.chapter img {
@apply w-3/10 h-3/10;
}
.text-truncate {
@apply truncate overflow-hidden inline-block w-10em;
}
.pp-chapters {
margin: -15rem 0 0 0;
padding: var(--efy_gap);
max-width: 400rem;
gap: var(--efy_gap0);
border-radius: var(--efy_radius);
overflow: auto;
}
.pp-chapters .chapter {
padding: 10rem;
border-radius: var(--efy_radius);
border: var(--efy_border);
}
.pp-chapters [title="chapters"] {
padding: 5rem 10rem;
border-radius: var(--efy_radius);
border: var(--efy_border);
}
.pp-chapters .chapter .flex {
gap: 0 15rem;
}
.pp-chapters.pp-mobile {
margin: 15rem 0 0 0;
max-width: 100%;
}
.pp-chapter-active,
.pp-chapters .chapter:hover {
background: var(--efy_color);
background-clip: padding-box;
color: var(--efy_text2);
border: 0 !important;
margin: 0;
}
</style>

View File

@ -0,0 +1,48 @@
<template v-if="text">
<div class="whitespace-pre-wrap">
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-if="showFullText" v-html="fullText()" />
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-else v-html="colapsedText()" />
<span v-if="text.length > 100 && !showFullText">...</span>
<button
v-if="text.length > 100"
style="
margin: 0 0 0 10rem;
background: var(--efy_bg1);
-webkit-text-fill-color: var(--efy_text);
padding: 5rem 8reml;
"
class="efy_shadow_trans efy_shadow_button_off efy_button_text_off"
@click="showFullText = !showFullText"
>
{{ showFullText ? $t("actions.show_less") : $t("actions.show_more") }}
</button>
</div>
</template>
<script>
import { purifyHTML, rewriteDescription } from "@/utils/HtmlUtils";
export default {
props: {
text: {
type: String,
default: null,
},
},
data() {
return {
showFullText: false,
};
},
methods: {
fullText() {
return purifyHTML(rewriteDescription(this.text));
},
colapsedText() {
return purifyHTML(rewriteDescription(this.text.slice(0, 100)));
},
},
};
</script>

View File

@ -1,38 +1,43 @@
<template> <template>
<div class="comment flex mt-1.5"> <div class="comment flex mt-1.5">
<img :src="comment.thumbnail" class="comment-avatar" height="48" width="48" loading="lazy" alt="Avatar" /> <router-link style="height: fit-content" :to="comment.commentorUrl">
<img :src="comment.thumbnail" class="comment-avatar" height="48" width="48" loading="lazy" alt="Avatar" />
</router-link>
<div class="comment-content pl-2"> <div class="comment-content pl-2">
<div class="comment-header"> <div class="comment-header">
<div v-if="comment.pinned" class="comment-pinned"> <div v-if="comment.pinned" class="comment-pinned">
<font-awesome-icon icon="thumbtack" /> <font-awesome-icon icon="thumbtack" />
<span <span
class="ml-1.5"
v-t="{ v-t="{
path: 'comment.pinned_by', path: 'comment.pinned_by',
args: { author: uploader }, args: { author: uploader },
}" }"
class="ml-1.5"
/> />
</div> </div>
<div class="comment-author mt-1 flex"> <div class="comment-author flex align-center">
<router-link class="font-bold link" :to="comment.commentorUrl">{{ comment.author }}</router-link> <router-link class="link font-bold" :to="comment.commentorUrl">{{ comment.author }}</router-link>
<font-awesome-icon class="ml-1.5" v-if="comment.verified" icon="check" /> <font-awesome-icon v-if="comment.verified" class="ml-1.5" icon="check" />
<div class="comment-meta mb-1.5" v-text="' ' + comment.commentedTime + ' '" /> <div class="comment-meta mb-1.5" v-text="' ' + comment.commentedTime + ' '" />
<div class="i-fa-solid:thumbs-up" /> <div class="comment-footer mt-1 flex items-center">
<span class="ml-1" v-text="numberFormat(comment.likeCount)" /> <div class="i-fa6-solid:thumbs-up" />
<font-awesome-icon class="ml-1" v-if="comment.hearted" icon="heart" /> <span class="ml-1" v-text="numberFormat(comment.likeCount)" />
<font-awesome-icon v-if="comment.hearted" class="ml-1" icon="heart" />
</div>
</div> </div>
</div> </div>
<div class="whitespace-pre-wrap" v-html="purifyHTML(comment.commentText)" /> <!-- eslint-disable-next-line vue/no-v-html -->
<div class="whitespace-pre-wrap" v-html="purifiedText" />
<template v-if="comment.repliesPage && (!loadingReplies || !showingReplies)"> <template v-if="comment.repliesPage && (!loadingReplies || !showingReplies)">
<div @click="loadReplies" class="cursor-pointer"> <div class="cursor-pointer" @click="loadReplies">
<a v-text="`${$t('actions.reply_count', comment.replyCount)}`" /> <a v-text="`${$t('actions.reply_count', comment.replyCount)}`" />
<font-awesome-icon class="ml-1.5" icon="level-down-alt" /> <font-awesome-icon class="ml-1.5" icon="level-down-alt" />
</div> </div>
</template> </template>
<template v-if="showingReplies"> <template v-if="showingReplies">
<div @click="hideReplies" class="cursor-pointer"> <div class="cursor-pointer" @click="hideReplies">
<a v-t="'actions.hide_replies'" /> <a v-t="'actions.hide_replies'" />
<font-awesome-icon class="ml-1.5" icon="level-up-alt" /> <font-awesome-icon class="ml-1.5" icon="level-up-alt" />
</div> </div>
@ -41,7 +46,7 @@
<div v-for="reply in replies" :key="reply.commentId" class="w-full"> <div v-for="reply in replies" :key="reply.commentId" class="w-full">
<CommentItem :comment="reply" :uploader="uploader" :video-id="videoId" /> <CommentItem :comment="reply" :uploader="uploader" :video-id="videoId" />
</div> </div>
<div v-if="nextpage" @click="loadReplies" class="cursor-pointer"> <div v-if="nextpage" class="cursor-pointer" @click="loadReplies">
<a v-t="'actions.load_more_replies'" /> <a v-t="'actions.load_more_replies'" />
<font-awesome-icon class="ml-1.5" icon="level-down-alt" /> <font-awesome-icon class="ml-1.5" icon="level-down-alt" />
</div> </div>
@ -51,6 +56,8 @@
</template> </template>
<script> <script>
import { purifyHTML } from "@/utils/HtmlUtils";
export default { export default {
props: { props: {
comment: { comment: {
@ -70,6 +77,11 @@ export default {
nextpage: null, nextpage: null,
}; };
}, },
computed: {
purifiedText() {
return purifyHTML(this.comment.commentText);
},
},
methods: { methods: {
async loadReplies() { async loadReplies() {
if (!this.showingReplies && this.loadingReplies) { if (!this.showingReplies && this.loadingReplies) {
@ -97,4 +109,7 @@ export default {
.comment-content { .comment-content {
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
.comment-avatar {
max-width: unset;
}
</style> </style>

View File

@ -0,0 +1,29 @@
<template>
<ModalComponent @close="$emit('close')">
<div>
<h5 v-text="message" />
<hr />
<div class="w-min flex" style="gap: var(--efy_gap0)">
<button v-t="'actions.cancel'" class="btn" @click="$emit('close')" />
<button v-t="'actions.okay'" class="btn" @click="$emit('confirm')" />
</div>
</div>
</ModalComponent>
</template>
<script>
import ModalComponent from "./ModalComponent.vue";
export default {
components: {
ModalComponent,
},
props: {
message: {
type: String,
required: true,
},
},
emits: ["close", "confirm"],
};
</script>

View File

@ -6,7 +6,10 @@
import { defineAsyncComponent } from "vue"; import { defineAsyncComponent } from "vue";
const props = defineProps({ const props = defineProps({
item: Object, item: {
type: Object,
required: true,
},
}); });
const VideoItem = defineAsyncComponent(() => import("./VideoItem.vue")); const VideoItem = defineAsyncComponent(() => import("./VideoItem.vue"));

View File

@ -1,6 +1,6 @@
<template> <template>
<p v-text="message" /> <p v-text="message" />
<button @click="toggleTrace" class="btn" v-t="'actions.show_more'" /> <button v-t="'actions.show_more'" class="btn" @click="toggleTrace" />
<p ref="stacktrace" class="whitespace-pre-wrap" hidden v-text="error" /> <p ref="stacktrace" class="whitespace-pre-wrap" hidden v-text="error" />
</template> </template>

View File

@ -1,40 +1,97 @@
<template> <template>
<button class="btn mr-2" @click="exportHandler">
<router-link to="/subscriptions">Subscriptions</router-link>
</button>
<span>
<a :href="getRssUrl" class="btn">
<font-awesome-icon icon="rss" />
</a>
</span>
<span class="md:float-right flex pp-sortby-feed">
<SortingSelector by-key="uploaded" @apply="order => videos.sort(order)" />
</span>
<hr /> <hr />
<div class="flex flex-wrap align-center" style="place-content: space-between; gap: var(--efy_gap0)">
<span class="buttons flex" style="gap: var(--efy_gap0)">
<router-link role="button" to="/subscriptions">Subscriptions</router-link>
<a :href="getRssUrl" role="button" class="pp-square">
<font-awesome-icon icon="rss" />
</a>
</span>
<div class="video-grid"> <div class="filters flex align-center">
<VideoItem :is-feed="true" v-for="video in videos" :key="video.url" :item="video" /> <span class="flex">
<label for="filters" v-text="`${$t('actions.filter')}:`" />
<select
id="filters"
v-model="selectedFilter"
default="all"
class="select flex-grow"
@change="onFilterChange()"
>
<option v-for="filter in availableFilters" :key="filter" v-t="`video.${filter}`" :value="filter" />
</select>
</span>
<span class="flex">
<label for="group-selector" v-text="`${$t('titles.channel_groups')}:`" />
<select id="group-selector" v-model="selectedGroupName" default="" class="select flex-grow">
<option v-t="`video.all`" value="" />
<option
v-for="group in channelGroups"
:key="group.groupName"
:value="group.groupName"
v-text="group.groupName"
/>
</select>
</span>
<span class="pp-sortby-feed flex">
<SortingSelector by-key="uploaded" @apply="order => videos.sort(order)" />
</span>
</div>
</div> </div>
<hr />
<LoadingIndicatorPage :show-content="videosStore != null" class="video-grid">
<template v-for="video in filteredVideos" :key="video.url">
<VideoItem v-if="shouldShowVideo(video)" :is-feed="true" :item="video" />
</template>
</LoadingIndicatorPage>
</template> </template>
<style>
.filters {
flex-wrap: wrap;
}
.filters,
.filters span {
gap: var(--efy_gap0);
}
.filters :is(select, label),
.buttons a[role="button"] {
margin: 0 !important;
white-space: nowrap;
align-items: center;
place-content: center;
}
.filters span {
align-items: center;
}
.buttons a[role="button"] {
height: var(--efy_ratio_width);
}
</style>
<script> <script>
import VideoItem from "./VideoItem.vue"; import VideoItem from "./VideoItem.vue";
import SortingSelector from "./SortingSelector.vue"; import SortingSelector from "./SortingSelector.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
export default { export default {
components: { components: {
VideoItem, VideoItem,
SortingSelector, SortingSelector,
LoadingIndicatorPage,
}, },
data() { data() {
return { return {
currentVideoCount: 0, currentVideoCount: 0,
videoStep: 100, videoStep: 100,
videosStore: [], videosStore: null,
videos: [], videos: [],
availableFilters: ["all", "shorts", "videos"],
selectedFilter: "all",
selectedGroupName: "",
channelGroups: [],
}; };
}, },
computed: { computed: {
@ -42,6 +99,12 @@ export default {
if (_this.authenticated) return _this.authApiUrl() + "/feed/rss?authToken=" + _this.getAuthToken(); if (_this.authenticated) return _this.authApiUrl() + "/feed/rss?authToken=" + _this.getAuthToken();
else return _this.authApiUrl() + "/feed/unauthenticated/rss?channels=" + _this.getUnauthenticatedChannels(); else return _this.authApiUrl() + "/feed/unauthenticated/rss?channels=" + _this.getUnauthenticatedChannels();
}, },
filteredVideos(_this) {
const selectedGroup = _this.channelGroups.filter(group => group.groupName == _this.selectedGroupName);
return _this.selectedGroupName == ""
? _this.videos
: _this.videos.filter(video => selectedGroup[0].channels.includes(video.uploaderUrl.substr(-11)));
},
}, },
mounted() { mounted() {
this.fetchFeed().then(videos => { this.fetchFeed().then(videos => {
@ -49,6 +112,23 @@ export default {
this.loadMoreVideos(); this.loadMoreVideos();
this.updateWatched(this.videos); this.updateWatched(this.videos);
}); });
this.selectedFilter = this.getPreferenceString("feedFilter") ?? "all";
if (!window.db) return;
const cursor = this.getChannelGroupsCursor();
cursor.onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
const group = cursor.value;
this.channelGroups.push({
groupName: group.groupName,
channels: JSON.parse(group.channels),
});
cursor.continue();
}
};
}, },
activated() { activated() {
document.title = this.$t("titles.feed") + " - Piped"; document.title = this.$t("titles.feed") + " - Piped";
@ -74,15 +154,31 @@ export default {
} }
}, },
loadMoreVideos() { loadMoreVideos() {
if (!this.videosStore) return;
this.currentVideoCount = Math.min(this.currentVideoCount + this.videoStep, this.videosStore.length); this.currentVideoCount = Math.min(this.currentVideoCount + this.videoStep, this.videosStore.length);
if (this.videos.length != this.videosStore.length) if (this.videos.length != this.videosStore.length) {
this.videos = this.videosStore.slice(0, this.currentVideoCount); this.videos = this.videosStore.slice(0, this.currentVideoCount);
this.fetchDeArrowContent(this.videos);
}
}, },
handleScroll() { handleScroll() {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - window.innerHeight) { if (window.innerHeight + window.scrollY >= document.body.offsetHeight - window.innerHeight) {
this.loadMoreVideos(); this.loadMoreVideos();
} }
}, },
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> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<footer class="efy_trans_filter"> <footer class="efy_trans_filter efy_shadow_trans efy_shadow_button_off">
<a aria-label="GitHub" href="https://github.com/TeamPiped/Piped" target="_blank"> <a aria-label="GitHub" href="https://github.com/TeamPiped/Piped" target="_blank">
<font-awesome-icon :icon="['fab', 'github']" /> <font-awesome-icon :icon="['fab', 'github']" />
<span v-t="'actions.source_code'" /> <span v-t="'actions.source_code'" />
@ -58,12 +58,13 @@ footer {
padding: 15rem 5rem; padding: 15rem 5rem;
border: var(--efy_border); border: var(--efy_border);
} }
footer > a {
margin: 0;
}
footer a { footer a {
color: var(--efy_text) !important; color: var(--efy_text) !important;
-webkit-text-fill-color: var(--efy_text) !important; -webkit-text-fill-color: var(--efy_text) !important;
background: transparent !important; background: transparent !important;
margin: 0;
display: flex;
gap: 8rem;
align-items: center;
} }
</style> </style>

View File

@ -1,13 +1,34 @@
<template> <template>
<h1 class="font-bold text-center" v-t="'titles.history'" /> <hr />
<div class="flex flex-wrap items-center place-content-between" style="gap: var(--efy_gap0)">
<div class="flex place-items-center"> <div class="flex" style="gap: var(--efy_gap0)">
<div class="mr-2"> <button v-t="'actions.clear_history'" class="m-0" @click="clearHistory" />
<button class="btn" v-t="'actions.clear_history'" @click="clearHistory" /> <button v-t="'actions.export_to_json'" class="m-0" @click="exportHistory" />
</div> </div>
<div class="mr-2"> <div class="flex flex-wrap items-center" style="gap: var(--efy_gap0)">
<SortingSelector by-key="watchedAt" @apply="order => videos.sort(order)" /> <div efy_select class="flex flex-wrap" style="gap: var(--efy_gap0)">
<input id="autoDelete" v-model="autoDeleteHistory" type="checkbox" @change="onChange" />
<label v-t="'actions.delete_automatically'" style="margin: 0" for="autoDelete" />
<select
v-model="autoDeleteDelayHours"
class="w-auto"
style="margin: 0 var(--efy_gap0) 0 0"
@change="onChange"
>
<option v-t="{ path: 'info.hours', args: { amount: '1' } }" value="1" />
<option v-t="{ path: 'info.hours', args: { amount: '3' } }" value="3" />
<option v-t="{ path: 'info.hours', args: { amount: '6' } }" value="6" />
<option v-t="{ path: 'info.hours', args: { amount: '12' } }" value="12" />
<option v-t="{ path: 'info.days', args: { amount: '1' } }" value="24" />
<option v-t="{ path: 'info.days', args: { amount: '3' } }" value="72" />
<option v-t="{ path: 'info.weeks', args: { amount: '1' } }" value="168" />
<option v-t="{ path: 'info.weeks', args: { amount: '3' } }" value="336" />
<option v-t="{ path: 'info.months', args: { amount: '1' } }" value="672" />
<option v-t="{ path: 'info.months', args: { amount: '2' } }" value="1344" />
</select>
</div>
<SortingSelector by-key="watchedAt" @apply="order => videos.sort(order)" style="gap: 0" />
</div> </div>
</div> </div>
@ -16,8 +37,6 @@
<div class="video-grid"> <div class="video-grid">
<VideoItem v-for="video in videos" :key="video.url" :item="video" /> <VideoItem v-for="video in videos" :key="video.url" :item="video" />
</div> </div>
<br />
</template> </template>
<script> <script>
@ -32,28 +51,39 @@ export default {
data() { data() {
return { return {
videos: [], videos: [],
autoDeleteHistory: false,
autoDeleteDelayHours: "24",
}; };
}, },
mounted() { mounted() {
this.autoDeleteHistory = this.getPreferenceBoolean("autoDeleteWatchHistory", false);
this.autoDeleteDelayHours = this.getPreferenceString("autoDeleteWatchHistoryDelayHours", "24");
(async () => { (async () => {
if (window.db) { if (window.db && this.getPreferenceBoolean("watchHistory", false)) {
var tx = window.db.transaction("watch_history", "readonly"); var tx = window.db.transaction("watch_history", "readwrite");
var store = tx.objectStore("watch_history"); var store = tx.objectStore("watch_history");
const cursorRequest = store.index("watchedAt").openCursor(null, "prev"); const cursorRequest = store.index("watchedAt").openCursor(null, "prev");
cursorRequest.onsuccess = e => { cursorRequest.onsuccess = e => {
const cursor = e.target.result; const cursor = e.target.result;
if (cursor) { if (cursor) {
const video = cursor.value; const video = cursor.value;
this.videos.push({
url: "/watch?v=" + video.videoId,
title: video.title,
uploaderName: video.uploaderName,
uploaderUrl: video.uploaderUrl,
duration: video.duration,
thumbnail: video.thumbnail,
watchedAt: video.watchedAt,
});
if (this.videos.length < 1000) cursor.continue(); if (this.videos.length < 1000) cursor.continue();
if (!this.shouldRemoveVideo(video)) {
this.videos.push({
url: "/watch?v=" + video.videoId,
title: video.title,
uploaderName: video.uploaderName,
uploaderUrl: video.uploaderUrl,
duration: video.duration,
thumbnail: video.thumbnail,
watchedAt: video.watchedAt,
watched: true,
currentTime: video.currentTime,
});
} else {
store.delete(video.videoId);
}
} }
}; };
} }
@ -71,6 +101,32 @@ export default {
} }
this.videos = []; 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");
},
onChange() {
this.setPreference("autoDeleteWatchHistory", this.autoDeleteHistory);
this.setPreference("autoDeleteWatchHistoryDelayHours", this.autoDeleteDelayHours);
},
shouldRemoveVideo(video) {
if (!this.autoDeleteHistory) return false;
// convert from hours to milliseconds
let maximumTimeDiff = Number(this.autoDeleteDelayHours) * 60 * 60 * 1000;
return Date.now() - video.watchedAt > maximumTimeDiff;
},
}, },
}; };
</script> </script>

View File

@ -1,4 +1,5 @@
<template> <template>
<hr />
<div> <div>
<form style="display: grid; gap: 15rem"> <form style="display: grid; gap: 15rem">
<div> <div>
@ -11,9 +12,7 @@
<input v-model="override" id="import-override" type="checkbox" /> <input v-model="override" id="import-override" type="checkbox" />
<label for="import-override">Override</label> <label for="import-override">Override</label>
</div> </div>
<div> <a class="btn w-auto" @click="handleImport" role="button" style="margin: 0">Import</a>
<a class="btn w-auto" @click="handleImport">Import</a>
</div>
</form> </form>
<br /> <br />
<strong>Importing Subscriptions from YouTube</strong> <strong>Importing Subscriptions from YouTube</strong>
@ -106,10 +105,14 @@ export default {
} }
// FreeTube DB // FreeTube DB
else if (text.indexOf("allChannels") != -1) { else if (text.indexOf("allChannels") != -1) {
const json = JSON.parse(text); const lines = text.split("\n");
json.subscriptions.forEach(item => { for (let line of lines) {
this.subscriptions.push(item.id); if (line === "") continue;
}); const json = JSON.parse(line);
json.subscriptions.forEach(item => {
this.subscriptions.push(item.id);
});
}
} }
// Google Takeout JSON // Google Takeout JSON
else if (text.indexOf("contentDetails") != -1) { else if (text.indexOf("contentDetails") != -1) {

View File

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

View File

@ -1,13 +1,13 @@
<template> <template>
<ModalComponent> <ModalComponent>
<h3 v-t="'titles.account'" class="font-bold my-4" /> <h5 v-t="'titles.account'" class="font-bold my-4" />
<hr /> <hr />
<div class="text-center"> <div class="text-center">
<form class="children:pb-3"> <form class="children:pb-3">
<div> <div>
<input <input
v-model="username" v-model="username"
class="input w-full" class="m-0"
type="text" type="text"
autocomplete="username" autocomplete="username"
:placeholder="$t('login.username')" :placeholder="$t('login.username')"
@ -18,7 +18,7 @@
<div> <div>
<input <input
v-model="password" v-model="password"
class="input w-full" class="m-0"
type="password" type="password"
autocomplete="password" autocomplete="password"
:placeholder="$t('login.password')" :placeholder="$t('login.password')"
@ -26,9 +26,9 @@
v-on:keyup.enter="login" v-on:keyup.enter="login"
/> />
</div> </div>
<div class="flex justify-end"> <div class="flex justify-end p-0!" style="gap: var(--efy_gap0)">
<a class="btn mr-2 cursor-pointer" @click="register" v-t="'titles.register'" /> <a role="button" class="m-0!" @click="register" v-t="'titles.register'" />
<a class="btn cursor-pointer" @click="login" v-t="'titles.login'" /> <a role="button" class="m-0!" @click="login" v-t="'titles.login'" />
</div> </div>
</form> </form>
</div> </div>

View File

@ -0,0 +1,70 @@
<template>
<h1 v-t="'titles.login'" class="my-4 text-center font-bold" />
<hr />
<div class="text-center">
<form class="children:pb-3">
<div>
<input
v-model="username"
class="input"
type="text"
autocomplete="username"
:placeholder="$t('login.username')"
:aria-label="$t('login.username')"
@keyup.enter="login"
/>
</div>
<div>
<input
v-model="password"
class="input"
type="password"
autocomplete="password"
:placeholder="$t('login.password')"
:aria-label="$t('login.password')"
@keyup.enter="login"
/>
</div>
<div>
<a v-t="'titles.login'" class="btn w-auto" @click="login" />
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
username: null,
password: null,
};
},
mounted() {
//TODO: Add Server Side check
if (this.getAuthToken()) {
this.$router.push("/");
}
},
activated() {
document.title = this.$t("titles.login") + " - Piped";
},
methods: {
login() {
if (!this.username || !this.password) return;
this.fetchJson(this.authApiUrl() + "/login", null, {
method: "POST",
body: JSON.stringify({
username: this.username,
password: this.password,
}),
}).then(resp => {
if (resp.token) {
this.setPreference("authToken" + this.hashCode(this.authApiUrl()), resp.token);
window.location = "/"; // done to bypass cache
} else alert(resp.error);
});
},
},
};
</script>

View File

@ -2,7 +2,9 @@
<div class="modal"> <div class="modal">
<div @click="handleClick"> <div @click="handleClick">
<div class="modal-container"> <div class="modal-container">
<button @click="$emit('close')"><font-awesome-icon icon="xmark" /></button> <button @click="$emit('close')" class="pp-color btn m-0">
<font-awesome-icon icon="xmark" />
</button>
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
@ -11,6 +13,7 @@
<script> <script>
export default { export default {
emits: ["close"],
mounted() { mounted() {
window.addEventListener("keydown", this.handleKeyDown); window.addEventListener("keydown", this.handleKeyDown);
}, },
@ -36,16 +39,13 @@ export default {
.modal { .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-dark-900 bg-opacity-80 transition-opacity table;
} }
.modal > div { .modal > div {
@apply table-cell align-middle; @apply table-cell align-middle;
} }
.modal-container { .modal-container {
@apply w-300rem m-auto max-w-[100vw] relative; @apply w-100% m-auto max-w-[421rem] relative;
} }
.modal-container > button { .modal-container > button {
@apply absolute right-50 top-30; @apply absolute right-[12rem] top-[15rem];
} }
</style> </style>

View File

@ -56,10 +56,10 @@
</div> </div>
<div class="lt-md:hidden flex flex-1 justify-start" style="position: relative"> <div class="lt-md:hidden flex flex-1 justify-start" style="position: relative">
<input <input
ref="videoSearch"
v-model="searchText" v-model="searchText"
type="text" type="text"
role="search" role="search"
ref="videoSearch"
:title="$t('actions.search')" :title="$t('actions.search')"
:placeholder="$t('actions.search')" :placeholder="$t('actions.search')"
@keyup="onKeyUp" @keyup="onKeyUp"
@ -78,8 +78,8 @@
<router-link v-t="'titles.preferences'" to="/preferences" /> <router-link v-t="'titles.preferences'" to="/preferences" />
<p <p
v-if="shouldShowLogin" v-if="shouldShowLogin"
class="cursor-pointer font-bold"
v-t="'titles.account'" v-t="'titles.account'"
class="cursor-pointer font-bold"
@click="showLoginModal = !showLoginModal" @click="showLoginModal = !showLoginModal"
/> />
<router-link v-if="shouldShowHistory" v-t="'titles.history'" to="/history" /> <router-link v-if="shouldShowHistory" v-t="'titles.history'" to="/history" />
@ -87,15 +87,8 @@
<router-link v-if="!shouldShowTrending" v-t="'titles.feed'" to="/feed" /> <router-link v-if="!shouldShowTrending" v-t="'titles.feed'" to="/feed" />
<button <button
efy_sidebar_btn="relative, pp-desktop" efy_sidebar_btn="relative, pp-desktop"
style=" style="background: transparent; padding: 0; margin: -5rem 0 0 0; border: 0"
background: transparent; class="efy_trans_filter_off efy_shadow_button_off"
-webkit-text-fill-color: var(--efy_text);
padding: 0;
margin: -5rem 0 0 0;
border: 0;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
"
> >
<i efy_icon="menu" style="margin: 0" /> <i efy_icon="menu" style="margin: 0" />
</button> </button>
@ -103,7 +96,7 @@
</nav> </nav>
<!-- search suggestions for mobile devices --> <!-- search suggestions for mobile devices -->
<div class="w-{full - 4} md:hidden mx-2" style="position: relative"> <div class="w-{full - 4} md:hidden" style="position: relative">
<input <input
v-model="searchText" v-model="searchText"
type="text" type="text"
@ -114,8 +107,9 @@
@keypress="onKeyPress" @keypress="onKeyPress"
@focus="onInputFocus" @focus="onInputFocus"
@blur="onInputBlur" @blur="onInputBlur"
style="margin: 15rem 0 0 0"
/> />
<span v-if="searchText" class="delete-search" @click="searchText = ''">x</span> <span v-if="searchText" class="delete-search" @click="searchText = ''"></span>
</div> </div>
<SearchSuggestions <SearchSuggestions
v-show="(searchText || showSearchHistory) && suggestionsVisible" v-show="(searchText || showSearchHistory) && suggestionsVisible"
@ -128,13 +122,13 @@
<style> <style>
.pp-nav { .pp-nav {
margin-bottom: 15rem;
gap: 15rem; gap: 15rem;
} }
.pp-nav > .pp-logo > a { .pp-nav > .pp-logo > a {
font-size: 25rem; font-size: 25rem;
font-family: "nunito"; font-family: "nunito";
background: transparent; background: transparent;
margin-left: 5rem;
} }
.pp-nav > div input { .pp-nav > div input {
margin: 0 !important; margin: 0 !important;
@ -204,17 +198,17 @@ export default {
suggestionsVisible: false, suggestionsVisible: false,
showLoginModal: false, showLoginModal: false,
showTopNav: false, showTopNav: false,
homePagePath: "/",
registrationDisabled: false,
}; };
}, },
mounted() {
const query = new URLSearchParams(window.location.search).get("search_query");
if (query) this.onSearchTextChange(query);
this.focusOnSearchBar();
},
computed: { computed: {
shouldShowLogin(_this) { shouldShowLogin(_this) {
return _this.getAuthToken() == null; return _this.getAuthToken() == null;
}, },
shouldShowRegister(_this) {
return _this.registrationDisabled == false ? _this.shouldShowLogin : false;
},
shouldShowHistory(_this) { shouldShowHistory(_this) {
return _this.getPreferenceBoolean("watchHistory", false); return _this.getPreferenceBoolean("watchHistory", false);
}, },
@ -225,6 +219,13 @@ export default {
return _this.getPreferenceBoolean("searchHistory", false) && localStorage.getItem("search_history"); return _this.getPreferenceBoolean("searchHistory", false) && localStorage.getItem("search_history");
}, },
}, },
mounted() {
this.fetchAuthConfig();
const query = new URLSearchParams(window.location.search).get("search_query");
if (query) this.onSearchTextChange(query);
this.focusOnSearchBar();
this.homePagePath = this.getHomePage(this);
},
methods: { methods: {
// focus on search bar when Ctrl+k is pressed // focus on search bar when Ctrl+k is pressed
focusOnSearchBar() { focusOnSearchBar() {
@ -241,12 +242,7 @@ export default {
}, },
onKeyPress(e) { onKeyPress(e) {
if (e.key === "Enter") { if (e.key === "Enter") {
e.target.blur(); this.submitSearch(e);
this.$router.push({
name: "SearchResults",
query: { search_query: this.searchText },
});
return;
} }
}, },
onInputFocus() { onInputFocus() {
@ -259,6 +255,22 @@ export default {
onSearchTextChange(searchText) { onSearchTextChange(searchText) {
this.searchText = searchText; this.searchText = searchText;
}, },
async fetchAuthConfig() {
this.fetchJson(this.authApiUrl() + "/config").then(config => {
this.registrationDisabled = config?.registrationDisabled === true;
});
},
onSearchClick(e) {
this.submitSearch(e);
},
submitSearch(e) {
e.target.blur();
this.$router.push({
name: "SearchResults",
query: { search_query: this.searchText },
});
return;
},
}, },
}; };
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="flex flex-col justify-center items-center min-h-[88vh]"> <div class="flex flex-col justify-center items-center min-h-[88vh]">
<h1 class="font-bold !text-[170rem] mb-[-6vh]">404</h1> <h1 class="font-bold !text-[170rem] mb-[-6vh]">404</h1>
<h2 class="!text-[40rem]" v-t="'info.page_not_found'" /> <h2 v-t="'info.page_not_found'" class="!text-[40rem]" />
<a class="btn mt-128" href="/" v-t="'actions.back_to_home'" /> <a v-t="'actions.back_to_home'" role="button" href="/" />
</div> </div>
</template> </template>

View File

@ -1,15 +1,16 @@
<template> <template>
<ModalComponent> <ModalComponent @close="$emit('close')">
<h4 v-t="'actions.select_playlist'" class="mb-2" /> <h4 v-t="'actions.select_playlist'" class="mb-2" />
<select class="select w-full mb-2" v-model="selectedPlaylist"> <select v-model="selectedPlaylist" class="select w-full mb-2">
<option v-for="playlist in playlists" :value="playlist.id" :key="playlist.id" v-text="playlist.name" /> <option v-for="playlist in playlists" :key="playlist.id" :value="playlist.id" v-text="playlist.name" />
</select> </select>
<div class="flex justify-end"> <div class="flex justify-end" style="gap: var(--efy_gap0)">
<button ref="addButton" v-t="'actions.create_playlist'" class="btn pp-color" @click="onCreatePlaylist" />
<button <button
class="btn"
@click="handleClick(selectedPlaylist)"
ref="addButton" ref="addButton"
v-t="'actions.add_to_playlist'" v-t="'actions.add_to_playlist'"
class="btn pp-color"
@click="handleClick(selectedPlaylist)"
/> />
</div> </div>
</ModalComponent> </ModalComponent>
@ -23,11 +24,16 @@ export default {
ModalComponent, ModalComponent,
}, },
props: { props: {
videoInfo: {
type: Object,
required: true,
},
videoId: { videoId: {
type: String, type: String,
required: true, required: true,
}, },
}, },
emits: ["close"],
data() { data() {
return { return {
playlists: [], playlists: [],
@ -62,31 +68,25 @@ export default {
this.$refs.addButton.disabled = true; this.$refs.addButton.disabled = true;
this.processing = true; this.processing = true;
this.fetchJson(this.authApiUrl() + "/user/playlists/add", null, { this.addVideosToPlaylist(playlistId, [this.videoId], [this.videoInfo]).then(json => {
method: "POST",
body: JSON.stringify({
playlistId: playlistId,
videoId: this.videoId,
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
}).then(json => {
this.setPreference("selectedPlaylist" + this.hashCode(this.authApiUrl()), playlistId); this.setPreference("selectedPlaylist" + this.hashCode(this.authApiUrl()), playlistId);
this.$emit("close"); this.$emit("close");
if (json.error) alert(json.error); if (json.error) alert(json.error);
}); });
}, },
async fetchPlaylists() { async fetchPlaylists() {
this.fetchJson(this.authApiUrl() + "/user/playlists", null, { this.getPlaylists().then(json => {
headers: {
Authorization: this.getAuthToken(),
},
}).then(json => {
this.playlists = json; this.playlists = json;
}); });
}, },
onCreatePlaylist() {
const name = prompt(this.$t("actions.create_playlist"));
if (!name) return;
this.createPlaylist(name).then(json => {
if (json.error) alert(json.error);
else this.fetchPlaylists();
});
},
}, },
}; };
</script> </script>

View File

@ -1,34 +1,47 @@
<template> <template>
<div> <div class="video-card flex flex-col flex-justify-between efy_shadow_trans">
<router-link :to="props.item.url"> <router-link :to="props.item.url">
<div class="relative"> <div class="relative">
<img class="w-full" :src="props.item.thumbnail" loading="lazy" /> <img class="thumbnail" :src="props.item.thumbnail" loading="lazy" />
</div>
<div class="flex items-center h-[44rem] overflow-hidden">
<p v-text="props.item.name" class="pp-video-card-title" />
</div> </div>
<p>
<span v-text="props.item.name" />
<font-awesome-icon class="ml-1.5" v-if="props.item.verified" icon="check" />
</p>
</router-link> </router-link>
<p v-if="props.item.description" v-text="props.item.description" /> <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" />
<font-awesome-icon class="ml-1.5" v-if="props.item.uploaderVerified" icon="check" />
</p>
</router-link>
<a v-if="props.item.uploaderName" class="link" v-text="props.item.uploaderName" /> <div class="pp-video-card-buttons">
<template v-if="props.item.videos >= 0"> <button
<br v-if="props.item.uploaderName" /> v-if="props.item.videos >= 0"
<strong v-text="`${props.item.videos} ${$t('video.videos')}`" /> v-text="`${props.item.videos} ${$t('video.videos')}`"
</template> class="efy_shadow_trans efy_shadow_button_off efy_button_text_off"
/>
<br /> <router-link
v-if="props.item.uploaderUrl && item.uploaderName"
:to="props.item.uploaderUrl"
:title="props.item.uploaderName"
class="pp-video-card-channel"
style="padding: 0; flex-grow: 1; background: transparent; border: 0"
>
<div class="pp-text efy_shadow_trans efy_shadow_button_off flex-grow-1">
<span v-text="props.item.uploaderName" style="max-width: 106rem" />
<font-awesome-icon class="ml-1.5" v-if="item.uploaderVerified" icon="check" />
</div>
</router-link>
<a
v-else-if="props.item.uploaderName"
class="pp-video-card-channel efy_shadow_trans"
v-text="props.item.uploaderName"
/>
</div>
</div> </div>
</template> </template>
<script setup> <script setup>
const props = defineProps({ const props = defineProps({
item: Object, item: {
type: Object,
required: true,
},
}); });
</script> </script>

View File

@ -1,29 +1,53 @@
<template> <template>
<ErrorHandler v-if="playlist && playlist.error" :message="playlist.message" :error="playlist.error" /> <ErrorHandler v-if="playlist && playlist.error" :message="playlist.message" :error="playlist.error" />
<div v-if="playlist" v-show="!playlist.error"> <LoadingIndicatorPage v-show="!playlist?.error" :show-content="playlist">
<h1 class="text-center my-4" v-text="playlist.name" /> <hr />
<h5 class="mb-[10rem]" v-text="playlist.name" />
<div class="flex justify-between items-center"> <CollapsableText v-if="playlist?.description" :text="playlist.description" />
<div> <hr />
<router-link class="link" :to="playlist.uploaderUrl || '/'">
<img :src="playlist.uploaderAvatar" loading="lazy" /> <div class="flex items-center justify-between">
<strong v-text="playlist.uploader" /> <div class="pp-video-card-buttons m-0!">
<router-link
:to="playlist.uploaderUrl || '/'"
class="pp-video-card-channel p-0!"
style="background: transparent; border: 0"
>
<img
v-if="playlist.uploaderAvatar"
:src="playlist.uploaderAvatar"
loading="lazy"
width="36"
height="36"
class="w-36rem h-36rem efy_shadow_trans"
/>
<button class="pp-text efy_shadow_trans efy_shadow_button_off efy_button_text_off">
<span v-text="playlist.uploader" />
<font-awesome-icon class="ml-1.5" v-if="playlist.uploaderVerified" icon="check" />
</button>
</router-link> </router-link>
<button
v-text="`${playlist.videos} ${$t('video.videos')}`"
class="efy_button_text_off efy_shadow_trans efy_shadow_button_off"
/>
</div> </div>
<div> <div class="pp-flex-bookmarks">
<strong v-text="`${playlist.videos} ${$t('video.videos')}`" /> <button v-if="!isPipedPlaylist" class="btn" @click="bookmarkPlaylist">
<br /> <font-awesome-icon class="mr-[5rem]" icon="bookmark" />
<button class="btn mr-1 ml-2" v-if="authenticated && !isPipedPlaylist" @click="clonePlaylist"> {{ $t(`actions.${isBookmarked ? "playlist_bookmarked" : "bookmark_playlist"}`) }}
{{ $t("actions.clone_playlist") }}<font-awesome-icon class="ml-3" icon="clone" /> </button>
<button v-if="authenticated && !isPipedPlaylist" class="btn mr-1 ml-2" @click="clonePlaylist">
<font-awesome-icon class="mr-[5rem]" icon="clone" />{{ $t("actions.clone_playlist") }}
</button> </button>
<button class="btn mr-1" @click="downloadPlaylistAsTxt"> <button class="btn mr-1" @click="downloadPlaylistAsTxt">
{{ $t("actions.download_as_txt") }} {{ $t("actions.download_as_txt") }}
</button> </button>
<a class="btn" :href="getRssUrl"> <a :href="getRssUrl" role="button" class="btn pp-square">
<font-awesome-icon icon="rss" /> <font-awesome-icon icon="rss" />
</a> </a>
<WatchOnYouTubeButton :link="`https://www.youtube.com/playlist?list=${this.$route.query.list}`" /> <WatchOnButton :link="`https://www.youtube.com/playlist?list=${$route.query.list}`" class="pp-square" />
</div> </div>
</div> </div>
@ -37,29 +61,45 @@
:index="index" :index="index"
:playlist-id="$route.query.list" :playlist-id="$route.query.list"
:admin="admin" :admin="admin"
@remove="removeVideo(index)"
height="94" height="94"
width="168" width="168"
@remove="removeVideo(index)"
/> />
</div> </div>
</div> </LoadingIndicatorPage>
</template> </template>
<style>
.pp-flex-bookmarks {
display: flex;
flex-wrap: wrap;
gap: var(--efy_gap0);
}
.pp-flex-bookmarks > * {
margin: 0;
}
</style>
<script> <script>
import ErrorHandler from "./ErrorHandler.vue"; import ErrorHandler from "./ErrorHandler.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import CollapsableText from "./CollapsableText.vue";
import VideoItem from "./VideoItem.vue"; import VideoItem from "./VideoItem.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; import WatchOnButton from "./WatchOnButton.vue";
export default { export default {
components: { components: {
ErrorHandler, ErrorHandler,
VideoItem, VideoItem,
WatchOnYouTubeButton, WatchOnButton,
LoadingIndicatorPage,
CollapsableText,
}, },
data() { data() {
return { return {
playlist: null, playlist: null,
admin: false, admin: false,
isBookmarked: false,
}; };
}, },
computed: { computed: {
@ -74,19 +114,17 @@ export default {
}, },
}, },
mounted() { mounted() {
this.getPlaylistData();
const playlistId = this.$route.query.list; const playlistId = this.$route.query.list;
if (this.authenticated && playlistId?.length == 36) if (this.authenticated && playlistId?.length == 36)
this.fetchJson(this.authApiUrl() + "/user/playlists", null, { this.getPlaylists().then(json => {
headers: {
Authorization: this.getAuthToken(),
},
}).then(json => {
if (json.error) alert(json.error); 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;
}); });
else if (playlistId.startsWith("local")) this.admin = true;
this.isPlaylistBookmarked();
}, },
activated() { activated() {
this.getPlaylistData();
window.addEventListener("scroll", this.handleScroll); window.addEventListener("scroll", this.handleScroll);
if (this.playlist) this.updateTitle(); if (this.playlist) this.updateTitle();
}, },
@ -95,12 +133,21 @@ export default {
}, },
methods: { methods: {
async fetchPlaylist() { async fetchPlaylist() {
const playlistId = this.$route.query.list;
if (playlistId.startsWith("local")) {
return this.getPlaylist(playlistId);
}
return await await this.fetchJson(this.authApiUrl() + "/playlists/" + this.$route.query.list); return await await this.fetchJson(this.authApiUrl() + "/playlists/" + this.$route.query.list);
}, },
async getPlaylistData() { async getPlaylistData() {
this.fetchPlaylist() this.fetchPlaylist()
.then(data => (this.playlist = data)) .then(data => (this.playlist = data))
.then(() => this.updateTitle()); .then(() => {
this.updateTitle();
this.updateWatched(this.playlist.relatedStreams);
this.fetchDeArrowContent(this.playlist.relatedStreams);
});
}, },
async updateTitle() { async updateTitle() {
document.title = this.playlist.name + " - Piped"; document.title = this.playlist.name + " - Piped";
@ -116,6 +163,7 @@ export default {
this.playlist.nextpage = json.nextpage; this.playlist.nextpage = json.nextpage;
this.loading = false; this.loading = false;
json.relatedStreams.map(stream => this.playlist.relatedStreams.push(stream)); json.relatedStreams.map(stream => this.playlist.relatedStreams.push(stream));
this.fetchDeArrowContent(this.playlist.relatedStreams);
}); });
} }
}, },
@ -144,6 +192,48 @@ export default {
}); });
this.download(data, this.playlist.name + ".txt", "text/plain"); 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> </script>

View File

@ -29,18 +29,7 @@ export default {
}, },
selectedIndex: { selectedIndex: {
type: Number, type: Number,
}, required: true,
},
mounted() {
this.updateScroll();
},
methods: {
updateScroll() {
const elems = Array.from(this.$refs.scrollable.children).filter(elm => elm.matches("div"));
const index = this.selectedIndex - 1;
if (index < elems.length)
this.$refs.scrollable.scrollTop =
elems[this.selectedIndex - 1].offsetTop - this.$refs.scrollable.offsetTop;
}, },
}, },
watch: { watch: {
@ -54,5 +43,18 @@ export default {
deep: true, deep: true,
}, },
}, },
mounted() {
this.updateScroll();
this.updateWatched(this.playlist.relatedStreams);
},
methods: {
updateScroll() {
const elems = Array.from(this.$refs.scrollable.children).filter(elm => elm.matches("div"));
const index = this.selectedIndex - 1;
if (index < elems.length)
this.$refs.scrollable.scrollTop =
elems[this.selectedIndex - 1].offsetTop - this.$refs.scrollable.offsetTop;
},
},
}; };
</script> </script>

View File

@ -1,125 +1,176 @@
<template> <template>
<h1 class="font-bold text-center my-4" v-t="'titles.playlists'" /> <hr />
<div class="flex flex-wrap justify-between items-center" style="gap: var(--efy_gap0)">
<button
v-t="'actions.create_playlist'"
style="height: var(--efy_ratio_width); margin: 0"
@click="onCreatePlaylist"
/>
<div class="flex flex-wrap" style="gap: var(--efy_gap0)">
<button
v-if="playlists.length > 0"
v-t="'actions.export_to_json'"
@click="exportPlaylists"
style="height: var(--efy_ratio_width); margin: 0"
/>
<input
id="fileSelector"
ref="fileSelector"
type="file"
class="display-none"
multiple="multiple"
@change="importPlaylists"
/>
<label v-t="'actions.import_from_json_csv'" for="fileSelector" class="m-0! font-bold" role="button" />
</div>
</div>
<hr /> <hr />
<div> <div class="video-grid">
<div class="flex justify-between"> <div v-for="playlist in playlists" :key="playlist.id" class="video-card efy_trans_filter efy_shadow_trans">
<button v-t="'actions.create_playlist'" class="btn mr-2" @click="onCreatePlaylist" /> <router-link :to="`/playlist?list=${playlist.id}`">
<div class="flex"> <img class="thumbnail" :src="playlist.thumbnail" alt="thumbnail" />
<p
style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; margin: 0 15rem"
class="flex link"
:title="playlist.name"
v-text="playlist.name"
/>
</router-link>
<div class="pp-video-card-buttons flex gap-15rem children:m-0" style="flex-wrap: wrap">
<button <button
v-if="this.playlists.length > 0" v-text="`${playlist.videos} ${$t('video.videos')}`"
v-t="'actions.export_to_json'" class="efy_shadow_trans efy_shadow_button_off efy_button_text_off"
class="btn"
@click="exportPlaylists"
/> />
<input <button
id="fileSelector" v-t="'actions.edit_playlist'"
ref="fileSelector" class="pp-color h-auto"
type="file" @click="showPlaylistEditModal(playlist)"
class="display-none" />
@change="importPlaylists" <button
v-t="'actions.delete_playlist'"
class="pp-color h-auto"
@click="playlistToDelete = playlist.id"
/> />
<label for="fileSelector" v-t="'actions.import_from_json'" class="btn ml-2" role="button" />
</div> </div>
</div> <ModalComponent v-if="playlist.id == playlistToEdit" @close="playlistToEdit = null">
<div class="flex flex-col gap-2">
<div class="video-grid"> <h2 v-t="'actions.edit_playlist'" />
<div v-for="playlist in playlists" :key="playlist.id" class="efy_trans_filter"> <input
<router-link :to="`/playlist?list=${playlist.id}`"> v-model="newPlaylistName"
<img class="w-full" :src="playlist.thumbnail" alt="thumbnail" /> class="input"
<p type="text"
style="display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical" :placeholder="$t('actions.playlist_name')"
class="flex link"
:title="playlist.name"
v-text="playlist.name"
/> />
</router-link> <input
<div class="pp-video-card-buttons flex gap-15rem children:m-0" style="flex-wrap: wrap"> v-model="newPlaylistDescription"
<button class="input"
class="thumbnail-overlay thumbnail-right" type="text"
v-text="`${playlist.videos} ${$t('video.videos')}`" :placeholder="$t('actions.playlist_description')"
/>
<button
class="pp-color h-auto"
@click="renamePlaylist(playlist.id)"
v-t="'actions.rename_playlist'"
/>
<button
class="pp-color h-auto"
@click="deletePlaylist(playlist.id)"
v-t="'actions.delete_playlist'"
/> />
<button v-t="'actions.okay'" class="btn ml-auto" @click="editPlaylist(playlist)" />
</div> </div>
</ModalComponent>
<ConfirmModal
v-if="playlistToDelete == playlist.id"
:message="$t('actions.delete_playlist_confirm')"
@close="playlistToDelete = null"
@confirm="onDeletePlaylist(playlist.id)"
/>
</div>
</div>
<hr />
<h5 v-if="bookmarks" v-t="'titles.bookmarks'" class="mb-[15rem]" />
<div v-if="bookmarks" class="video-grid">
<div
v-for="(playlist, index) in bookmarks"
:key="playlist.playlistId"
class="pp-bookmark video-card efy_trans_filter efy_shadow_trans"
>
<router-link :to="`/playlist?list=${playlist.playlistId}`">
<img class="thumbnail" :src="playlist.thumbnail" alt="thumbnail" />
<div class="flex items-center h-[44rem] overflow-hidden">
<p class="pp-video-card-title" :title="playlist.name" v-text="playlist.name" />
</div>
</router-link>
<div class="pp-video-card-buttons flex gap-15rem">
<button @click.prevent="removeBookmark(index)" class="btn pp-color aspect-square">
<font-awesome-icon icon="bookmark" />
</button>
<button
v-text="`${playlist.videos} ${$t('video.videos')}`"
class="efy_shadow_trans efy_shadow_button_off efy_button_text_off"
/>
</div> </div>
<a :href="playlist.uploaderUrl" class="pp-video-card-channel">
<img class="w-36rem h-36rem efy_shadow_trans" :src="playlist.uploaderAvatar" width="36" height="36" />
<div class="pp-text efy_shadow_trans">
<span v-text="playlist.uploader" />
</div>
</a>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import ConfirmModal from "./ConfirmModal.vue";
import ModalComponent from "./ModalComponent.vue";
export default { export default {
components: { ConfirmModal, ModalComponent },
data() { data() {
return { return {
playlists: [], playlists: [],
bookmarks: [],
playlistToDelete: null,
playlistToEdit: null,
newPlaylistName: "",
newPlaylistDescription: "",
}; };
}, },
mounted() { mounted() {
if (this.authenticated) this.fetchPlaylists(); this.fetchPlaylists();
else this.$router.push("/login"); this.loadPlaylistBookmarks();
}, },
activated() { activated() {
document.title = this.$t("titles.playlists") + " - Piped"; document.title = this.$t("titles.playlists") + " - Piped";
}, },
methods: { methods: {
fetchPlaylists() { fetchPlaylists() {
this.fetchJson(this.authApiUrl() + "/user/playlists", null, { this.getPlaylists().then(json => {
headers: {
Authorization: this.getAuthToken(),
},
}).then(json => {
this.playlists = json; this.playlists = json;
}); });
}, },
renamePlaylist(id) { showPlaylistEditModal(playlist) {
const newName = prompt(this.$t("actions.new_playlist_name")); this.newPlaylistName = playlist.name;
if (!newName) return; this.newPlaylistDescription = playlist.description;
this.fetchJson(this.authApiUrl() + "/user/playlists/rename", null, { this.playlistToEdit = playlist.id;
method: "POST",
body: JSON.stringify({
playlistId: id,
newName: newName,
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
}).then(json => {
if (json.error) alert(json.error);
else {
this.playlists.forEach((playlist, index) => {
if (playlist.id == id) {
this.playlists[index].name = newName;
return;
}
});
}
});
}, },
deletePlaylist(id) { editPlaylist(selectedPlaylist) {
if (confirm(this.$t("actions.delete_playlist_confirm"))) // save the new name and description since they could be overwritten during the http request
this.fetchJson(this.authApiUrl() + "/user/playlists/delete", null, { const newName = this.newPlaylistName;
method: "POST", const newDescription = this.newPlaylistDescription;
body: JSON.stringify({ if (newName != selectedPlaylist.name) {
playlistId: id, this.renamePlaylist(selectedPlaylist.id, newName).then(json => {
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
}).then(json => {
if (json.error) alert(json.error); if (json.error) alert(json.error);
else this.playlists = this.playlists.filter(playlist => playlist.id !== id); else selectedPlaylist.name = newName;
}); });
}
if (newDescription != selectedPlaylist.description) {
this.changePlaylistDescription(selectedPlaylist.id, newDescription).then(json => {
if (json.error) alert(json.error);
else selectedPlaylist.description = newDescription;
});
}
this.playlistToEdit = null;
},
onDeletePlaylist(id) {
this.deletePlaylist(id).then(json => {
if (json.error) alert(json.error);
else this.playlists = this.playlists.filter(playlist => playlist.id !== id);
});
this.playlistToDelete = null;
}, },
onCreatePlaylist() { onCreatePlaylist() {
const name = prompt(this.$t("actions.create_playlist")); const name = prompt(this.$t("actions.create_playlist"));
@ -129,19 +180,6 @@ export default {
else this.fetchPlaylists(); 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() { async exportPlaylists() {
if (!this.playlists) return; if (!this.playlists) return;
let json = { let json = {
@ -154,8 +192,8 @@ export default {
this.download(JSON.stringify(json), "playlists.json", "application/json"); this.download(JSON.stringify(json), "playlists.json", "application/json");
}, },
async fetchPlaylistJson(playlistId) { async fetchPlaylistJson(playlistId) {
let playlist = await this.fetchJson(this.authApiUrl() + "/playlists/" + playlistId); let playlist = await this.getPlaylist(playlistId);
let playlistJson = { return {
name: playlist.name, name: playlist.name,
// possible other types: history, watch later, ... // possible other types: history, watch later, ...
type: "playlist", type: "playlist",
@ -164,57 +202,69 @@ export default {
// list of the videos, starting with "https://youtube.com" to clarify that those are YT videos // 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), videos: playlist.relatedStreams.map(stream => "https://youtube.com" + stream.url),
}; };
return playlistJson;
}, },
async importPlaylists() { async importPlaylists() {
const file = this.$refs.fileSelector.files[0]; const files = this.$refs.fileSelector.files;
for (let file of files) {
await this.importPlaylistFile(file);
}
window.location.reload();
},
async importPlaylistFile(file) {
let text = await file.text(); let text = await file.text();
let tasks = []; let tasks = [];
// list of playlists exported from Piped // list of playlists exported from Piped
if (text.includes("playlists")) { if (file.name.slice(-4).toLowerCase() == ".csv") {
const lines = text.split("\n");
const playlistName = lines[1].split(",")[4];
const playlist = {
name: playlistName != "" ? playlistName : new Date().toJSON(),
videos: lines
.slice(4, lines.length)
.filter(line => line != "")
.slice(1)
.map(line => `https://youtube.com/watch?v=${line.split(",")[0]}`),
};
tasks.push(this.createPlaylistWithVideos(playlist));
} else if (text.includes('"Piped"')) {
// CSV from Google Takeout
let playlists = JSON.parse(text).playlists; let playlists = JSON.parse(text).playlists;
if (!playlists.length) { if (!playlists.length) {
alert(this.$t("actions.no_valid_playlists")); alert(this.$t("actions.no_valid_playlists"));
return; return;
} }
for (var i = 0; i < playlists.length; i++) { for (let playlist of playlists) {
tasks.push(this.createPlaylistWithVideos(playlists[i])); tasks.push(this.createPlaylistWithVideos(playlist));
} }
// 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 { } else {
alert(this.$t("actions.no_valid_playlists")); alert(this.$t("actions.no_valid_playlists"));
return; return;
} }
await Promise.all(tasks); await Promise.all(tasks);
window.location.reload();
}, },
async createPlaylistWithVideos(playlist) { async createPlaylistWithVideos(playlist) {
let newPlaylist = await this.createPlaylist(playlist.name); let newPlaylist = await this.createPlaylist(playlist.name);
let videoIds = playlist.videos.map(url => url.substr(-11)); let videoIds = playlist.videos.map(url => url.substr(-11));
await this.addVideosToPlaylist(newPlaylist.playlistId, videoIds); await this.addVideosToPlaylist(newPlaylist.playlistId, videoIds);
}, },
async addVideosToPlaylist(playlistId, videoIds) { async loadPlaylistBookmarks() {
await this.fetchJson(this.authApiUrl() + "/user/playlists/add", null, { if (!window.db) return;
method: "POST", var tx = window.db.transaction("playlist_bookmarks", "readonly");
body: JSON.stringify({ var store = tx.objectStore("playlist_bookmarks");
playlistId: playlistId, const cursorRequest = store.openCursor();
videoIds: videoIds, cursorRequest.onsuccess = e => {
}), const cursor = e.target.result;
headers: { if (cursor) {
Authorization: this.getAuthToken(), this.bookmarks.push(cursor.value);
"Content-Type": "application/json", 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

@ -1,7 +1,7 @@
<template> <template>
<div class="pp-pref-cards"> <div class="pp-pref-cards">
<div efy_card="grid"> <div efy_card="grid">
<h2>Quick</h2> <h5>Quick</h5>
<label class="pref" for="ddlLanguageSelection"> <label class="pref" for="ddlLanguageSelection">
<strong v-t="'actions.language_selection'" /> <strong v-t="'actions.language_selection'" />
<select <select
@ -85,42 +85,60 @@
</select> </select>
</label> </label>
</template> </template>
<br />
<p v-t="'info.preferences_note'" />
<button class="btn" v-t="'actions.reset_preferences'" @click="resetPreferences()" />
<button class="btn mx-4" v-t="'actions.backup_preferences'" @click="backupPreferences()" />
<label
for="fileSelector"
class="btn text-center"
v-t="'actions.restore_preferences'"
@click="restorePreferences()"
/>
<input class="hidden" id="fileSelector" ref="fileSelector" type="file" @change="restorePreferences()" />
<div class="pref items-start! flex-col">
<strong>Preferences</strong>
<div class="flex flex-wrap" style="gap: var(--efy_gap0)">
<button style="height: var(--efy_ratio_width)" @click="showConfirmResetPrefsDialog = true">
Reset
</button>
<button style="height: var(--efy_ratio_width)" @click="backupPreferences()">Backup</button>
<label for="fileSelector" class="btn text-center" role="button" @click="restorePreferences()">
Restore
</label>
<input
id="fileSelector"
ref="fileSelector"
class="hidden"
type="file"
@change="restorePreferences()"
/>
</div>
<p v-t="'info.preferences_note'" />
<ConfirmModal
v-if="showConfirmResetPrefsDialog"
:message="$t('actions.confirm_reset_preferences')"
@close="showConfirmResetPrefsDialog = false"
@confirm="resetPreferences()"
/>
</div>
<!-- options that are visible only when logged in --> <!-- options that are visible only when logged in -->
<div v-if="this.authenticated"> <div v-if="authenticated" class="pref items-start! flex-col">
<label class="pp-delete-account pref" for="txtDeleteAccountPassword" efy_card="grid"> <label v-t="'actions.delete_account'" for="txtDeleteAccountPassword" class="font-bold" />
<h6 v-t="'actions.delete_account'" /> <div class="flex flex-wrap" style="gap: var(--efy_gap0)">
<input <input
id="txtDeleteAccountPassword" id="txtDeleteAccountPassword"
ref="txtDeleteAccountPassword" ref="txtDeleteAccountPassword"
v-model="password" v-model="password"
v-on:keyup.enter="deleteAccount"
:placeholder="$t('login.password')" :placeholder="$t('login.password')"
:aria-label="$t('login.password')" :aria-label="$t('login.password')"
class="input w-auto mr-2" class="input mr-2 w-auto"
type="password" type="password"
@keyup.enter="deleteAccount"
/> />
<a class="btn w-full" @click="deleteAccount" v-t="'actions.delete_account'" /> <button v-t="'actions.delete_account'" class="w-auto" @click="deleteAccount" />
</label> </div>
<button class="btn w-full" @click="logout" v-t="'actions.logout'" /> </div>
<button class="btn w-full" @click="invalidateSession" v-t="'actions.invalidate_session'" /> <div v-if="authenticated" class="pref items-start! flex-col" style="border-bottom: var(--efy_border)">
<strong>Logout</strong>
<div class="flex flex-wrap" style="gap: var(--efy_gap0)">
<button v-t="'actions.logout'" class="w-auto" @click="logout" />
<button v-t="'actions.invalidate_session'" class="w-auto" @click="invalidateSession" />
</div>
</div> </div>
</div> </div>
<div efy_card="grid"> <div efy_card="grid">
<h2 v-t="'titles.player'" /> <h5 v-t="'titles.player'" />
<label class="pref" for="chkAutoPlayVideo"> <label class="pref" for="chkAutoPlayVideo">
<strong v-t="'actions.autoplay_video'" /> <strong v-t="'actions.autoplay_video'" />
<input <input
@ -131,6 +149,26 @@
@change="onChange($event)" @change="onChange($event)"
/> />
</label> </label>
<label class="pref" for="chkAutoDisplayCaptions">
<strong v-t="'actions.auto_display_captions'" />
<input
id="chkAutoDisplayCaptions"
v-model="autoDisplayCaptions"
class="checkbox"
type="checkbox"
@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"> <label class="pref" for="chkAudioOnly">
<strong v-t="'actions.audio_only'" /> <strong v-t="'actions.audio_only'" />
<input id="chkAudioOnly" v-model="listen" class="checkbox" type="checkbox" @change="onChange($event)" /> <input id="chkAudioOnly" v-model="listen" class="checkbox" type="checkbox" @change="onChange($event)" />
@ -157,7 +195,7 @@
<input <input
id="txtBufferingGoal" id="txtBufferingGoal"
v-model="bufferingGoal" v-model="bufferingGoal"
class="input w-auto" class="input w-24"
type="text" type="text"
@change="onChange($event)" @change="onChange($event)"
/> />
@ -202,6 +240,20 @@
@change="onChange($event)" @change="onChange($event)"
/> />
</label> </label>
<!-- chapters layout on mobile -->
<label class="pref" for="chkMinimizeChapters">
<strong v-t="'actions.chapters_layout_mobile'" />
<select
id="ddlDefaultHomepage"
v-model="mobileChapterLayout"
class="select w-auto"
@change="onChange($event)"
>
<option v-t="'video.chapters_horizontal'" value="Horizontal" />
<option v-t="'video.chapters_vertical'" value="Vertical" />
</select>
</label>
<label class="pref" for="chkShowWatchOnYouTube"> <label class="pref" for="chkShowWatchOnYouTube">
<strong v-t="'actions.show_watch_on_youtube'" /> <strong v-t="'actions.show_watch_on_youtube'" />
<input <input
@ -212,6 +264,16 @@
@change="onChange($event)" @change="onChange($event)"
/> />
</label> </label>
<label class="pref" for="chkShowSearchSuggestions">
<strong v-t="'actions.show_search_suggestions'" />
<input
id="chkShowSearchSuggestions"
v-model="searchSuggestions"
class="checkbox"
type="checkbox"
@change="onChange($event)"
/>
</label>
<label class="pref" for="chkStoreSearchHistory"> <label class="pref" for="chkStoreSearchHistory">
<strong v-t="'actions.store_search_history'" /> <strong v-t="'actions.store_search_history'" />
<input <input
@ -247,7 +309,7 @@
<select <select
id="ddlEnabledCodecs" id="ddlEnabledCodecs"
v-model="enabledCodecs" v-model="enabledCodecs"
class="select w-auto h-auto" class="select h-auto w-auto"
multiple multiple
@change="onChange($event)" @change="onChange($event)"
> >
@ -279,12 +341,16 @@
</div> </div>
<div efy_card="grid"> <div efy_card="grid">
<h2>SponsorBlock</h2> <h5>SponsorBlock + DeArrow</h5>
<p> <p class="pref" style="justify-content: unset !important">
<span v-t="'actions.uses_api_from'" /><a class="link" href="https://sponsor.ajay.app/" <span v-t="'actions.uses_api_from'" /><a class="link" href="https://sponsor.ajay.app/"
>sponsor.ajay.app</a >sponsor.ajay.app</a
> >
</p> </p>
<label class="pref" for="chkDeArrow">
<strong v-t="'actions.enable_dearrow'" />
<input id="chkDeArrow" v-model="dearrow" class="checkbox" type="checkbox" @change="onChange($event)" />
</label>
<label class="pref" for="chkEnableSponsorblock"> <label class="pref" for="chkEnableSponsorblock">
<strong v-t="'actions.enable_sponsorblock'" /> <strong v-t="'actions.enable_sponsorblock'" />
<input <input
@ -295,106 +361,41 @@
@change="onChange($event)" @change="onChange($event)"
/> />
</label> </label>
<label class="pref" for="chkSkipSponsors"> <div v-if="sponsorBlock">
<strong v-t="'actions.skip_sponsors'" /> <label v-for="[name, item] in skipOptions" :key="name" class="pref" :for="'ddlSkip_' + name">
<input <strong v-t="item.label" />
id="chkSkipSponsors" <select
v-model="skipSponsor" :id="'ddlSkip_' + name"
class="checkbox" v-model="item.value"
type="checkbox" class="select w-auto"
@change="onChange($event)" @change="onChange($event)"
/> >
</label> <option v-t="'actions.no'" value="no" />
<label class="pref" for="chkSkipIntro"> <option v-t="'actions.skip_button_only'" value="button" />
<strong v-t="'actions.skip_intro'" /> <option v-t="'actions.skip_automatically'" value="auto" />
<input </select>
id="chkSkipIntro" </label>
v-model="skipIntro" <label class="pref" for="chkShowMarkers">
class="checkbox" <strong v-t="'actions.show_markers'" />
type="checkbox" <input
@change="onChange($event)" id="chkShowMarkers"
/> v-model="showMarkers"
</label> class="checkbox"
<label class="pref" for="chkSkipOutro"> type="checkbox"
<strong v-t="'actions.skip_outro'" /> @change="onChange($event)"
<input />
id="chkSkipOutro" </label>
v-model="skipOutro" <label class="pref" for="txtMinSegmentLength" style="border-bottom: var(--efy_border)">
class="checkbox" <strong v-t="'actions.min_segment_length'" />
type="checkbox" <input
@change="onChange($event)" id="txtMinSegmentLength"
/> v-model="minSegmentLength"
</label> class="input w-24"
<label class="pref" for="chkSkipPreview"> type="text"
<strong v-t="'actions.skip_preview'" /> @change="onChange($event)"
<input />
id="chkSkipPreview" </label>
v-model="skipPreview" </div>
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> </div>
</div> </div>
@ -406,7 +407,7 @@
<th v-t="'preferences.instance_locations'" /> <th v-t="'preferences.instance_locations'" />
<th v-t="'preferences.has_cdn'" /> <th v-t="'preferences.has_cdn'" />
<th v-t="'preferences.registered_users'" /> <th v-t="'preferences.registered_users'" />
<th class="lt-md:hidden" v-t="'preferences.version'" /> <th v-t="'preferences.version'" class="lt-md:hidden" />
<th v-t="'preferences.up_to_date'" /> <th v-t="'preferences.up_to_date'" />
<th v-t="'preferences.ssl_score'" /> <th v-t="'preferences.ssl_score'" />
</tr> </tr>
@ -420,7 +421,7 @@
<td class="lt-md:hidden" v-text="instance.version" /> <td class="lt-md:hidden" v-text="instance.version" />
<td v-text="`${instance.up_to_date ? '&#9989;' : '&#10060;'}`" /> <td v-text="`${instance.up_to_date ? '&#9989;' : '&#10060;'}`" />
<td> <td>
<a :href="sslScore(instance.api_url)" target="_blank" v-t="'actions.view_ssl_score'" /> <a v-t="'actions.view_ssl_score'" :href="sslScore(instance.api_url)" target="_blank" />
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -429,26 +430,37 @@
<script> <script>
import CountryMap from "@/utils/CountryMaps/en.json"; import CountryMap from "@/utils/CountryMaps/en.json";
import ConfirmModal from "./ConfirmModal.vue";
export default { export default {
components: {
ConfirmModal,
},
data() { data() {
return { return {
mobileChapterLayout: "Vertical",
selectedInstance: null, selectedInstance: null,
authInstance: false, authInstance: false,
selectedAuthInstance: null, selectedAuthInstance: null,
instances: [], instances: [],
sponsorBlock: true, sponsorBlock: true,
skipSponsor: true, skipOptions: new Map([
skipIntro: false, ["sponsor", { value: "auto", label: "actions.skip_sponsors" }],
skipOutro: false, ["intro", { value: "no", label: "actions.skip_intro" }],
skipPreview: false, ["outro", { value: "no", label: "actions.skip_outro" }],
skipInteraction: true, ["preview", { value: "no", label: "actions.skip_preview" }],
skipSelfPromo: true, ["interaction", { value: "auto", label: "actions.skip_interaction" }],
skipMusicOffTopic: true, ["selfpromo", { value: "auto", label: "actions.skip_self_promo" }],
skipHighlight: false, ["music_offtopic", { value: "auto", label: "actions.skip_non_music" }],
skipFiller: false, ["poi_highlight", { value: "no", label: "actions.skip_highlight" }],
["filler", { value: "no", label: "actions.skip_filler_tangent" }],
]),
showMarkers: true, showMarkers: true,
minSegmentLength: 0,
dearrow: false,
selectedTheme: "dark", selectedTheme: "dark",
autoPlayVideo: true, autoPlayVideo: true,
autoDisplayCaptions: false,
autoPlayNextCountdown: 5,
listen: false, listen: false,
resolutions: [144, 240, 360, 480, 720, 1080, 1440, 2160, 4320], resolutions: [144, 240, 360, 480, 720, 1080, 1440, 2160, 4320],
defaultQuality: 0, defaultQuality: 0,
@ -461,6 +473,7 @@ export default {
minimizeRecommendations: false, minimizeRecommendations: false,
minimizeChapters: false, minimizeChapters: false,
showWatchOnYouTube: false, showWatchOnYouTube: false,
searchSuggestions: true,
watchHistory: false, watchHistory: false,
searchHistory: false, searchHistory: false,
hideWatched: false, hideWatched: false,
@ -468,6 +481,7 @@ export default {
languages: [ languages: [
{ code: "ar", name: "Arabic" }, { code: "ar", name: "Arabic" },
{ code: "az", name: "Azərbaycan" }, { code: "az", name: "Azərbaycan" },
{ code: "bg", name: "Български" },
{ code: "bn", name: "বাংলা" }, { code: "bn", name: "বাংলা" },
{ code: "bs", name: "Bosanski" }, { code: "bs", name: "Bosanski" },
{ code: "ca", name: "Català" }, { code: "ca", name: "Català" },
@ -495,6 +509,7 @@ export default {
{ code: "ml", name: "മലയാളം" }, { code: "ml", name: "മലയാളം" },
{ code: "nb_NO", name: "Norwegian Bokmål" }, { code: "nb_NO", name: "Norwegian Bokmål" },
{ code: "nl", name: "Nederlands" }, { code: "nl", name: "Nederlands" },
{ code: "oc", name: "Occitan" },
{ code: "or", name: "ଓଡ଼ିଆ" }, { code: "or", name: "ଓଡ଼ିଆ" },
{ code: "pl", name: "Polski" }, { code: "pl", name: "Polski" },
{ code: "pt", name: "Português" }, { code: "pt", name: "Português" },
@ -502,6 +517,7 @@ export default {
{ code: "pt_BR", name: "Português (Brasil)" }, { code: "pt_BR", name: "Português (Brasil)" },
{ code: "ro", name: "Română" }, { code: "ro", name: "Română" },
{ code: "ru", name: "Русский" }, { code: "ru", name: "Русский" },
{ code: "si", name: "සිංහල" },
{ code: "sr", name: "Српски" }, { code: "sr", name: "Српски" },
{ code: "sv", name: "Svenska" }, { code: "sv", name: "Svenska" },
{ code: "ta", name: "தமிழ்" }, { code: "ta", name: "தமிழ்" },
@ -516,6 +532,7 @@ export default {
disableLBRY: false, disableLBRY: false,
proxyLBRY: false, proxyLBRY: false,
password: null, password: null,
showConfirmResetPrefsDialog: false,
}; };
}, },
activated() { activated() {
@ -526,7 +543,7 @@ export default {
this.fetchJson("https://piped-instances.kavin.rocks/").then(resp => { this.fetchJson("https://piped-instances.kavin.rocks/").then(resp => {
this.instances = 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({ this.instances.push({
name: "Custom Instance", name: "Custom Instance",
api_url: this.apiUrl(), api_url: this.apiUrl(),
@ -541,57 +558,30 @@ export default {
this.selectedAuthInstance = this.getPreferenceString("auth_instance_url", this.selectedInstance); this.selectedAuthInstance = this.getPreferenceString("auth_instance_url", this.selectedInstance);
this.sponsorBlock = this.getPreferenceBoolean("sponsorblock", true); this.sponsorBlock = this.getPreferenceBoolean("sponsorblock", true);
if (localStorage.getItem("selectedSkip") !== null) { var skipOptions, skipList;
var skipList = localStorage.getItem("selectedSkip").split(","); if ((skipOptions = this.getPreferenceJSON("skipOptions")) !== undefined) {
this.skipSponsor = Object.entries(skipOptions).forEach(([key, value]) => {
this.skipIntro = var opt = this.skipOptions.get(key);
this.skipOutro = if (opt !== undefined) opt.value = value;
this.skipPreview = else console.log("Unknown sponsor type: " + key);
this.skipInteraction = });
this.skipSelfPromo = } else if ((skipList = this.getPreferenceString("selectedSkip")) !== undefined) {
this.skipMusicOffTopic = skipList = skipList.split(",");
this.skipHighlight = this.skipOptions.forEach(opt => (opt.value = "no"));
this.skipFiller =
false;
skipList.forEach(skip => { skipList.forEach(skip => {
switch (skip) { var opt = this.skipOptions.get(skip);
case "sponsor": if (opt !== undefined) opt.value = "auto";
this.skipSponsor = true; else console.log("Unknown sponsor type: " + skip);
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;
}
}); });
} }
this.showMarkers = this.getPreferenceBoolean("showMarkers", true); this.showMarkers = this.getPreferenceBoolean("showMarkers", true);
this.minSegmentLength = Math.max(this.getPreferenceNumber("minSegmentLength", 0), 0);
this.dearrow = this.getPreferenceBoolean("dearrow", false);
this.selectedTheme = this.getPreferenceString("theme", "dark"); this.selectedTheme = this.getPreferenceString("theme", "dark");
this.autoPlayVideo = this.getPreferenceBoolean("playerAutoPlay", true); this.autoPlayVideo = this.getPreferenceBoolean("playerAutoPlay", true);
this.autoDisplayCaptions = this.getPreferenceBoolean("autoDisplayCaptions", false);
this.autoPlayNextCountdown = this.getPreferenceNumber("autoPlayNextCountdown", 5);
this.listen = this.getPreferenceBoolean("listen", false); this.listen = this.getPreferenceBoolean("listen", false);
this.defaultQuality = Number(localStorage.getItem("quality")); this.defaultQuality = Number(localStorage.getItem("quality"));
this.bufferingGoal = Math.max(Number(localStorage.getItem("bufferGoal")), 10); this.bufferingGoal = Math.max(Number(localStorage.getItem("bufferGoal")), 10);
@ -602,6 +592,7 @@ export default {
this.minimizeRecommendations = this.getPreferenceBoolean("minimizeRecommendations", false); this.minimizeRecommendations = this.getPreferenceBoolean("minimizeRecommendations", false);
this.minimizeChapters = this.getPreferenceBoolean("minimizeChapters", false); this.minimizeChapters = this.getPreferenceBoolean("minimizeChapters", false);
this.showWatchOnYouTube = this.getPreferenceBoolean("showWatchOnYouTube", false); this.showWatchOnYouTube = this.getPreferenceBoolean("showWatchOnYouTube", false);
this.searchSuggestions = this.getPreferenceBoolean("searchSuggestions", true);
this.watchHistory = this.getPreferenceBoolean("watchHistory", false); this.watchHistory = this.getPreferenceBoolean("watchHistory", false);
this.searchHistory = this.getPreferenceBoolean("searchHistory", false); this.searchHistory = this.getPreferenceBoolean("searchHistory", false);
this.selectedLanguage = this.getPreferenceString("hl", await this.defaultLanguage); this.selectedLanguage = this.getPreferenceString("hl", await this.defaultLanguage);
@ -609,6 +600,7 @@ export default {
this.disableLBRY = this.getPreferenceBoolean("disableLBRY", false); this.disableLBRY = this.getPreferenceBoolean("disableLBRY", false);
this.proxyLBRY = this.getPreferenceBoolean("proxyLBRY", false); this.proxyLBRY = this.getPreferenceBoolean("proxyLBRY", false);
this.hideWatched = this.getPreferenceBoolean("hideWatched", false); this.hideWatched = this.getPreferenceBoolean("hideWatched", false);
this.mobileChapterLayout = this.getPreferenceString("mobileChapterLayout", "Vertical");
if (this.selectedLanguage != "en") { if (this.selectedLanguage != "en") {
try { try {
this.CountryMap = await import(`../utils/CountryMaps/${this.selectedLanguage}.json`).then( this.CountryMap = await import(`../utils/CountryMaps/${this.selectedLanguage}.json`).then(
@ -638,21 +630,19 @@ export default {
localStorage.setItem("auth_instance_url", this.selectedAuthInstance); localStorage.setItem("auth_instance_url", this.selectedAuthInstance);
localStorage.setItem("sponsorblock", this.sponsorBlock); localStorage.setItem("sponsorblock", this.sponsorBlock);
var sponsorSelected = []; var skipOptions = {};
if (this.skipSponsor) sponsorSelected.push("sponsor"); this.skipOptions.forEach((v, k) => (skipOptions[k] = v.value));
if (this.skipIntro) sponsorSelected.push("intro"); localStorage.setItem("skipOptions", JSON.stringify(skipOptions));
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);
localStorage.setItem("showMarkers", this.showMarkers); localStorage.setItem("showMarkers", this.showMarkers);
localStorage.setItem("minSegmentLength", this.minSegmentLength);
localStorage.setItem("dearrow", this.dearrow);
localStorage.setItem("theme", this.selectedTheme); localStorage.setItem("theme", this.selectedTheme);
localStorage.setItem("playerAutoPlay", this.autoPlayVideo); localStorage.setItem("playerAutoPlay", this.autoPlayVideo);
localStorage.setItem("autoDisplayCaptions", this.autoDisplayCaptions);
localStorage.setItem("autoPlayNextCountdown", this.autoPlayNextCountdown);
localStorage.setItem("listen", this.listen); localStorage.setItem("listen", this.listen);
localStorage.setItem("quality", this.defaultQuality); localStorage.setItem("quality", this.defaultQuality);
localStorage.setItem("bufferGoal", this.bufferingGoal); localStorage.setItem("bufferGoal", this.bufferingGoal);
@ -663,6 +653,7 @@ export default {
localStorage.setItem("minimizeRecommendations", this.minimizeRecommendations); localStorage.setItem("minimizeRecommendations", this.minimizeRecommendations);
localStorage.setItem("minimizeChapters", this.minimizeChapters); localStorage.setItem("minimizeChapters", this.minimizeChapters);
localStorage.setItem("showWatchOnYouTube", this.showWatchOnYouTube); localStorage.setItem("showWatchOnYouTube", this.showWatchOnYouTube);
localStorage.setItem("searchSuggestions", this.searchSuggestions);
localStorage.setItem("watchHistory", this.watchHistory); localStorage.setItem("watchHistory", this.watchHistory);
localStorage.setItem("searchHistory", this.searchHistory); localStorage.setItem("searchHistory", this.searchHistory);
if (!this.searchHistory) localStorage.removeItem("search_history"); if (!this.searchHistory) localStorage.removeItem("search_history");
@ -671,6 +662,7 @@ export default {
localStorage.setItem("disableLBRY", this.disableLBRY); localStorage.setItem("disableLBRY", this.disableLBRY);
localStorage.setItem("proxyLBRY", this.proxyLBRY); localStorage.setItem("proxyLBRY", this.proxyLBRY);
localStorage.setItem("hideWatched", this.hideWatched); localStorage.setItem("hideWatched", this.hideWatched);
localStorage.setItem("mobileChapterLayout", this.mobileChapterLayout);
if (shouldReload) window.location.reload(); if (shouldReload) window.location.reload();
} }
@ -700,7 +692,7 @@ export default {
window.location = "/"; window.location = "/";
}, },
resetPreferences() { resetPreferences() {
if (!confirm(this.$t("actions.confirm_reset_preferences"))) return; this.showConfirmResetPrefsDialog = false;
// clear the local storage // clear the local storage
localStorage.clear(); localStorage.clear();
// redirect to the home page // redirect to the home page
@ -739,5 +731,37 @@ export default {
<style> <style>
.pref { .pref {
@apply flex justify-between items-center; @apply flex justify-between items-center;
padding: 10rem;
border-top: var(--efy_border);
gap: 10rem;
}
/*.pref:nth-child(odd) {
background: var(--efy_bg1);
}*/
.pref :is(input, select, button, [role="button"]) {
margin: 0;
}
.pref strong {
line-height: 1;
}
.pref :is([type="number"], [type="text"], select) {
min-width: 60rem;
max-width: 250rem;
}
.pp-pref-cards {
margin-top: 15rem;
}
[efy_card*="grid"] {
padding: 0;
gap: 0;
}
[efy_card*="grid"]:active {
transform: scale(1) !important;
}
[efy_card*="grid"] h5 {
padding: 5rem 10rem;
}
tbody:nth-child(odd) {
background: var(--efy_bg1) !important;
} }
</style> </style>

30
src/components/QrCode.vue Normal file
View File

@ -0,0 +1,30 @@
<template>
<canvas ref="qrCodeCanvas" style="border-radius: var(--efy_radius)" />
</template>
<script>
import QRCode from "qrcode";
export default {
props: {
text: {
type: String,
required: true,
},
},
watch: {
text() {
this.generateQrCode();
},
},
mounted() {
this.generateQrCode();
},
methods: {
generateQrCode() {
QRCode.toCanvas(this.$refs.qrCodeCanvas, this.text, error => {
if (error) console.error(error);
});
},
},
};
</script>

View File

@ -0,0 +1,114 @@
<template>
<h1 v-t="'titles.register'" class="my-4 text-center font-bold" />
<hr />
<div class="flex justify-center text-center">
<form class="items-center px-3 children:pb-3">
<div>
<input
v-model="username"
class="input w-full"
type="text"
autocomplete="username"
:placeholder="$t('login.username')"
:aria-label="$t('login.username')"
@keyup.enter="register"
/>
</div>
<div class="flex justify-center">
<input
v-model="password"
class="input w-full"
:type="showPassword ? 'text' : 'password'"
autocomplete="password"
:placeholder="$t('login.password')"
:aria-label="$t('login.password')"
@keyup.enter="register"
/>
<button type="button" class="btn ml-2" @click="showPassword = !showPassword">
<div class="i-fa6-solid:eye" />
</button>
</div>
<div class="flex justify-center">
<input
v-model="passwordConfirm"
class="input w-full"
:type="showConfirmPassword ? 'text' : 'password'"
autocomplete="password"
:placeholder="$t('login.password_confirm')"
:aria-label="$t('login.password_confirm')"
@keyup.enter="register"
/>
<button type="button" class="btn ml-2" @click="showConfirmPassword = !showConfirmPassword">
<div class="i-fa6-solid:eye" />
</button>
</div>
<div>
<a v-t="'titles.register'" class="btn w-auto" @click="register" />
</div>
</form>
</div>
<ConfirmModal
v-if="showUnsecureRegisterDialog"
:message="$t('info.register_no_email_note')"
@close="showUnsecureRegisterDialog = false"
@confirm="
forceUnsecureRegister = true;
showUnsecureRegisterDialog = false;
register();
"
/>
</template>
<script>
import { isEmail } from "../utils/Misc.js";
import ConfirmModal from "./ConfirmModal.vue";
export default {
components: { ConfirmModal },
data() {
return {
username: null,
password: null,
passwordConfirm: null,
showPassword: false,
showConfirmPassword: false,
showUnsecureRegisterDialog: false,
forceUnsecureRegister: false,
};
},
mounted() {
//TODO: Add Server Side check
if (this.getAuthToken()) {
this.$router.push("/");
}
},
activated() {
document.title = "Register - Piped";
},
methods: {
register() {
if (!this.username || !this.password) return;
if (this.password != this.passwordConfirm) {
alert(this.$t("login.passwords_incorrect"));
return;
}
if (isEmail(this.username) && !this.forceUnsecureRegister) {
this.showUnsecureRegisterDialog = true;
return;
}
this.fetchJson(this.authApiUrl() + "/register", null, {
method: "POST",
body: JSON.stringify({
username: this.username,
password: this.password,
}),
}).then(resp => {
if (resp.token) {
this.setPreference("authToken" + this.hashCode(this.authApiUrl()), resp.token);
window.location = "/"; // done to bypass cache
} else alert(resp.error);
});
},
},
};
</script>

View File

@ -1,13 +1,20 @@
<template> <template>
<h1 class="text-center my-2" v-text="$route.query.search_query" /> <hr />
<div class="flex flex-wrap place-content-between items-center">
<label for="ddlSearchFilters" class="mr-2"> <h5 class="ml-[5rem]" v-text="$route.query.search_query" />
<strong v-text="`${$t('actions.filter')}:`" /> <div class="flex items-center" style="gap: var(--efy_gap0)">
</label> <label v-text="`${$t('actions.filter')}:`" for="ddlSearchFilters" />
<select id="ddlSearchFilters" v-model="selectedFilter" default="all" class="select w-auto" @change="updateFilter()"> <select
<option v-for="filter in availableFilters" :key="filter" :value="filter" v-t="`search.${filter}`" /> id="ddlSearchFilters"
</select> v-model="selectedFilter"
default="all"
class="w-auto; m-0"
@change="updateFilter()"
>
<option v-for="filter in availableFilters" :key="filter" v-t="`search.${filter}`" :value="filter" />
</select>
</div>
</div>
<hr /> <hr />
<div v-if="results && results.corrected"> <div v-if="results && results.corrected">
@ -18,19 +25,21 @@
</i18n-t> </i18n-t>
</div> </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"> <template v-for="result in results.items" :key="result.url">
<ContentItem :item="result" height="94" width="168" /> <ContentItem :item="result" height="94" width="168" />
</template> </template>
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
import ContentItem from "./ContentItem.vue"; import ContentItem from "./ContentItem.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
export default { export default {
components: { components: {
ContentItem, ContentItem,
LoadingIndicatorPage,
}, },
data() { data() {
return { return {
@ -44,6 +53,7 @@ export default {
"music_videos", "music_videos",
"music_albums", "music_albums",
"music_playlists", "music_playlists",
"music_artists",
], ],
selectedFilter: this.$route.query.filter ?? "all", selectedFilter: this.$route.query.filter ?? "all",
}; };
@ -72,7 +82,10 @@ export default {
}, },
async updateResults() { async updateResults() {
document.title = this.$route.query.search_query + " - Piped"; 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() { updateFilter() {
this.$router.replace({ this.$router.replace({

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="absolute suggestions-container"> <div class="suggestions-container absolute">
<ul> <ul>
<li <li
v-for="(suggestion, i) in searchSuggestions" v-for="(suggestion, i) in searchSuggestions"
@ -50,12 +50,16 @@ export default {
if (!this.searchText) { if (!this.searchText) {
if (this.getPreferenceBoolean("searchHistory", false)) if (this.getPreferenceBoolean("searchHistory", false))
this.searchSuggestions = JSON.parse(localStorage.getItem("search_history")) ?? []; this.searchSuggestions = JSON.parse(localStorage.getItem("search_history")) ?? [];
} else if (this.getPreferenceBoolean("searchSuggestions", true)) {
this.searchSuggestions =
(
await this.fetchJson(this.apiUrl() + "/opensearch/suggestions", {
query: this.searchText,
})
)?.[1] ?? [];
} else { } else {
this.searchSuggestions = ( this.searchSuggestions = [];
await this.fetchJson(this.apiUrl() + "/opensearch/suggestions", { return;
query: this.searchText,
})
)?.[1];
} }
this.searchSuggestions.unshift(this.searchText); this.searchSuggestions.unshift(this.searchText);
this.setSelected(0); this.setSelected(0);
@ -86,6 +90,6 @@ export default {
background: var(--efy_text2); background: var(--efy_text2);
box-shadow: 0 0 20rem var(--efy_text_trans); box-shadow: 0 0 20rem var(--efy_text_trans);
padding: var(--efy_gap); padding: var(--efy_gap);
margin: calc(-12rem + var(--efy_gap)) 0 var(--efy_gap) 0; margin: calc(40rem + var(--efy_gap)) 0 var(--efy_gap) 0;
} }
</style> </style>

View File

@ -1,36 +1,47 @@
<template> <template>
<ModalComponent> <ModalComponent>
<h4 v-t="'actions.share'" /> <h5 v-t="'actions.share'" />
<div class="flex justify-between mt-2 mb-2"> <div class="flex justify-between mt-2 mb-2">
<label v-t="'actions.piped_link'" /> <label v-t="'actions.piped_link'" />
<input type="checkbox" v-model="pipedLink" @change="onChange" /> <input v-model="pipedLink" type="checkbox" @change="onChange" />
</div> </div>
<div v-if="this.hasPlaylist" class="flex justify-between"> <div v-if="hasPlaylist" class="flex justify-between">
<label v-t="'actions.with_playlist'" /> <label v-t="'actions.with_playlist'" />
<input type="checkbox" v-model="withPlaylist" @change="onChange" /> <input v-model="withPlaylist" type="checkbox" @change="onChange" />
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<label v-t="'actions.with_timecode'" for="withTimeCode" /> <label v-t="'actions.with_timecode'" for="withTimeCode" />
<input id="withTimeCode" type="checkbox" v-model="withTimeCode" @change="onChange" /> <input id="withTimeCode" v-model="withTimeCode" type="checkbox" @change="onChange" />
</div> </div>
<div v-if="this.withTimeCode" class="flex justify-between mt-2" style="align-items: center"> <div v-if="withTimeCode" class="flex justify-between mt-2" style="align-items: center">
<label v-t="'actions.time_code'" /> <label v-t="'actions.time_code'" />
<input class="input w-300 mb-0rem" type="text" v-model="timeStamp" /> <input v-model="timeStamp" style="max-width: 100rem" type="number" @change="onChange" />
</div> </div>
<a :href="generatedLink" target="_blank"> <a :href="generatedLink" target="_blank">
<h6 class="mb-2 mt-2" v-text="generatedLink" /> <h6 class="mb-2 mt-2" v-text="generatedLink" />
</a> </a>
<div class="flex justify-end mt-4"> <QrCode v-if="showQrCode" :text="generatedLink" class="mb-[10rem]" />
<button class="btn" style="margin-right: 15rem" v-t="'actions.follow_link'" @click="followLink()" /> <div class="flex flex-wrap justify-end" style="gap: var(--efy_gap0)">
<button class="btn" v-t="'actions.copy_link'" @click="copyLink()" /> <button v-t="'actions.generate_qrcode'" class="btn" @click="showQrCode = !showQrCode" />
<button v-t="'actions.follow_link'" class="btn ml-3" @click="followLink()" />
<button v-t="'actions.copy_link'" class="btn ml-3" @click="copyLink()" />
</div> </div>
</ModalComponent> </ModalComponent>
</template> </template>
<script setup>
import { defineAsyncComponent } from "vue";
const QrCode = defineAsyncComponent(() => import("./QrCode.vue"));
</script>
<script> <script>
import ModalComponent from "./ModalComponent.vue"; import ModalComponent from "./ModalComponent.vue";
export default { export default {
components: {
ModalComponent,
},
props: { props: {
videoId: { videoId: {
type: String, type: String,
@ -42,14 +53,13 @@ export default {
}, },
playlistId: { playlistId: {
type: String, type: String,
default: undefined,
}, },
playlistIndex: { playlistIndex: {
type: Number, type: Number,
default: undefined,
}, },
}, },
components: {
ModalComponent,
},
data() { data() {
return { return {
withTimeCode: true, withTimeCode: true,
@ -57,8 +67,23 @@ export default {
withPlaylist: true, withPlaylist: true,
timeStamp: null, timeStamp: null,
hasPlaylist: false, hasPlaylist: false,
showQrCode: false,
}; };
}, },
computed: {
generatedLink() {
var baseUrl = this.pipedLink
? window.location.origin + "/watch?v=" + this.videoId
: "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;
},
},
mounted() { mounted() {
this.timeStamp = parseInt(this.currentTime); this.timeStamp = parseInt(this.currentTime);
this.withTimeCode = this.getPreferenceBoolean("shareWithTimeCode", true); this.withTimeCode = this.getPreferenceBoolean("shareWithTimeCode", true);
@ -87,19 +112,5 @@ export default {
this.setPreference("shareWithPlaylist", this.withPlaylist, true); this.setPreference("shareWithPlaylist", this.withPlaylist, true);
}, },
}, },
computed: {
generatedLink() {
var baseUrl = this.pipedLink
? window.location.origin + "/watch?v=" + this.videoId
: "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;
},
},
}; };
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<label for="ddlSortBy" v-t="'actions.sort_by'" class="mr-2" /> <label v-t="'actions.sort_by'" for="ddlSortBy" class="m-0" />
<select id="ddlSortBy" v-model="selectedSort" class="select w-auto m-0"> <select id="ddlSortBy" v-model="selectedSort" class="w-auto m-0">
<option v-for="(value, key) in options" v-t="`actions.${key}`" :key="key" :value="value" /> <option v-for="(value, key) in options" :key="key" v-t="`actions.${key}`" :value="value" />
</select> </select>
</template> </template>
@ -18,7 +18,10 @@ const options = {
const selectedSort = ref("descending"); const selectedSort = ref("descending");
const props = defineProps({ const props = defineProps({
byKey: String, byKey: {
type: String,
required: true,
},
}); });
const emit = defineEmits(["apply"]); const emit = defineEmits(["apply"]);

View File

@ -1,29 +1,100 @@
<template> <template>
<div class="flex justify-between w-full"> <hr />
<div class="flex"> <!-- import / export section -->
<button class="btn mr-2"> <div class="flex justify-between flex-wrap m0c">
<router-link to="/import" v-t="'actions.import_from_json'" /> <div efy_card class="w-auto!" style="padding: var(--efy_padding)">
</button> <i18n-t keypath="titles.subscriptions" efy_card />{{ ": " + subscriptions.length }}
<button class="btn" @click="exportHandler" v-t="'actions.export_to_json'" />
</div> </div>
<i18n-t keypath="subscriptions.subscribed_channels_count">{{ subscriptions.length }}</i18n-t> <div class="m0c flex flex-wrap">
<router-link v-t="'actions.import_from_json_csv'" to="/import" role="button" />
<button v-t="'actions.export_to_json'" @click="exportHandler" />
<input
id="fileSelector"
ref="fileSelector"
type="file"
class="display-none"
multiple="multiple"
@change="importGroupsHandler"
/>
<label
for="fileSelector"
role="button"
v-text="`${$t('actions.import_from_json')} (${$t('titles.channel_groups')})`"
class="font-bold"
/>
<button
@click="exportGroupsHandler"
v-text="`${$t('actions.export_to_json')} (${$t('titles.channel_groups')})`"
/>
</div>
</div>
<hr />
<div class="m0c w-full flex flex-wrap">
<button
v-for="group in channelGroups"
:key="group.groupName"
class="flex gap-[10rem] items-center"
:class="{ selected: selectedGroup === group }"
@click="selectGroup(group)"
>
<span v-text="group.groupName !== '' ? group.groupName : $t('video.all')" />
<div v-if="group.groupName != '' && selectedGroup == group" class="flex flex-wrap gap-[10rem] items-center">
<div>|</div>
<font-awesome-icon class="mx-2" icon="edit" @click="showEditGroupModal = true" />
<div>|</div>
<font-awesome-icon class="mx-2" icon="circle-minus" @click="deleteGroup(group)" />
</div>
</button>
<button class="btn mx-1">
<font-awesome-icon icon="circle-plus" @click="showCreateGroupModal = true" />
</button>
</div> </div>
<hr /> <hr />
<!-- Subscriptions card list --> <!-- Subscriptions card list -->
<div class="pp-subs-cards"> <div class="pp-subs-cards">
<!-- channel info card --> <!-- channel info card -->
<div efy_card class="pp-subs-card" v-for="subscription in subscriptions" :key="subscription.url"> <div v-for="subscription in filteredSubscriptions" :key="subscription.url" efy_card class="pp-subs-card">
<router-link :to="subscription.url" class="pp-import-channel flex font-bold"> <router-link :to="subscription.url" class="pp-import-channel flex font-bold">
<img :src="subscription.avatar" width="48" height="48" /> <img :src="subscription.avatar" width="48" height="48" />
<span class="mx-2" v-text="subscription.name" /> <span class="mx-2" v-text="subscription.name" />
</router-link> </router-link>
<!-- (un)subscribe btn --> <!-- (un)subscribe btn -->
<button <button
@click="handleButton(subscription)"
v-t="`actions.${subscription.subscribed ? 'unsubscribe' : 'subscribe'}`" v-t="`actions.${subscription.subscribed ? 'unsubscribe' : 'subscribe'}`"
class="btn mt-2 w-full"
@click="handleButton(subscription)"
/> />
</div> </div>
</div> </div>
<ModalComponent v-if="showCreateGroupModal" @close="showCreateGroupModal = !showCreateGroupModal">
<h2 v-t="'actions.create_group'" />
<div class="flex flex-col">
<input v-model="newGroupName" class="input my-4" type="text" :placeholder="$t('actions.group_name')" />
<button v-t="'actions.create_group'" class="btn ml-auto w-max" @click="createGroup()" />
</div>
</ModalComponent>
<ModalComponent v-if="showEditGroupModal" @close="showEditGroupModal = false">
<div class="mb-5 mt-3 flex justify-between">
<input v-model="editedGroupName" type="text" class="input" />
<button v-t="'actions.okay'" class="btn" :placeholder="$t('actions.group_name')" @click="editGroupName()" />
</div>
<div class="mb-2 mt-3 flex flex-col overflow-y-scroll" style="height: 300rem">
<div v-for="subscription in subscriptions" :key="subscription.name">
<div class="mr-3 flex justify-between">
<span>{{ subscription.name }}</span>
<input
type="checkbox"
class="checkbox"
:checked="selectedGroup.channels.includes(subscription.url.substr(-11))"
@change="checkedChange(subscription)"
/>
</div>
<hr />
</div>
</div>
</ModalComponent>
</template> </template>
<style> <style>
@ -34,25 +105,71 @@
} }
.pp-subs-card :is(a, span) { .pp-subs-card :is(a, span) {
-webkit-text-fill-color: var(--efy_text) !important; -webkit-text-fill-color: var(--efy_text) !important;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
.pp-subs-card button { .pp-subs-card button {
margin-bottom: 0; margin-bottom: 0;
width: 100%; width: 100%;
} }
.selected {
}
.m0c {
gap: var(--efy_gap0);
}
.m0c :is(button, [role="button"]) {
margin: 0;
}
</style> </style>
<script> <script>
import ModalComponent from "./ModalComponent.vue";
export default { export default {
components: { ModalComponent },
data() { data() {
return { return {
subscriptions: [], subscriptions: [],
selectedGroup: {
groupName: "",
channels: [],
},
channelGroups: [],
showCreateGroupModal: false,
showEditGroupModal: false,
newGroupName: "",
editedGroupName: "",
}; };
}, },
computed: {
filteredSubscriptions(_this) {
return _this.selectedGroup.groupName == ""
? _this.subscriptions
: _this.subscriptions.filter(channel => _this.selectedGroup.channels.includes(channel.url.substr(-11)));
},
},
mounted() { mounted() {
this.fetchSubscriptions().then(json => { this.fetchSubscriptions().then(json => {
this.subscriptions = json; this.subscriptions = json;
this.subscriptions.forEach(subscription => (subscription.subscribed = true)); this.subscriptions.forEach(subscription => (subscription.subscribed = true));
}); });
this.channelGroups.push(this.selectedGroup);
if (!window.db) return;
const cursor = this.getChannelGroupsCursor();
cursor.onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
const group = cursor.value;
this.channelGroups.push({
groupName: group.groupName,
channels: JSON.parse(group.channels),
});
cursor.continue();
}
};
}, },
activated() { activated() {
document.title = "Subscriptions - Piped"; document.title = "Subscriptions - Piped";
@ -91,7 +208,6 @@ export default {
}, },
exportHandler() { exportHandler() {
const subscriptions = []; const subscriptions = [];
this.subscriptions.forEach(subscription => { this.subscriptions.forEach(subscription => {
subscriptions.push({ subscriptions.push({
url: "https://www.youtube.com" + subscription.url, url: "https://www.youtube.com" + subscription.url,
@ -99,15 +215,76 @@ export default {
service_id: 0, service_id: 0,
}); });
}); });
const json = JSON.stringify({ const json = JSON.stringify({
app_version: "", app_version: "",
app_version_int: 0, app_version_int: 0,
subscriptions: subscriptions, subscriptions: subscriptions,
}); });
this.download(json, "subscriptions.json", "application/json"); this.download(json, "subscriptions.json", "application/json");
}, },
selectGroup(group) {
this.selectedGroup = group;
this.editedGroupName = group.groupName;
},
createGroup() {
if (!this.newGroupName || this.channelGroups.some(group => group.groupName == this.newGroupName)) return;
const newGroup = {
groupName: this.newGroupName,
channels: [],
};
this.channelGroups.push(newGroup);
this.createOrUpdateChannelGroup(newGroup);
this.newGroupName = "";
this.showCreateGroupModal = false;
},
editGroupName() {
const oldGroupName = this.selectedGroup.groupName;
const newGroupName = this.editedGroupName;
// the group mustn't yet exist and the name can't be empty
if (!newGroupName || newGroupName == oldGroupName) return;
if (this.channelGroups.some(group => group.groupName == newGroupName)) return;
// create a new group with the same info and delete the old one
this.selectedGroup.groupName = newGroupName;
this.createOrUpdateChannelGroup(this.selectedGroup);
this.deleteChannelGroup(oldGroupName);
this.showEditGroupModal = false;
},
deleteGroup(group) {
this.deleteChannelGroup(group.groupName);
this.channelGroups = this.channelGroups.filter(g => g != group);
this.selectedGroup = this.channelGroups[0];
},
checkedChange(subscription) {
const channelId = subscription.url.substr(-11);
this.selectedGroup.channels = this.selectedGroup.channels.includes(channelId)
? this.selectedGroup.channels.filter(channel => channel != channelId)
: this.selectedGroup.channels.concat(channelId);
this.createOrUpdateChannelGroup(this.selectedGroup);
},
async importGroupsHandler() {
const files = this.$refs.fileSelector.files;
for (let file of files) {
const groups = JSON.parse(await file.text()).groups;
for (let group of groups) {
this.createOrUpdateChannelGroup(group);
this.channelGroups.push(group);
}
}
},
exportGroupsHandler() {
const json = {
format: "Piped",
version: 1,
groups: this.channelGroups.slice(1),
};
this.download(JSON.stringify(json), "channel_groups.json", "application/json");
},
}, },
}; };
</script> </script>

View File

@ -0,0 +1,28 @@
<template>
<div class="toast">
<slot />
<button v-t="'actions.dismiss'" @click="dismiss" class="m-0 mt-[10rem]" />
</div>
</template>
<script>
export default {
emits: ["dismissed"],
methods: {
dismiss() {
this.$emit("dismissed");
},
},
};
</script>
<style>
.toast {
@apply flex flex-col justify-center fixed top-[15rem] right-[15rem] min-w-max z-9999;
background: var(--efy_text2);
color: var(--efy_text);
padding: 15rem;
border-radius: var(--efy_radius);
border: var(--efy_border);
}
</style>

View File

@ -1,15 +1,17 @@
<template> <template>
<div class="video-grid"> <LoadingIndicatorPage :show-content="videos.length != 0" class="video-grid mt-[15rem]">
<VideoItem v-for="video in videos" :key="video.url" :item="video" height="118" width="210" /> <VideoItem v-for="video in videos" :key="video.url" :item="video" height="118" width="210" />
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import VideoItem from "./VideoItem.vue"; import VideoItem from "./VideoItem.vue";
export default { export default {
components: { components: {
VideoItem, VideoItem,
LoadingIndicatorPage,
}, },
data() { data() {
return { return {
@ -23,21 +25,15 @@ export default {
this.fetchTrending(region).then(videos => { this.fetchTrending(region).then(videos => {
this.videos = videos; this.videos = videos;
this.updateWatched(this.videos); this.updateWatched(this.videos);
this.fetchDeArrowContent(this.videos);
}); });
}, },
activated() { activated() {
document.title = this.$t("titles.trending") + " - Piped"; document.title = this.$t("titles.trending") + " - Piped";
if (this.videos.length > 0) this.updateWatched(this.videos); if (this.videos.length > 0) this.updateWatched(this.videos);
if (this.$route.path == "/") { if (this.$route.path == "/") {
switch (this.getPreferenceString("homepage", "trending")) { let homepage = this.getHomePage(this);
case "trending": if (homepage !== undefined) this.$router.push(homepage);
break;
case "feed":
this.$router.push("/feed");
return;
default:
break;
}
} }
}, },
methods: { methods: {

View File

@ -1,6 +1,8 @@
<template> <template>
<div v-if="showVideo" class="efy_trans_filter"> <div v-if="showVideo" class="video-card efy_trans_filter efy_shadow_trans">
<!-- EFY-->
<router-link <router-link
class="video_item_link"
:to="{ :to="{
path: '/watch', path: '/watch',
query: { query: {
@ -9,26 +11,61 @@
...(index >= 0 && { index: index + 1 }), ...(index >= 0 && { index: index + 1 }),
}, },
}" }"
class="video_item_link"
> >
<img <img
class="w-full" :src="thumbnail"
:src="item.thumbnail" :alt="title"
:alt="item.title"
:class="{ 'shorts-img': item.isShort }" :class="{ 'shorts-img': item.isShort }"
class="thumbnail"
loading="lazy" loading="lazy"
/> />
<p class="pp-video-card-title my-2 overflow-hidden flex link" :title="item.title" v-text="item.title" /> <!-- progress bar -->
<div
v-if="item.watched && item.duration > 0"
class="relative h-1 w-full"
style="
height: 4rem;
background: rgba(255, 255, 255, 0.067);
bottom: 8px;
border-radius: 0px;
box-shadow: 0 -5rem 10rem #0005;
backdrop-filter: blur(24px);
"
>
<div
class="absolute bottom-0 left-0"
:style="{ width: `clamp(0%, ${(item.currentTime / item.duration) * 100}%, 100%` }"
style="
height: 4rem;
background: var(--efy_color);
border-radius: 0 var(--efy_radius0) var(--efy_radius0) 0;
box-shadow: 3rem 0 5rem #0005;
"
/>
</div>
<div class="flex items-center h-[44rem] overflow-hidden">
<p v-text="title" class="pp-video-card-title" />
</div>
</router-link> </router-link>
<div class="pp-video-card-buttons"> <div class="pp-video-card-buttons">
<button v-if="item.duration > 0" v-text="timeFormat(item.duration)" tabindex="-1" /> <button
<button v-if="item.views >= 0" tabindex="-1"> v-if="item.duration > 0"
<font-awesome-icon icon="eye" /> v-text="timeFormat(item.duration)"
<span class="pl-0.5" v-text="`${numberFormat(item.views)}`" /> class="efy_shadow_trans efy_shadow_button_off efy_button_text_off"
tabindex="-1"
/>
<button
v-if="item.views >= 0"
class="efy_shadow_trans efy_shadow_button_off efy_button_text_off"
tabindex="-1"
>
<font-awesome-icon icon="eye" style="margin-right: 5rem" />
<span v-text="`${numberFormat(item.views)}`" />
</button> </button>
<router-link <router-link
class="btn" class="btn efy_shadow_trans efy_shadow_button_off efy_button_text_off"
role="button"
:to="{ :to="{
path: '/watch', path: '/watch',
query: { query: {
@ -43,45 +80,65 @@
> >
<font-awesome-icon icon="headphones" /> <font-awesome-icon icon="headphones" />
</router-link> </router-link>
<button v-if="authenticated" :title="$t('actions.add_to_playlist')" @click="showModal = !showModal"> <button
:title="$t('actions.add_to_playlist')"
@click="showModal = !showModal"
class="btn efy_shadow_trans efy_shadow_button_off efy_button_text_off"
>
<font-awesome-icon icon="circle-plus" /> <font-awesome-icon icon="circle-plus" />
</button> </button>
<button <button
v-if="admin" v-if="admin"
:title="$t('actions.remove_from_playlist')"
ref="removeButton" ref="removeButton"
@click="removeVideo(item.url.substr(-11))" :title="$t('actions.remove_from_playlist')"
class="efy_shadow_trans efy_shadow_button_off efy_button_text_off"
@click="showConfirmRemove = true"
> >
<font-awesome-icon icon="circle-minus" /> <font-awesome-icon icon="circle-minus" />
</button> </button>
<button v-if="item.uploadedDate" class="pl-0.5" v-text="item.uploadedDate" tabindex="-1" /> <ConfirmModal
v-if="showConfirmRemove"
:message="$t('actions.delete_playlist_video_confirm')"
@close="showConfirmRemove = false"
@confirm="removeVideo(item.url.substr(-11))"
/>
<PlaylistAddModal
v-if="showModal"
:video-id="item.url.substr(-11)"
:video-info="item"
@close="showModal = !showModal"
/>
<button
v-if="item.uploaded > 0"
class="efy_shadow_trans efy_shadow_button_off efy_button_text_off"
v-text="timeAgo(item.uploaded)"
/>
<button v-else-if="item.uploadedDate" v-text="item.uploadedDate" tabindex="-1" />
<button class="pp-color" v-if="item.isShort" v-t="'video.shorts'" tabindex="-1" /> <button class="pp-color" v-if="item.isShort" v-t="'video.shorts'" tabindex="-1" />
<button v-else-if="item.duration < 0" v-t="'video.live'" class="pp-color" tabindex="-1" /> <button v-else-if="item.duration < 0" v-t="'video.live'" class="pp-color" tabindex="-1" />
<button v-if="item.watched" v-t="'video.watched'" class="pp-color" tabindex="-1" /> <button v-if="item.watched" v-t="'video.watched'" class="pp-color" tabindex="-1" />
</div> </div>
<router-link <router-link
:to="item.uploaderUrl"
class="pp-video-card-channel"
v-if="item.uploaderUrl && item.uploaderName && !hideChannel" v-if="item.uploaderUrl && item.uploaderName && !hideChannel"
:to="item.uploaderUrl"
:title="item.uploaderName"
class="pp-video-card-channel"
> >
<img <img
v-if="item.uploaderAvatar" v-if="item.uploaderAvatar"
:src="item.uploaderAvatar" :src="item.uploaderAvatar"
loading="lazy" loading="lazy"
:alt="item.uploaderName" class="mt-0.5 w-36rem h-36rem efy_shadow_trans efy_shadow_button_off"
class="mt-0.5 w-36rem h-36rem"
width="36" width="36"
height="36" height="36"
/> />
<div class="pp-text efy_shadow_trans efy_shadow_button_off">
<div class="pp-text" title="item.uploaderName">
<span v-text="item.uploaderName" /> <span v-text="item.uploaderName" />
<font-awesome-icon class="ml-1.5" v-if="item.uploaderVerified" icon="check" /> <font-awesome-icon class="ml-1.5" v-if="item.uploaderVerified" icon="check" />
</div> </div>
</router-link> </router-link>
</div> </div>
<PlaylistAddModal v-if="showModal" :video-id="video.url.substr(-11)" @close="showModal = !showModal" />
</template> </template>
<style> <style>
@ -95,8 +152,10 @@
<script> <script>
import PlaylistAddModal from "./PlaylistAddModal.vue"; import PlaylistAddModal from "./PlaylistAddModal.vue";
import ConfirmModal from "./ConfirmModal.vue";
export default { export default {
components: { PlaylistAddModal, ConfirmModal },
props: { props: {
item: { item: {
type: Object, type: Object,
@ -115,34 +174,32 @@ export default {
playlistId: { type: String, default: null }, playlistId: { type: String, default: null },
admin: { type: Boolean, default: false }, admin: { type: Boolean, default: false },
}, },
emits: ["remove"],
data() { data() {
return { return {
showModal: false, showModal: false,
showVideo: true, showVideo: true,
showConfirmRemove: false,
}; };
}, },
computed: {
title() {
return this.item.dearrow?.titles[0]?.title ?? this.item.title;
},
thumbnail() {
return this.item.dearrow?.thumbnails[0]?.thumbnail ?? this.item.thumbnail;
},
},
mounted() { mounted() {
this.shouldShowVideo(); this.shouldShowVideo();
}, },
methods: { methods: {
removeVideo() { removeVideo() {
if (confirm(this.$t("actions.delete_playlist_video_confirm"))) { this.$refs.removeButton.disabled = true;
this.$refs.removeButton.disabled = true; this.removeVideoFromPlaylist(this.playlistId, this.index).then(json => {
this.fetchJson(this.authApiUrl() + "/user/playlists/remove", null, { if (json.error) alert(json.error);
method: "POST", else this.$emit("remove");
body: JSON.stringify({ });
playlistId: this.playlistId,
index: this.index,
}),
headers: {
Authorization: this.getAuthToken(),
"Content-Type": "application/json",
},
}).then(json => {
if (json.error) alert(json.error);
else this.$emit("remove");
});
}
}, },
shouldShowVideo() { shouldShowVideo() {
if (!this.isFeed || !this.getPreferenceBoolean("hideWatched", false)) return; if (!this.isFeed || !this.getPreferenceBoolean("hideWatched", false)) return;
@ -158,6 +215,5 @@ export default {
}; };
}, },
}, },
components: { PlaylistAddModal },
}; };
</script> </script>

View File

@ -2,15 +2,49 @@
<div <div
ref="container" ref="container"
data-shaka-player-container data-shaka-player-container
class="w-full max-h-screen flex justify-center efy_trans_filter_off" class="relative max-h-screen w-full flex justify-center efy_trans_filter_off"
:class="{ 'player-container': !isEmbed }" :class="{ 'player-container': !isEmbed }"
> >
<video ref="videoEl" class="w-full" data-shaka-player :autoplay="shouldAutoPlay" :loop="selectedAutoLoop" /> <video ref="videoEl" class="w-full" data-shaka-player :autoplay="shouldAutoPlay" :loop="selectedAutoLoop" />
<span
id="preview-container"
ref="previewContainer"
class="absolute bottom-0 z-[2000] mb-[3.5%] hidden flex-col items-center"
>
<canvas id="preview" ref="preview" style="border-radius: var(--efy_radius0)" />
<span
class="w-min"
style="
border-radius: var(--efy_radius0);
background: var(--efy_text2);
color: var(--efy_text);
padding: 3rem 6rem;
"
v-text="timeFormat(currentTime)"
/>
</span>
<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>
<span
v-if="error > 0"
v-t="{ path: 'player.failed', args: [error] }"
class="absolute top-8 rounded bg-black/80 p-2 text-lg backdrop-blur-sm"
/>
</div> </div>
</template> </template>
<script> <script>
import("shaka-player/dist/controls.css"); import "shaka-player/dist/controls.css";
import { parseTimeParam } from "@/utils/Misc";
const shaka = import("shaka-player/dist/shaka-player.ui.js"); const shaka = import("shaka-player/dist/shaka-player.ui.js");
if (!window.muxjs) { if (!window.muxjs) {
import("mux.js").then(muxjs => { import("mux.js").then(muxjs => {
@ -27,14 +61,6 @@ export default {
return {}; return {};
}, },
}, },
playlist: {
type: Object,
default: null,
},
index: {
type: Number,
default: -1,
},
sponsors: { sponsors: {
type: Object, type: Object,
default: () => { default: () => {
@ -45,12 +71,17 @@ export default {
selectedAutoLoop: Boolean, selectedAutoLoop: Boolean,
isEmbed: Boolean, isEmbed: Boolean,
}, },
emits: ["timeupdate"], emits: ["timeupdate", "ended", "navigateNext"],
data() { data() {
return { return {
lastUpdate: new Date().getTime(), lastUpdate: new Date().getTime(),
initialSeekComplete: false, initialSeekComplete: false,
destroying: false, destroying: false,
inSegment: false,
isHoveringTimebar: false,
currentTime: 0,
seekbarPadding: 2,
error: 0,
}; };
}, },
computed: { computed: {
@ -88,7 +119,7 @@ export default {
this.hotkeysPromise.then(() => { this.hotkeysPromise.then(() => {
var self = this; var self = this;
this.$hotkeys( 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) { function (e, handler) {
const videoEl = self.$refs.videoEl; const videoEl = self.$refs.videoEl;
switch (handler.key) { switch (handler.key) {
@ -175,7 +206,7 @@ export default {
e.preventDefault(); e.preventDefault();
break; break;
case "shift+n": case "shift+n":
self.navigateNext(); self.$emit("navigateNext");
e.preventDefault(); e.preventDefault();
break; break;
case "shift+,": case "shift+,":
@ -184,6 +215,22 @@ export default {
case "shift+.": case "shift+.":
self.$player.trickPlay(Math.min(videoEl.playbackRate + 0.25, 2)); self.$player.trickPlay(Math.min(videoEl.playbackRate + 0.25, 2));
break; 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 +246,8 @@ export default {
}, },
methods: { methods: {
async loadVideo() { async loadVideo() {
this.updateSponsors();
const component = this; const component = this;
const videoEl = this.$refs.videoEl; const videoEl = this.$refs.videoEl;
@ -207,26 +256,9 @@ export default {
const time = this.$route.query.t ?? this.$route.query.start; const time = this.$route.query.t ?? this.$route.query.start;
if (time) { if (time) {
let start = 0; videoEl.currentTime = parseTimeParam(time);
if (/^[\d]*$/g.test(time)) {
start = time;
} else {
const hours = /([\d]*)h/gi.exec(time)?.[1];
const minutes = /([\d]*)m/gi.exec(time)?.[1];
const seconds = /([\d]*)s/gi.exec(time)?.[1];
if (hours) {
start += parseInt(hours) * 60 * 60;
}
if (minutes) {
start += parseInt(minutes) * 60;
}
if (seconds) {
start += parseInt(seconds);
}
}
videoEl.currentTime = start;
this.initialSeekComplete = true; this.initialSeekComplete = true;
} else if (window.db) { } else if (window.db && this.getPreferenceBoolean("watchHistory", false)) {
var tx = window.db.transaction("watch_history", "readonly"); var tx = window.db.transaction("watch_history", "readonly");
var store = tx.objectStore("watch_history"); var store = tx.objectStore("watch_history");
var request = store.get(this.video.id); var request = store.get(this.video.id);
@ -256,9 +288,7 @@ export default {
const MseSupport = window.MediaSource !== undefined; const MseSupport = window.MediaSource !== undefined;
const lbry = this.getPreferenceBoolean("disableLBRY", false) const lbry = null;
? null
: this.video.videoStreams.filter(stream => stream.quality === "LBRY")[0];
var uri; var uri;
var mime; var mime;
@ -268,9 +298,10 @@ export default {
mime = "application/x-mpegURL"; mime = "application/x-mpegURL";
} else if (this.video.audioStreams.length > 0 && !lbry && MseSupport) { } else if (this.video.audioStreams.length > 0 && !lbry && MseSupport) {
if (!this.video.dash) { if (!this.video.dash) {
const dash = ( const dash = (await import("../utils/DashUtils.js")).generate_dash_file_from_formats(
await import("@/utils/DashUtils.js").then(mod => mod.default) streams,
).generate_dash_file_from_formats(streams, this.video.duration); this.video.duration,
);
uri = "data:application/dash+xml;charset=utf-8;base64," + btoa(dash); uri = "data:application/dash+xml;charset=utf-8;base64," + btoa(dash);
} else { } else {
@ -306,7 +337,7 @@ export default {
uri = this.video.hls; uri = this.video.hls;
mime = "application/x-mpegURL"; mime = "application/x-mpegURL";
} else { } 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"; mime = "video/mp4";
} }
@ -356,22 +387,19 @@ export default {
else this.setPlayerAttrs(this.$player, videoEl, uri, mime, this.$shaka); else this.setPlayerAttrs(this.$player, videoEl, uri, mime, this.$shaka);
if (noPrevPlayer) { if (noPrevPlayer) {
videoEl.addEventListener("loadeddata", () => {
if (document.pictureInPictureElement) videoEl.requestPictureInPicture();
});
videoEl.addEventListener("timeupdate", () => { videoEl.addEventListener("timeupdate", () => {
const time = videoEl.currentTime; const time = videoEl.currentTime;
this.$emit("timeupdate", time); this.$emit("timeupdate", time);
this.updateProgressDatabase(time); this.updateProgressDatabase(time);
if (this.sponsors && this.sponsors.segments) { if (this.sponsors && this.sponsors.segments) {
this.sponsors.segments.map(segment => { const segment = this.findCurrentSegment(time);
if (!segment.skipped || this.selectedAutoLoop) { this.inSegment = !!segment;
const end = segment.segment[1]; if (segment?.autoskip && (!segment.skipped || this.selectedAutoLoop)) {
if (time >= segment.segment[0] && time < end) { this.skipSegment(videoEl, segment);
console.log("Skipped segment at " + time); }
videoEl.currentTime = end;
segment.skipped = true;
return;
}
}
});
} }
}); });
@ -386,18 +414,27 @@ export default {
}); });
videoEl.addEventListener("ended", () => { videoEl.addEventListener("ended", () => {
if ( this.$emit("ended");
!this.selectedAutoLoop &&
this.selectedAutoPlay &&
(this.playlist?.relatedStreams?.length > 0 || this.video.relatedStreams.length > 0)
) {
this.navigateNext();
}
}); });
} }
//TODO: Add sponsors on seekbar: https://github.com/ajayyy/SponsorBlock/blob/e39de9fd852adb9196e0358ed827ad38d9933e29/src/js-components/previewBar.ts#L12 //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) { setPlayerAttrs(localPlayer, videoEl, uri, mime, shaka) {
const url = "/watch?v=" + this.video.id; const url = "/watch?v=" + this.video.id;
@ -471,8 +508,13 @@ export default {
this.updateMarkers(); this.updateMarkers();
const event = new Event("playerInit");
window.dispatchEvent(event);
const player = this.$ui.getControls().getPlayer(); const player = this.$ui.getControls().getPlayer();
this.setupSeekbarPreview();
this.$player = player; this.$player = player;
const disableVideo = this.getPreferenceBoolean("listen", false) && !this.video.livestream; const disableVideo = this.getPreferenceBoolean("listen", false) && !this.video.livestream;
@ -483,6 +525,9 @@ export default {
manifest: { manifest: {
disableVideo: disableVideo, disableVideo: disableVideo,
}, },
streaming: {
segmentPrefetchLimit: 10,
},
}); });
const quality = this.getPreferenceNumber("quality", 0); const quality = this.getPreferenceNumber("quality", 0);
@ -490,70 +535,83 @@ export default {
quality > 0 && (this.video.audioStreams.length > 0 || this.video.livestream) && !disableVideo; quality > 0 && (this.video.audioStreams.length > 0 || this.video.livestream) && !disableVideo;
if (qualityConds) this.$player.configure("abr.enabled", false); if (qualityConds) this.$player.configure("abr.enabled", false);
player.load(uri, 0, mime).then(() => { player
const isSafari = window.navigator?.vendor?.includes("Apple"); .load(uri, 0, mime)
.then(() => {
const isSafari = window.navigator?.vendor?.includes("Apple");
if (!isSafari) { if (!isSafari) {
// Set the audio language // Set the audio language
const prefLang = this.getPreferenceString("hl", "en").substr(0, 2); const prefLang = this.getPreferenceString("hl", "en").substr(0, 2);
var lang = "en"; var lang = "en";
for (var l in player.getAudioLanguages()) { for (var l in player.getAudioLanguages()) {
if (l == prefLang) { if (l == prefLang) {
lang = l; lang = l;
return; 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)
tracks.forEach(track => {
const audioBandwidth = track.audioBandwidth;
if (audioBandwidth > bestAudio) bestAudio = audioBandwidth;
});
// Find best matching stream based on resolution and bitrate
tracks
.sort((a, b) => a.bandwidth - b.bandwidth)
.forEach(stream => {
if (stream.audioBandwidth < bestAudio) return;
const diff = Math.abs(quality - stream.height);
if (diff < leastDiff) {
leastDiff = diff;
bestStream = stream;
} }
}); }
player.selectAudioLanguage(lang);
}
player.selectVariantTrack(bestStream); if (qualityConds) {
} var leastDiff = Number.MAX_VALUE;
var bestStream = null;
this.video.subtitles.map(subtitle => { var bestAudio = 0;
player.addTextTrackAsync(
subtitle.url, const tracks = player
subtitle.code, .getVariantTracks()
"SUBTITLE", .filter(track => track.language == lang || track.language == "und");
subtitle.mimeType,
null, // Choose the best audio stream
subtitle.name, if (quality >= 480)
); tracks.forEach(track => {
const audioBandwidth = track.audioBandwidth;
if (audioBandwidth > bestAudio) bestAudio = audioBandwidth;
});
// Find best matching stream based on resolution and bitrate
tracks
.sort((a, b) => a.bandwidth - b.bandwidth)
.forEach(stream => {
if (stream.audioBandwidth < bestAudio) return;
const diff = Math.abs(quality - stream.height);
if (diff < leastDiff) {
leastDiff = diff;
bestStream = stream;
}
});
player.selectVariantTrack(bestStream);
}
this.video.subtitles.map(subtitle => {
player.addTextTrackAsync(
subtitle.url,
subtitle.code,
"subtitles",
subtitle.mimeType,
null,
subtitle.name,
);
});
videoEl.volume = this.getPreferenceNumber("volume", 1);
const rate = this.getPreferenceNumber("rate", 1);
videoEl.playbackRate = rate;
videoEl.defaultPlaybackRate = rate;
const autoDisplayCaptions = this.getPreferenceBoolean("autoDisplayCaptions", false);
this.$player.setTextTrackVisibility(autoDisplayCaptions);
})
.catch(e => {
console.error(e);
this.error = e.code;
}); });
videoEl.volume = this.getPreferenceNumber("volume", 1);
const rate = this.getPreferenceNumber("rate", 1); // expand the player to fullscreen when the fullscreen query equals true
videoEl.playbackRate = rate; if (this.$route.query.fullscreen === "true" && !this.$ui.getControls().isFullScreenEnabled())
videoEl.defaultPlaybackRate = rate; this.$ui.getControls().toggleFullScreen();
});
}, },
async updateProgressDatabase(time) { async updateProgressDatabase(time) {
// debounce // debounce
@ -578,29 +636,7 @@ export default {
this.$refs.videoEl.currentTime = time; 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() { updateMarkers() {
const markers = this.$refs.container.querySelector(".shaka-ad-markers"); const markers = this.$refs.container.querySelector(".shaka-ad-markers");
const array = ["to right"]; const array = ["to right"];
@ -608,38 +644,19 @@ export default {
const start = (segment.segment[0] / this.video.duration) * 100; const start = (segment.segment[0] / this.video.duration) * 100;
const end = (segment.segment[1] / this.video.duration) * 100; const end = (segment.segment[1] / this.video.duration) * 100;
var color; var color = [
switch (segment.category) { "sponsor",
case "sponsor": "selfpromo",
color = "#00d400"; "interaction",
break; "poi_highlight",
case "selfpromo": "intro",
color = "#ffff00"; "outro",
break; "preview",
case "interaction": "filler",
color = "#cc00ff"; "music_offtopic",
break; ].includes(segment.category)
case "poi_highlight": ? `var(--spon-seg-${segment.category})`
color = "#ff1684"; : "var(--spon-seg-default)";
break;
case "intro":
color = "#00ffff";
break;
case "outro":
color = "#0202ed";
break;
case "preview":
color = "#008fd6";
break;
case "filler":
color = "#7300FF";
break;
case "music_offtopic":
color = "#ff9900";
break;
default:
color = "white";
}
array.push(`transparent ${start}%`); array.push(`transparent ${start}%`);
array.push(`${color} ${start}%`); array.push(`${color} ${start}%`);
@ -653,33 +670,133 @@ export default {
if (markers) markers.style.background = `linear-gradient(${array.join(",")})`; if (markers) markers.style.background = `linear-gradient(${array.join(",")})`;
}, },
destroy(hotkeys) { updateSponsors() {
if (this.$ui) {
this.$ui.destroy();
this.$ui = undefined;
this.$player = undefined;
}
if (this.$player) {
this.$player.destroy();
this.$player = undefined;
}
if (hotkeys) this.$hotkeys?.unbind();
this.$refs.container?.querySelectorAll("div").forEach(node => node.remove());
},
},
watch: {
sponsors() {
if (this.getPreferenceBoolean("showMarkers", true)) { if (this.getPreferenceBoolean("showMarkers", true)) {
this.shakaPromise.then(() => { this.shakaPromise.then(() => {
this.updateMarkers(); 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;
this.$refs.previewContainer.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 container = this.$refs.previewContainer;
const canvas = this.$refs.preview;
const ctx = canvas.getContext("2d");
const offsetX = frame.positionX * frame.frameWidth;
const offsetY = frame.positionY * frame.frameHeight;
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 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 - this.seekbarPadding;
this.currentTime = position / 1000;
container.style.left = `max(${this.seekbarPadding}%, min(${left}%, ${maxLeft}%))`;
container.style.display = "flex";
},
// 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 && !document.pictureInPictureElement) {
this.$ui.destroy();
this.$ui = undefined;
this.$player = undefined;
}
if (this.$player) {
this.$player.destroy();
if (!document.pictureInPictureElement) this.$player = undefined;
}
if (hotkeys) this.$hotkeys?.unbind();
this.$refs.container?.querySelectorAll("div").forEach(node => node.remove());
},
}, },
}; };
</script> </script>
<style> <style>
:root {
--player-base: rgba(255, 255, 255, 0.3);
--player-buffered: rgba(255, 255, 255, 0.54);
--player-played: rgba(255, 0, 0);
--spon-seg-sponsor: #00d400;
--spon-seg-selfpromo: #ffff00;
--spon-seg-interaction: #cc00ff;
--spon-seg-poi_highlight: #ff1684;
--spon-seg-intro: #00ffff;
--spon-seg-outro: #0202ed;
--spon-seg-preview: #008fd6;
--spon-seg-filler: #7300ff;
--spon-seg-music_offtopic: #ff9900;
--spon-seg-default: white;
}
.player-container { .player-container {
@apply max-h-75vh min-h-64; @apply max-h-75vh min-h-64;
background: #000; background: #000;
@ -732,11 +849,15 @@ html .shaka-range-element:focus {
.material-icons-round { .material-icons-round {
font-family: "Material Icons Round" !important; font-family: "Material Icons Round" !important;
} }
.material-icons-round, .shaka-bottom-controls .material-icons-round,
.shaka-current-time { .shaka-current-time {
-webkit-text-fill-color: #fff !important; -webkit-text-fill-color: #fff !important;
filter: none !important; filter: none !important;
opacity: 1 !important; opacity: 1 !important;
box-shadow: none !important;
}
.shaka-bottom-controls .material-icons-round {
box-shadow: none !important;
} }
.shaka-overflow-menu, .shaka-overflow-menu,
.shaka-settings-menu { .shaka-settings-menu {
@ -767,4 +888,17 @@ html .shaka-range-element:focus {
.shaka-controls-container { .shaka-controls-container {
border-radius: var(--efy_radius) !important; border-radius: var(--efy_radius) !important;
} }
.skip-segment-button {
z-index: 1000;
position: absolute;
transform: translate(0, -50%);
top: 50%;
right: 0;
border-right: 0;
border-radius: var(--efy_radius) 0 0 var(--efy_radius);
display: flex;
align-items: center;
justify-content: center;
}
</style> </style>

View File

@ -0,0 +1,29 @@
<script>
export default {
props: {
link: {
type: String,
required: true,
},
platform: {
type: String,
required: false,
default: "YouTube",
},
},
};
</script>
<template>
<template v-if="getPreferenceBoolean('showWatchOnYouTube', false)">
<a
:href="link"
role="button"
class="pp-square flex items-center justify-center"
:aria-label="'Watch on Odysee'"
:title="`${$t('player.watch_on')}${platform}`"
>
<font-awesome-icon class="mx-1.5" :icon="['fab', platform.toLowerCase()]" />
</a>
</template>
</template>

View File

@ -1,24 +0,0 @@
<script>
export default {
props: {
link: String,
},
};
</script>
<template>
<template v-if="this.getPreferenceBoolean('showWatchOnYouTube', false)">
<a :href="link" class="pp-watch-onyt-btn btn" role="button" :title="$t('player.watch_on') + 'YouTube'">
<font-awesome-icon class="mx-1.5" :icon="['fab', 'youtube']" />
</a>
</template>
</template>
<style>
.pp-watch-onyt-btn {
display: flex;
margin-left: var(--efy_gap0);
align-items: center;
gap: 5rem;
}
</style>

View File

@ -1,43 +1,48 @@
<template> <template>
<div v-if="video && isEmbed" class="absolute top-0 left-0 h-full w-full bg-black z-50"> <div v-if="video && isEmbed" class="absolute left-0 top-0 z-50 h-full w-full bg-black">
<VideoPlayer <VideoPlayer
ref="videoPlayer" ref="videoPlayer"
:video="video" :video="video"
:sponsors="sponsors" :sponsors="sponsors"
:playlist="playlist"
:index="index"
:selected-auto-play="false" :selected-auto-play="false"
:selected-auto-loop="selectedAutoLoop" :selected-auto-loop="selectedAutoLoop"
:is-embed="isEmbed" :is-embed="isEmbed"
/> />
</div> </div>
<div v-if="video && !isEmbed" class="w-full"> <LoadingIndicatorPage :show-content="video && !isEmbed" class="w-full mt-[15rem]">
<ErrorHandler v-if="video && video.error" :message="video.message" :error="video.error" /> <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 v-show="!video.error">
<div :class="isMobile ? 'flex-col' : 'flex'"> <div :class="isMobile ? 'flex-col' : 'flex'">
<VideoPlayer <keep-alive>
ref="videoPlayer" <VideoPlayer
:video="video" ref="videoPlayer"
:sponsors="sponsors" :video="video"
:playlist="playlist" :sponsors="sponsors"
:index="index" :selected-auto-play="selectedAutoPlay"
:selected-auto-play="selectedAutoPlay" :selected-auto-loop="selectedAutoLoop"
:selected-auto-loop="selectedAutoLoop" @timeupdate="onTimeUpdate"
@timeupdate="onTimeUpdate" @ended="onVideoEnded"
/> @navigate-next="navigateNext"
/>
</keep-alive>
<ChaptersBar <ChaptersBar
:mobileLayout="isMobile"
v-if="video?.chapters?.length > 0 && showChapters" v-if="video?.chapters?.length > 0 && showChapters"
:mobile-layout="isMobile"
:chapters="video.chapters" :chapters="video.chapters"
:player-position="currentTime" :player-position="currentTime"
@seek="navigate" @seek="navigate"
/> />
</div> </div>
<!-- video title --> <!-- video title -->
<div class="pp-video-title font-bold mt-2 text-2xl break-words" v-text="video.title" /> <div class="pp-video-title mt-2 break-words text-2xl font-bold" v-text="video.title" />
<div class="pp-bellow-video flex flex-wrap mt-3 mb-3"> <div class="pp-bellow-video mb-3 mt-3 flex flex-wrap">
<!-- views / date --> <!-- views / date -->
<div class="flex flex-auto"> <div class="flex flex-auto">
<span v-t="{ path: 'video.views', args: { views: addCommas(video.views) } }" /> <span v-t="{ path: 'video.views', args: { views: addCommas(video.views) } }" />
@ -48,11 +53,11 @@
<div class="pp-likes flex children:mr-2"> <div class="pp-likes flex children:mr-2">
<template v-if="video.likes >= 0"> <template v-if="video.likes >= 0">
<div class="flex items-center"> <div class="flex items-center">
<div class="i-fa-solid:thumbs-up" /> <div class="i-fa6-solid:thumbs-up" />
<strong class="ml-1" v-text="addCommas(video.likes)" /> <strong class="ml-1" v-text="addCommas(video.likes)" />
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<div class="i-fa-solid:thumbs-down" /> <div class="i-fa6-solid:thumbs-down" />
<strong class="ml-1" v-text="video.dislikes >= 0 ? addCommas(video.dislikes) : '?'" /> <strong class="ml-1" v-text="video.dislikes >= 0 ? addCommas(video.dislikes) : '?'" />
</div> </div>
</template> </template>
@ -72,54 +77,29 @@
video.uploader video.uploader
}}</router-link> }}</router-link>
<!-- Verified Badge --> <!-- Verified Badge -->
<font-awesome-icon class="ml-1" v-if="video.uploaderVerified" icon="check" /> <font-awesome-icon v-if="video.uploaderVerified" class="ml-1" icon="check" />
</div> </div>
<div class="pp-watch-buttons"> <div class="pp-watch-buttons">
<!-- Subscribe button --> <!-- Subscribe button -->
<button <button
class="btn"
@click="subscribeHandler"
v-t="{ v-t="{
path: `actions.${subscribed ? 'unsubscribe' : 'subscribe'}`, path: `actions.${subscribed ? 'unsubscribe' : 'subscribe'}`,
args: { count: numberFormat(video.uploaderSubscriberCount) }, args: { count: numberFormat(video.uploaderSubscriberCount) },
}" }"
class="btn"
@click="subscribeHandler"
/> />
<!-- RSS Feed button -->
<a
aria-label="RSS feed"
title="RSS feed"
role="button"
v-if="video.uploaderUrl"
:href="`${apiUrl()}/feed/unauthenticated/rss?channels=${video.uploaderUrl.split('/')[2]}`"
target="_blank"
class="btn flex-col"
>
<font-awesome-icon icon="rss" />
</a>
<WatchOnYouTubeButton :link="`https://youtu.be/${getVideoId()}`" />
<!-- Share Dialog -->
<button class="btn" @click="showShareModal = !showShareModal">
<i18n-t class="lt-lg:hidden" keypath="actions.share" tag="strong"></i18n-t>
<font-awesome-icon class="mx-1.5 ml-1" icon="fa-share" />
</button>
<!-- LBRY -->
<a v-if="video.lbryId" :href="'https://odysee.com/' + video.lbryId" class="btn">
<i18n-t keypath="player.watch_on" tag="strong">LBRY</i18n-t>
</a>
<!-- listen / watch toggle -->
<router-link
:to="toggleListenUrl"
:aria-label="(isListening ? 'Watch ' : 'Listen to ') + video.title"
:title="(isListening ? 'Watch ' : 'Listen to ') + video.title"
class="btn flex-col"
>
<font-awesome-icon :icon="isListening ? 'tv' : 'headphones'" />
</router-link>
<!-- Playlist Add button --> <!-- Playlist Add button -->
<button class="btn" v-if="authenticated" @click="showModal = !showModal"> <button class="btn flex items-center" @click="showModal = !showModal">
{{ $t("actions.add_to_playlist") }}<font-awesome-icon class="ml-1" icon="circle-plus" /> {{ $t("actions.add_to_playlist") }}<font-awesome-icon class="ml-1" icon="circle-plus" />
</button> </button>
<PlaylistAddModal v-if="showModal" :video-id="getVideoId()" @close="showModal = !showModal" /> <PlaylistAddModal
v-if="showModal"
:video-id="getVideoId()"
:video-info="video"
@close="showModal = !showModal"
/>
<!-- Share Dialog -->
<ShareModal <ShareModal
v-if="showShareModal" v-if="showShareModal"
:video-id="getVideoId()" :video-id="getVideoId()"
@ -128,59 +108,119 @@
:playlist-index="index" :playlist-index="index"
@close="showShareModal = !showShareModal" @close="showShareModal = !showShareModal"
/> />
<button class="btn flex items-center share-btn" @click="showShareModal = !showShareModal">
<font-awesome-icon class="mx-1.5 mr-1" icon="fa-share" />
<i18n-t keypath="actions.share" tag="strong"></i18n-t>
</button>
<!-- YouTube -->
<WatchOnButton :link="`https://youtu.be/${getVideoId()}`" />
<!-- Odysee -->
<WatchOnButton v-if="video.lbryId" platform="Odysee" :link="`https://odysee.com/${video.lbryId}`" />
<!-- listen / watch toggle -->
<router-link
:to="toggleListenUrl"
role="button"
:aria-label="(isListening ? 'Watch ' : 'Listen to ') + video.title"
:title="(isListening ? 'Watch ' : 'Listen to ') + video.title"
class="pp-square btn flex items-center"
>
<font-awesome-icon class="mx-1.5" :icon="isListening ? 'tv' : 'headphones'" />
</router-link>
<!-- RSS Feed button -->
<a
v-if="video.uploaderUrl"
aria-label="RSS feed"
title="RSS feed"
role="button"
:href="`${apiUrl()}/feed/unauthenticated/rss?channels=${video.uploaderUrl.split('/')[2]}`"
target="_blank"
class="pp-square btn flex items-center"
>
<font-awesome-icon class="mx-1.5" icon="rss" />
</a>
<button class="btn flex items-center gap-1 <md:hidden" @click="downloadCurrentFrame">
<i class="i-fa6-solid:download" />{{ $t("actions.download_frame") }}
</button>
</div> </div>
</div> </div>
<hr /> <hr class="mb-2" />
<div
v-for="metaInfo in video?.metaInfo ?? []"
:key="metaInfo.title"
class="btn my-3 flex flex-wrap cursor-default gap-2 px-4 py-2"
>
<span>{{ metaInfo.description ?? metaInfo.title }}</span>
<a v-for="(link, linkIndex) in metaInfo.urls" :key="linkIndex" :href="link" class="underline">{{
metaInfo.urlTexts[linkIndex]
}}</a>
<br />
</div>
<div efy_select> <div efy_select>
<input id="showDesc" type="checkbox" v-model="showDesc" /> <input id="showDesc" v-model="showDesc" type="checkbox" />
<label for="showDesc" v-t="'actions.show_description'" /> <label v-t="'actions.show_description'" for="showDesc" />
<input id="showComments" type="checkbox" v-model="showComments" @click="toggleComments" /> <input id="showComments" v-model="showComments" type="checkbox" @click="toggleComments" />
<label for="showComments" v-t="'actions.show_comments'" /> <label
<input id="showRecs" type="checkbox" v-model="showRecs" /> v-text="`${$t('actions.show_comments')} - ${numberFormat(comments?.commentCount)}`"
<label for="showRecs" v-t="'actions.show_recommendations'" /> for="showComments"
/>
<input id="showRecs" v-model="showRecs" type="checkbox" />
<label v-t="'actions.show_recommendations'" for="showRecs" />
<span v-show="video?.chapters?.length > 0">
<input id="showChapters" v-model="showChapters" type="checkbox" />
<label v-t="'actions.show_chapters'" class="ml-2" for="showChapters" />
</span>
<input id="chkAutoLoop" v-model="selectedAutoLoop" type="checkbox" @change="onChange($event)" /> <input id="chkAutoLoop" v-model="selectedAutoLoop" type="checkbox" @change="onChange($event)" />
<label for="chkAutoLoop" v-text="`${$t('actions.loop_this_video')}`" /> <label for="chkAutoLoop" v-text="`${$t('actions.loop_this_video')}`" />
<input id="chkAutoPlay" v-model="selectedAutoPlay" type="checkbox" @change="onChange($event)" /> <input id="chkAutoPlay" v-model="selectedAutoPlay" type="checkbox" @change="onChange($event)" />
<label for="chkAutoPlay" v-text="`${$t('actions.auto_play_next_video')}`" /> <label for="chkAutoPlay" v-text="`${$t('actions.auto_play_next_video')}`" />
<span v-show="video?.chapters?.length > 0">
<input id="showChapters" type="checkbox" v-model="showChapters" />
<label class="ml-2" for="showChapters" v-t="'actions.show_chapters'" />
</span>
</div> </div>
<!-- eslint-disable-next-line vue/no-v-html --> <template v-if="showDesc">
<div <hr />
v-show="showDesc" <!-- eslint-disable-next-line vue/no-v-html -->
class="break-words mb-2" <div class="description break-words" v-html="purifiedDescription" />
v-html="purifyHTML(video.description)" <hr />
style="border-top: var(--efy_border); margin: 15rem 0; padding: 15rem 0" <div
/> v-if="sponsors && sponsors.segments"
<div v-text="`${$t('video.sponsor_segments')}: ${sponsors.segments.length}`"
v-if="showDesc && sponsors && sponsors.segments" />
v-text="`${$t('video.sponsor_segments')}: ${sponsors.segments.length}`" <div v-if="video.category" v-text="`${$t('video.category')}: ${video.category}`" />
/> <div v-text="`${$t('video.license')}: ${video.license}`" />
<div class="capitalize" v-text="`${$t('video.visibility')}: ${video.visibility}`" />
<hr />
<div v-if="video.tags" class="video-tags">
<router-link
v-for="tag in video.tags"
:key="tag"
class="line-clamp-1 efy_trans_filter efy_shadow_trans"
:to="`/results?search_query=${encodeURIComponent(tag)}`"
>{{ tag }}</router-link
>
</div>
</template>
</div> </div>
<hr /> <hr />
<div class="grid pp-rec-vids"> <div class="pp-rec-vids grid">
<div v-if="!showComments" class="w-full"></div> <div v-if="!showComments" class="w-full"></div>
<div v-if="!comments" class=""> <div v-else-if="!comments">
<p class="text-center mt-8" v-t="'comment.loading'"></p> <p v-t="'comment.loading'" class="mt-8 text-center"></p>
</div> </div>
<div v-else-if="comments.disabled" class=""> <div v-else-if="comments.disabled">
<p class="text-center mt-8" v-t="'comment.disabled'"></p> <p v-t="'comment.disabled'" class="mt-8 text-center"></p>
</div> </div>
<div v-else ref="comments" v-show="showComments" class="pp-comments"> <div v-else ref="comments" class="pp-comments">
<CommentItem <CommentItem
v-for="comment in comments.comments" v-for="comment in comments.comments"
:key="comment.commentId" :key="comment.commentId"
:comment="comment" :comment="comment"
:uploader="video.uploader" :uploader="video.uploader"
:video-id="getVideoId()" :video-id="getVideoId()"
class="efy_trans_filter" class="efy_trans_filter efy_shadow_trans"
/> />
</div> </div>
@ -203,7 +243,7 @@
</div> </div>
</div> </div>
</div> </div>
</div> </LoadingIndicatorPage>
</template> </template>
<script> <script>
@ -215,7 +255,11 @@ import ChaptersBar from "./ChaptersBar.vue";
import PlaylistAddModal from "./PlaylistAddModal.vue"; import PlaylistAddModal from "./PlaylistAddModal.vue";
import ShareModal from "./ShareModal.vue"; import ShareModal from "./ShareModal.vue";
import PlaylistVideos from "./PlaylistVideos.vue"; import PlaylistVideos from "./PlaylistVideos.vue";
import WatchOnYouTubeButton from "./WatchOnYouTubeButton.vue"; import WatchOnButton from "./WatchOnButton.vue";
import LoadingIndicatorPage from "./LoadingIndicatorPage.vue";
import ToastComponent from "./ToastComponent.vue";
import { parseTimeParam } from "@/utils/Misc";
import { purifyHTML, rewriteDescription } from "@/utils/HtmlUtils";
export default { export default {
name: "App", name: "App",
@ -228,14 +272,14 @@ export default {
PlaylistAddModal, PlaylistAddModal,
ShareModal, ShareModal,
PlaylistVideos, PlaylistVideos,
WatchOnYouTubeButton, WatchOnButton,
LoadingIndicatorPage,
ToastComponent,
}, },
data() { data() {
const smallViewQuery = window.matchMedia("(max-width: 640px)"); const smallViewQuery = window.matchMedia("(max-width: 640px)");
return { return {
video: { video: null,
title: "Loading...",
},
playlistId: null, playlistId: null,
playlist: null, playlist: null,
index: null, index: null,
@ -256,6 +300,9 @@ export default {
showShareModal: false, showShareModal: false,
isMobile: true, isMobile: true,
currentTime: 0, currentTime: 0,
shouldShowToast: false,
timeoutCounter: null,
counter: 0,
}; };
}, },
computed: { computed: {
@ -277,6 +324,12 @@ export default {
year: "numeric", year: "numeric",
}); });
}, },
defaultCounter(_this) {
return _this.getPreferenceNumber("autoPlayNextCountdown", 5);
},
purifiedDescription() {
return purifyHTML(this.video.description);
},
}, },
mounted() { mounted() {
// check screen size // check screen size
@ -295,7 +348,7 @@ export default {
(async () => { (async () => {
const videoId = this.getVideoId(); const videoId = this.getVideoId();
const instance = this; 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 tx = window.db.transaction("watch_history", "readwrite");
var store = tx.objectStore("watch_history"); var store = tx.objectStore("watch_history");
var request = store.get(videoId); var request = store.get(videoId);
@ -325,6 +378,8 @@ export default {
this.getPlaylistData(); this.getPlaylistData();
this.getSponsors(); this.getSponsors();
if (!this.isEmbed && this.showComments) this.getComments(); if (!this.isEmbed && this.showComments) this.getComments();
if (this.isEmbed) document.querySelector("html").style.overflow = "hidden";
window.addEventListener("click", this.handleClick);
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
this.smallView = this.smallViewQuery.matches; this.smallView = this.smallViewQuery.matches;
}); });
@ -336,7 +391,7 @@ export default {
this.showDesc = !this.getPreferenceBoolean("minimizeDescription", false); this.showDesc = !this.getPreferenceBoolean("minimizeDescription", false);
this.showRecs = !this.getPreferenceBoolean("minimizeRecommendations", false); this.showRecs = !this.getPreferenceBoolean("minimizeRecommendations", false);
this.showChapters = !this.getPreferenceBoolean("minimizeChapters", false); this.showChapters = !this.getPreferenceBoolean("minimizeChapters", false);
if (this.video.duration) { if (this.video?.duration) {
document.title = this.video.title + " - Piped"; document.title = this.video.title + " - Piped";
this.$refs.videoPlayer.loadVideo(); this.$refs.videoPlayer.loadVideo();
} }
@ -345,24 +400,45 @@ export default {
deactivated() { deactivated() {
this.active = false; this.active = false;
window.removeEventListener("scroll", this.handleScroll); window.removeEventListener("scroll", this.handleScroll);
this.dismiss();
}, },
unmounted() { unmounted() {
window.removeEventListener("scroll", this.handleScroll); window.removeEventListener("scroll", this.handleScroll);
window.removeEventListener("click", this.handleClick);
this.dismiss();
}, },
methods: { methods: {
fetchVideo() { fetchVideo() {
return this.fetchJson(this.apiUrl() + "/streams/" + this.getVideoId()); return this.fetchJson(this.apiUrl() + "/streams/" + this.getVideoId());
}, },
async fetchSponsors() { async fetchSponsors() {
return await this.fetchJson(this.apiUrl() + "/sponsors/" + this.getVideoId(), { var selectedSkip = this.getPreferenceString(
category: "selectedSkip",
'["' + "sponsor,interaction,selfpromo,music_offtopic",
this.getPreferenceString("selectedSkip", "sponsor,interaction,selfpromo,music_offtopic").replaceAll( ).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),
}); });
sponsors?.segments?.forEach(segment => {
const option = skipOptions[segment.category];
segment.autoskip = option === undefined || option === "auto";
});
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() { toggleComments() {
this.showComments = !this.showComments; this.showComments = !this.showComments;
@ -395,13 +471,10 @@ export default {
elem.outerHTML = elem.getAttribute("href"); elem.outerHTML = elem.getAttribute("href");
}); });
xmlDoc.querySelectorAll("br").forEach(elem => (elem.outerHTML = "\n")); xmlDoc.querySelectorAll("br").forEach(elem => (elem.outerHTML = "\n"));
this.video.description = this.urlify(xmlDoc.querySelector("body").innerHTML) this.video.description = rewriteDescription(xmlDoc.querySelector("body").innerHTML);
.replaceAll(/(?:http(?:s)?:\/\/)?(?:www\.)?youtube\.com(\/[/a-zA-Z0-9_?=&-]*)/gm, "$1") this.updateWatched(this.video.relatedStreams);
.replaceAll(
/(?:http(?:s)?:\/\/)?(?:www\.)?youtu\.be\/(?:watch\?v=)?([/a-zA-Z0-9_?=&-]*)/gm, this.fetchDeArrowContent(this.video.relatedStreams);
"/watch?v=$1",
)
.replaceAll("\n", "<br>");
} }
}); });
}, },
@ -422,6 +495,9 @@ export default {
} }
} }
}); });
await this.fetchPlaylistPages().then(() => {
this.fetchDeArrowContent(this.playlist.relatedStreams);
});
} }
}, },
async fetchPlaylistPages() { async fetchPlaylistPages() {
@ -500,6 +576,35 @@ export default {
} }
this.subscribed = !this.subscribed; this.subscribed = !this.subscribed;
}, },
handleClick(event) {
if (!event || !event.target) return;
if (!event.target.matches("a[href]")) return;
const target = event.target;
if (!target.getAttribute("href")) return;
if (this.handleTimestampLinks(target)) {
event.preventDefault();
}
},
handleTimestampLinks(target) {
try {
const url = new URL(target.getAttribute("href"), document.baseURI);
if (
url.searchParams.size > 2 ||
url.searchParams.get("v") !== this.getVideoId() ||
!url.searchParams.has("t")
) {
return false;
}
const time = parseTimeParam(url.searchParams.get("t"));
if (time) {
this.navigate(time);
}
return true;
} catch (e) {
console.error(e);
}
return false;
},
handleScroll() { handleScroll() {
if (this.loading || !this.comments || !this.comments.nextpage) return; if (this.loading || !this.comments || !this.comments.nextpage) return;
if (window.innerHeight + window.scrollY >= this.$refs.comments?.offsetHeight - window.innerHeight) { if (window.innerHeight + window.scrollY >= this.$refs.comments?.offsetHeight - window.innerHeight) {
@ -523,6 +628,98 @@ export default {
onTimeUpdate(time) { onTimeUpdate(time) {
this.currentTime = 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);
},
downloadCurrentFrame() {
const video = document.querySelector("video");
const canvas = document.createElement("canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const context = canvas.getContext("2d");
context.drawImage(video, 0, 0, canvas.width, canvas.height);
let link = document.createElement("a");
const currentTime = Math.round(video.currentTime * 1000) / 1000;
link.download = `${this.video.title}_${currentTime}s.png`;
link.href = canvas.toDataURL();
link.click();
},
}, },
}; };
</script> </script>
<style>
.v-enter-from,
.v-leave-to {
opacity: 0;
transform: translateX(100%) scale(0.5);
}
.description a {
text-decoration: underline;
filter: brightness(0.75);
}
@media (width <= 768px) {
.share-btn {
aspect-ratio: 1;
}
.share-btn strong {
display: none;
}
.share-btn svg {
margin: 0;
}
}
</style>

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

@ -0,0 +1 @@
{}

View File

@ -4,18 +4,22 @@
"login": "تسجيل الدخول", "login": "تسجيل الدخول",
"register": "إنشاء حساب", "register": "إنشاء حساب",
"preferences": "الإعدادات", "preferences": "الإعدادات",
"history": "تاريخ التصفح", "history": "سجل المشاهدة",
"subscriptions": "الاشتراكات", "subscriptions": "الاشتراكات",
"playlists": "قوائم التشغيل", "playlists": "قوائم التشغيل",
"feed": "التغذية", "feed": "محتوى الاشتراكات",
"account": "الحساب", "account": "الحساب",
"instance": "الخادم", "instance": "الخادم",
"player": "المشغل", "player": "المشغل",
"livestreams": "البث المباشر", "livestreams": "البث المباشر",
"channels": "القنوات" "channels": "القنوات",
"bookmarks": "الاشارات المرجعيه",
"channel_groups": "مجموعات القنوات",
"dearrow": "دي ارو"
}, },
"player": { "player": {
"watch_on": "شاهد عبر" "watch_on": "شاهد عبر",
"failed": "فشل مع رمز الخطأ {0}، راجع السجلات لمزيد من المعلومات"
}, },
"actions": { "actions": {
"subscribe": "اشتراك - {count}", "subscribe": "اشتراك - {count}",
@ -41,7 +45,7 @@
"enable_sponsorblock": "تفعيل مانع الإعلانات", "enable_sponsorblock": "تفعيل مانع الإعلانات",
"auto": "تلقائي", "auto": "تلقائي",
"dark": "داكن", "dark": "داكن",
"search": "بحث", "search": "بحث (Ctrl+K)",
"autoplay_video": "تشغيل تلقائي", "autoplay_video": "تشغيل تلقائي",
"audio_only": "صوت فقط", "audio_only": "صوت فقط",
"default_quality": "الجودة الأساسية", "default_quality": "الجودة الأساسية",
@ -50,7 +54,7 @@
"skip_interaction": "تخطي تذكير التفاعل (اشتراك)", "skip_interaction": "تخطي تذكير التفاعل (اشتراك)",
"skip_non_music": "تخطي الموسيقى: قسم غير الموسيقى", "skip_non_music": "تخطي الموسيقى: قسم غير الموسيقى",
"theme": "السمة", "theme": "السمة",
"instance_selection": "تحديد المثيل", "instance_selection": "قائمة الخوادم",
"export_to_json": "تصدير إلى JSON", "export_to_json": "تصدير إلى JSON",
"show_more": "اظهار المزيد", "show_more": "اظهار المزيد",
"skip_outro": "تخطي بطاقات النهاية / الاعتمادات", "skip_outro": "تخطي بطاقات النهاية / الاعتمادات",
@ -60,15 +64,15 @@
"skip_filler_tangent": "تخطي المحتوى الغير مهم", "skip_filler_tangent": "تخطي المحتوى الغير مهم",
"show_markers": "إظهار العلامات على المشغل", "show_markers": "إظهار العلامات على المشغل",
"buffering_goal": "هدف التخزين المؤقت (بالثواني)", "buffering_goal": "هدف التخزين المؤقت (بالثواني)",
"country_selection": "اختيار البلد", "country_selection": "البلد",
"default_homepage": "الصفحة الرئيسية الافتراضية", "default_homepage": "الصفحة الرئيسية الافتراضية",
"show_comments": "إظهار التعليقات", "show_comments": "إظهار التعليقات",
"minimize_description_default": "تصغير الوصف بشكل افتراضي", "minimize_description_default": "تصغير الوصف بشكل افتراضي",
"store_watch_history": "تخزين سجل المشاهدة", "store_watch_history": "تخزين سجل المشاهدة",
"language_selection": "اختيار اللغة", "language_selection": "اللغة",
"instances_list": "قائمة المثيلات", "instances_list": "قائمة المثيلات",
"enabled_codecs": "برامج الترميز الممكنة (متعددة)", "enabled_codecs": "برامج الترميز الممكنة (متعددة)",
"import_from_json": "استيراد من JSON/CSV", "import_from_json": "استيراد من JSON",
"loop_this_video": "تكرار هذا الفيديو", "loop_this_video": "تكرار هذا الفيديو",
"auto_play_next_video": "التشغيل التلقائي للفيديو التالي", "auto_play_next_video": "التشغيل التلقائي للفيديو التالي",
"donations": "التبرعات للتطوير", "donations": "التبرعات للتطوير",
@ -90,7 +94,7 @@
"minimize_recommendations_default": "تقليل التوصيات بشكل افتراضي", "minimize_recommendations_default": "تقليل التوصيات بشكل افتراضي",
"invalidate_session": "تسجيل الخروج من جميع الأجهزة", "invalidate_session": "تسجيل الخروج من جميع الأجهزة",
"different_auth_instance": "استخدام مثيل مختلف للمصادقة", "different_auth_instance": "استخدام مثيل مختلف للمصادقة",
"instance_auth_selection": "تحديد مثيل Autentication", "instance_auth_selection": "خادم المصادقة",
"clone_playlist": "استنساخ قائمة التشغيل", "clone_playlist": "استنساخ قائمة التشغيل",
"clone_playlist_success": "تم استنساخها بنجاح!", "clone_playlist_success": "تم استنساخها بنجاح!",
"download_as_txt": "تنزيل بتنسيق .txt", "download_as_txt": "تنزيل بتنسيق .txt",
@ -105,10 +109,8 @@
"follow_link": "اتبع الرابط", "follow_link": "اتبع الرابط",
"copy_link": "نسخ الرابط", "copy_link": "نسخ الرابط",
"time_code": "رمز الوقت (بالثواني)", "time_code": "رمز الوقت (بالثواني)",
"rename_playlist": "إعادة تسمية قائمة التشغيل",
"new_playlist_name": "اسم قائمة تشغيل جديد",
"show_chapters": "الفصول", "show_chapters": "الفصول",
"store_search_history": "حفظ سجل البحث", "store_search_history": "تخزين سجل البحث",
"documentation": "التوثيق", "documentation": "التوثيق",
"status_page": "الحالة", "status_page": "الحالة",
"source_code": "شفرة المصدر", "source_code": "شفرة المصدر",
@ -120,7 +122,31 @@
"show_watch_on_youtube": "عرض زر مشاهدة على يوتيوب", "show_watch_on_youtube": "عرض زر مشاهدة على يوتيوب",
"minimize_chapters_default": "تصغير الفصول بشكل افتراضي", "minimize_chapters_default": "تصغير الفصول بشكل افتراضي",
"no_valid_playlists": "لا يحتوي الملف على قوائم تشغيل صالحة!", "no_valid_playlists": "لا يحتوي الملف على قوائم تشغيل صالحة!",
"with_playlist": "المشاركة مع قائمة التشغيل" "with_playlist": "المشاركة مع قائمة التشغيل",
"bookmark_playlist": "الاشاره المرجعيه",
"playlist_bookmarked": "تم وضعها في الاشارات المرجعية",
"skip_button_only": "إظهار زر التخطي",
"skip_automatically": "تلقائيا",
"min_segment_length": "الحد الأدنى لطول الفصل (بالثواني)",
"skip_segment": "تخطي الجزء",
"show_less": "عرض أقل",
"autoplay_next_countdown": "العد التنازلي الافتراضي حتى الفيديو التالي ( ثانية )",
"dismiss": "تجاهل",
"group_name": "أسم المجموعة",
"create_group": "إنشاء مجموعة",
"auto_display_captions": "عرض التسميات التوضيحية تلقائيا",
"cancel": "إلغاء",
"okay": "حسنًا",
"playlist_description": "وصف قائمة التشغيل",
"playlist_name": "اسم قائمة التشغيل",
"edit_playlist": "تعديل قائمة التشغيل",
"show_search_suggestions": "إظهار اقتراحات البحث",
"chapters_layout_mobile": "تخطيط الفصول على الهاتف",
"delete_automatically": "الحذف تلقائيا بعد",
"enable_dearrow": "تمكين دي ارو",
"generate_qrcode": "إنشاء رمز الاستجابة السريعة",
"import_from_json_csv": "استيراد من JSON/CSV",
"download_frame": "إطار التحميل"
}, },
"video": { "video": {
"sponsor_segments": "المقاطع الإعلانية", "sponsor_segments": "المقاطع الإعلانية",
@ -130,7 +156,13 @@
"views": "{views} عدد المشاهدات", "views": "{views} عدد المشاهدات",
"shorts": "فديوهات قصيرة", "shorts": "فديوهات قصيرة",
"videos": "الفيديوات", "videos": "الفيديوات",
"live": "{0} مباشر" "live": "{0} مباشر",
"all": "الكل",
"category": "الفئة",
"chapters_vertical": "رَأسِيّ",
"chapters_horizontal": "أفقي",
"visibility": "الظهور",
"license": "الترخيص"
}, },
"search": { "search": {
"channels": "يوتيوب: القنوات", "channels": "يوتيوب: القنوات",
@ -141,7 +173,8 @@
"music_videos": "YT Music: مقاطع فيديو", "music_videos": "YT Music: مقاطع فيديو",
"did_you_mean": "هل تقصد: {0}؟", "did_you_mean": "هل تقصد: {0}؟",
"music_playlists": "YT Music: قوائم التشغيل", "music_playlists": "YT Music: قوائم التشغيل",
"music_albums": "YT Music: ألبومات" "music_albums": "YT Music: ألبومات",
"music_artists": "YT الموسيقى: الفنانين"
}, },
"preferences": { "preferences": {
"version": "الإصدار", "version": "الإصدار",
@ -160,7 +193,9 @@
}, },
"login": { "login": {
"username": "اسم المستخدم", "username": "اسم المستخدم",
"password": "كلمة السر" "password": "كلمة السر",
"passwords_incorrect": "كلمة المرور لم تتطابق!",
"password_confirm": "تأكيد كلمة المرور"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "مشترك في: {0}" "subscribed_channels_count": "مشترك في: {0}"
@ -173,6 +208,12 @@
"page_not_found": "لم يتم العثور على الصفحة", "page_not_found": "لم يتم العثور على الصفحة",
"copied": "نسخ!", "copied": "نسخ!",
"cannot_copy": "لا يمكن نسخه!", "cannot_copy": "لا يمكن نسخه!",
"local_storage": "يتطلب هذا الإجراء التخزين المحلي، هل يتم تمكين ملفات تعريف الارتباط؟" "local_storage": "يتطلب هذا الإجراء التخزين المحلي، هل يتم تمكين ملفات تعريف الارتباط؟",
"register_no_email_note": "لا ينصح باستخدام البريد الإلكتروني كاسم مستخدم. المضي قدما على أي حال؟",
"next_video_countdown": "تشغيل الفيديو التالي بعد { 0 } ق",
"weeks": "{amount} أسبوع (أسابيع)",
"hours": "{amount} ساعة (ساعات)",
"months": "{amount} شهر (أشهر)",
"days": "{amount} يوم (أيام)"
} }
} }

View File

@ -4,15 +4,18 @@
"login": "Daxil ol", "login": "Daxil ol",
"register": "Qeydiyyatdan keç", "register": "Qeydiyyatdan keç",
"feed": "Axın", "feed": "Axın",
"preferences": "Seçimlər", "preferences": "Üstünlüklər",
"history": "Tarixçə", "history": "Tarixçə",
"subscriptions": "Abunəliklər", "subscriptions": "Abunəliklər",
"playlists": "Pleylistlər", "playlists": "Oynatma Siyahıları",
"account": "Hesab", "account": "Hesab",
"instance": "Nümunə", "instance": "Nümunə",
"player": "Oynadıcı", "player": "Oynadıcı",
"livestreams": "Canlı yayımlar", "livestreams": "Canlı Yayımlar",
"channels": "Kanallar" "channels": "Kanallar",
"bookmarks": "Əlfəcinlər",
"channel_groups": "Kanal qrupları",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "{0} saytında bax" "watch_on": "{0} saytında bax"
@ -30,12 +33,12 @@
"uses_api_from": "API-dən istifadə edir ", "uses_api_from": "API-dən istifadə edir ",
"enable_sponsorblock": "SponsorBlok'u Aktivləşdir", "enable_sponsorblock": "SponsorBlok'u Aktivləşdir",
"skip_sponsors": "Sponsorları Ötür", "skip_sponsors": "Sponsorları Ötür",
"skip_intro": "Fasilə/Giriş Animasiyasını Ötür", "skip_intro": "Fasilə/Giriş Animasiyasın Ötür",
"skip_outro": "Bitiş Kartları/Kreditləri Ötür", "skip_outro": "Son Kartları/Kreditləri Ötür",
"skip_preview": "Önbaxışı/Anonsu Ötür", "skip_preview": "Önbaxışı/Anonsu Ötür",
"skip_interaction": "İnteraksiya Xatırladıcısını Ötür(Abunə Ol)", "skip_interaction": "Əlaqələndirmə Xatırladıcısın Ötür(Abunə Ol)",
"skip_self_promo": "Ödənişsiz/Özünü Reklamı Ötür", "skip_self_promo": "Ödənilməmiş/Özünü Reklamı Ötür",
"skip_non_music": "Musiqini Ötür: Musiqi Olmayan Bölmə", "skip_non_music": "Musiqini Ötür: Musiqisiz Bölmə",
"skip_highlight": "Anonsu Ötür", "skip_highlight": "Anonsu Ötür",
"skip_filler_tangent": "Doldurucu Səhnələri Ötür", "skip_filler_tangent": "Doldurucu Səhnələri Ötür",
"theme": "Tema", "theme": "Tema",
@ -44,70 +47,68 @@
"light": "İşıqlı", "light": "İşıqlı",
"autoplay_video": "Videonu Avto-oynat", "autoplay_video": "Videonu Avto-oynat",
"audio_only": "Yalnız Səs", "audio_only": "Yalnız Səs",
"default_quality": "Defolt Keyfiyyət", "default_quality": "Standart Keyfiyyət",
"buffering_goal": "Tamponlama hədəfi (saniyələrlə)", "buffering_goal": "Tamponlama hədəfi (saniyələrlə)",
"export_to_json": "JSON-a İxrac Et", "export_to_json": "JSON-a İxrac Et",
"import_from_json": "JSON/CSV-dan İdxal Et", "import_from_json": "JSON -dan İdxal Et",
"loop_this_video": "Bu Videonu Təkrarla", "loop_this_video": "Bu Videonu Təkrarla",
"auto_play_next_video": "Növbəti Videonu Avto-Oynat", "auto_play_next_video": "Növbəti Videonu Avto-Oynat",
"donations": "İnkişaf ianələri", "donations": "İnkişaf ianələri",
"minimize_description": "Açıqlamanı Kiçilt", "minimize_description": "Açıqlamanı Kiçilt",
"show_description": "Açıqlamanı Göstər", "show_description": "Açıqlamanı Göstər",
"minimize_recommendations": "Tövsiyələri kiçilt", "minimize_recommendations": "Tövsiyələri Kiçilt",
"show_recommendations": "Tövsiyələri Göstər", "show_recommendations": "Tövsiyələri Göstər",
"disable_lbry": "Yayım üçün LBRY-ni deaktiv et", "disable_lbry": "Yayım üçün LBRY-ni deaktiv et",
"enable_lbry_proxy": "LBRY üçün Proksi-ni Aktivləşdir", "enable_lbry_proxy": "LBRY üçün Proksi-ni Aktivləşdir",
"view_ssl_score": "SSL Nəticəsinə Bax", "view_ssl_score": "SSL Nəticəsinə Bax",
"search": "Axtarış", "search": "Axtarış (Ctrl+K)",
"filter": "Filtr", "filter": "Filtr",
"loading": "Yüklənir...", "loading": "Yüklənir...",
"clear_history": "Tarixçəni Təmizlə", "clear_history": "Tarixçəni Təmizlə",
"hide_replies": "Cavabları Gizlət", "hide_replies": "Cavabları Gizlət",
"load_more_replies": "Daha Çox Cavab Yüklə", "load_more_replies": "Daha Çox Cavab Yüklə",
"add_to_playlist": "Pleylistə Əlavə Et", "add_to_playlist": "Oynatma siyahısına əlavə et",
"remove_from_playlist": "Pleylistdən Sil", "remove_from_playlist": "Oynatma siyahısından təmizlə",
"delete_playlist_video_confirm": "Video pleylistdən silinsin?", "delete_playlist_video_confirm": "Video oynatma siyahısından silinsin?",
"create_playlist": "Pleylist Yarat", "create_playlist": "Oynatma Siyahısı Yarat",
"delete_playlist": "Pleylisti Sil", "delete_playlist": "Oynatma Siyahısın Sil",
"select_playlist": "Pleylist Seç", "select_playlist": "Oynatma Siyahısı Seç",
"delete_playlist_confirm": "Bu pleylist silinsin?", "delete_playlist_confirm": "Bu oynatma siyahısı silinsin?",
"please_select_playlist": "Lütfən, pleylist seç", "please_select_playlist": "Xahiş edilir, oynatma siyahısı seç",
"country_selection": "Ölkə Seçimi", "country_selection": "Ölkə",
"default_homepage": "Defolt Əsas Səhifə", "default_homepage": "Standart Əsas Səhifə",
"show_comments": "Şərhləri Göstər", "show_comments": "Şərhləri Göstər",
"instance_selection": "Nümunə Seçimi", "instance_selection": "İnstansiya",
"minimize_description_default": "Açıqlamanı Defolt Olaraq Kiçilt", "minimize_description_default": "Açıqlamanı Standart Olaraq Kiçilt",
"language_selection": "Dil Seçimi", "language_selection": "Dil",
"instances_list": "Nümunələr Siyahısı", "instances_list": "Nümunələr Siyahısı",
"show_more": "Daha Çox Göstər", "show_more": "Daha Çox Göstər",
"no": "Xeyr", "no": "Xeyr",
"store_watch_history": "Baxış Tarixçəsini Saxla", "store_watch_history": "Baxış Tarixçəsin Saxla",
"enabled_codecs": "Aktiv Kodeklər (Birdən çox)", "enabled_codecs": "Aktiv Kodlayıcılar (Çoxlu)",
"yes": "Bəli", "yes": "Bəli",
"show_markers": "Oynadıcıda Markerləri Göstər", "show_markers": "Oynadıcıda Markerləri Göstər",
"delete_account": "Hesabı Sil", "delete_account": "Hesabı Sil",
"logout": "Bu cihazdan çıx", "logout": "Bu cihazdan çıx",
"minimize_recommendations_default": "Defolt olaraq Tövsiyələri kiçilt", "minimize_recommendations_default": "Standart olaraq Tövsiyələri kiçilt",
"download_as_txt": ".txt kimi endir", "download_as_txt": ".txt kimi endir",
"reset_preferences": "Seçimləri sıfırla", "reset_preferences": "Üstünlükləri sıfırla",
"confirm_reset_preferences": "Seçimlərinizi sıfırlamaq istədiyinizə əminsiniz?", "confirm_reset_preferences": "Seçimlərinizi sıfırlamaq istədiyinizə əminsiniz?",
"backup_preferences": "Yedəkləmə seçimləri", "backup_preferences": "Nüsxələmə seçimləri",
"restore_preferences": "Seçimləri bərpa et", "restore_preferences": "Seçimləri bərpa et",
"invalidate_session": "Bütün cihazlardan çıxın", "invalidate_session": "Bütün cihazlardan çıxın",
"different_auth_instance": "Təsdiqləmə üçün fərqli nümunə istifadə et", "different_auth_instance": "Təsdiqləmə üçün fərqli nümunə istifadə et",
"instance_auth_selection": "Təsdiqləmə Nümunəsi Seçimi", "instance_auth_selection": "Təsdiqləmə İnstansiyası",
"clone_playlist": "Pleylisti Klonla", "clone_playlist": "Oynatma Siyahısın Klonla",
"clone_playlist_success": "Uğurla klonlandı!", "clone_playlist_success": "Uğurla klonlandı!",
"rename_playlist": "Pleylist adını dəyiş",
"time_code": "Vaxt kodu (saniyələrlə)", "time_code": "Vaxt kodu (saniyələrlə)",
"store_search_history": "Axtarış tarixçəsini saxla", "store_search_history": "Axtarış Tarixçəsin Saxla",
"documentation": "Sertifikatlaşdırma", "documentation": "Sənədləşdirmə",
"status_page": "Vəziyyət", "status_page": "Vəziyyət",
"source_code": "Mənbə kodu", "source_code": "Mənbə kodu",
"instance_donations": "Nümunə ianələri", "instance_donations": "Nümunə ianələri",
"hide_watched": "Axında baxılan videoları gizlət", "hide_watched": "Axında baxılan videoları gizlət",
"show_chapters": "Bölmələr", "show_chapters": "Bölmələr",
"new_playlist_name": "Yeni pleylist adı",
"share": "Paylaş", "share": "Paylaş",
"with_timecode": "Vaxt kodu ilə paylaş", "with_timecode": "Vaxt kodu ilə paylaş",
"follow_link": "Bağlantını izlə", "follow_link": "Bağlantını izlə",
@ -117,21 +118,45 @@
"reply_count": "{count} cavab", "reply_count": "{count} cavab",
"minimize_comments_default": "Şərhləri standart olaraq kiçilt", "minimize_comments_default": "Şərhləri standart olaraq kiçilt",
"minimize_comments": "Şərhləri Kiçilt", "minimize_comments": "Şərhləri Kiçilt",
"minimize_chapters_default": "Defolt olaraq bölmələri kiçilt", "minimize_chapters_default": "Standart olaraq bölmələri kiçilt",
"show_watch_on_youtube": "YouTube-da Baxış düyməsini göstər", "show_watch_on_youtube": "YouTube-da Baxış düyməsin göstər",
"no_valid_playlists": "Faylda etibarlı pleylistlər yoxdur!", "no_valid_playlists": "Faylın etibarlı oynatma siyahıları yoxdur!",
"with_playlist": "Pleylistlə paylaş" "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",
"create_group": "Qrup yarat",
"group_name": "Qrup adı",
"cancel": "Ləğv et",
"edit_playlist": "Oynatma siyahısın redaktə et",
"playlist_description": "Oynatma siyahısı təsviri",
"okay": "Oldu",
"chapters_layout_mobile": "Mobildə Bölmələrin Tərtibatı",
"playlist_name": "Oynatma siyahısı adı",
"show_search_suggestions": "Axtarış təkliflərin göstər",
"auto_display_captions": "Titrləri Avtomatik Göstər",
"import_from_json_csv": "JSON/CSV-dən idxal et",
"delete_automatically": "Sonranı avtomatik silin",
"download_frame": "Yükləmə çərçivəsi",
"enable_dearrow": "DeArrow'u Aktivləşdir",
"generate_qrcode": "QR Kodu Yarat"
}, },
"comment": { "comment": {
"pinned_by": "Tərəfindən Sabitləndi {author}", "pinned_by": "{author} tərəfindən sabitlənib",
"disabled": "Şərhlər yükləyici tərəfindən deaktiv edilib.", "disabled": "Şərhlər yükləyici tərəfindən bağlanıb.",
"loading": "Şərhlər yüklənir...", "loading": "Şərhlər yüklənilir...",
"user_disabled": "Şərhlər tənzimləmələrdə deaktiv edilib." "user_disabled": "Şərhlər tənzimləmələrdə qeyri-aktiv edilib."
}, },
"preferences": { "preferences": {
"instance_name": "Nümunə Adı", "instance_name": "Nümunə Adı",
"instance_locations": "Nümunə Məkanları", "instance_locations": "Nümunə Məkanları",
"has_cdn": "CDN varmı?", "has_cdn": "CDN Varmı?",
"registered_users": "Qeydiyyatdan Keçmiş İstifadəçilər", "registered_users": "Qeydiyyatdan Keçmiş İstifadəçilər",
"version": "Versiya", "version": "Versiya",
"up_to_date": "Güncəllənib?", "up_to_date": "Güncəllənib?",
@ -139,28 +164,37 @@
}, },
"login": { "login": {
"username": "İstifadəçi Adı", "username": "İstifadəçi Adı",
"password": "Şifrə" "password": "Şifrə",
"password_confirm": "Parolu təsdiqlə",
"passwords_incorrect": "Parollar uyğunlaşmır!"
}, },
"video": { "video": {
"videos": "Videolar", "videos": "Videolar",
"views": "{views} baxış", "views": "{views} baxış",
"watched": "Baxılıb", "watched": "Baxılıb",
"sponsor_segments": "Sponsorlar Seqmentləri", "sponsor_segments": "Sponsorlar Seqmentləri",
"ratings_disabled": "Reytinqlər Deaktivdir", "ratings_disabled": "Reytinqlər Qeyri-aktivdir",
"chapters": "Bölmələr", "chapters": "Bölmələr",
"live": "{0} Canlı", "live": "{0} Canlı",
"shorts": "Qısa" "shorts": "Qısa",
"all": "Hamısı",
"category": "Kateqoriya",
"chapters_horizontal": "Üfüqi",
"chapters_vertical": "Şaquli",
"license": "Lisenziya",
"visibility": "Görünüş"
}, },
"search": { "search": {
"did_you_mean": "Bunu nəzərdə tutursunuz: {0}?", "did_you_mean": "Bunu nəzərdə tutursunuz: {0}?",
"all": "YouTube: Hamısı", "all": "YouTube: Hamısı",
"videos": "YouTube: Videolar", "videos": "YouTube: Videolar",
"channels": "YouTube: Kanallar", "channels": "YouTube: Kanallar",
"playlists": "YouTube: Pleylistlər", "playlists": "YouTube: Oynatma siyahıları",
"music_songs": "YT Music: Mahnılar", "music_songs": "YT Music: Mahnılar",
"music_videos": "YT Music: Videolar", "music_videos": "YT Music: Videolar",
"music_albums": "YT Music: Albomlar", "music_albums": "YT Music: Albomlar",
"music_playlists": "YT Music: Pleylistlər" "music_playlists": "YT Music: Oynatma Siyahıları",
"music_artists": "YT Music: Sənətkarlar"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Abunə oldu: {0}" "subscribed_channels_count": "Abunə oldu: {0}"
@ -169,10 +203,16 @@
"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 yerli yaddaşında saxlanılır. Brauzer məlumatlarınızın silinməsi onları sıfırlayacaq."
}, },
"info": { "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ı", "page_not_found": "Səhifə tapılmadı",
"copied": "Kopyalandı!", "copied": "Nüsxələndi!",
"cannot_copy": "Kopyalanmır!", "cannot_copy": "Nüsxələnmir!",
"local_storage": "Bu fəaliyyət localStorage tələb edir, məlumat bazası aktivdir?" "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",
"hours": "{amount} saat",
"days": "{amount} gün",
"months": "{amount} ay",
"weeks": "{amount} həftə"
} }
} }

169
src/locales/bg.json Normal file
View File

@ -0,0 +1,169 @@
{
"titles": {
"channels": "Канали",
"login": "Вход",
"register": "Регистрация",
"feed": "Емисия",
"history": "История",
"playlists": "Плейлисти",
"instance": "Инстанция",
"player": "Плейър",
"livestreams": "Излъчвания на живо",
"bookmarks": "Отметки",
"trending": "Набиращи популярност",
"account": "Профил",
"preferences": "Настройки",
"subscriptions": "Абонаменти",
"dearrow": "DeArrow",
"channel_groups": "Канални групи"
},
"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": "Архивиране на настройките",
"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": "Скриване на гледани видеоклипове в Абонаменти",
"enable_dearrow": "Включи DeArrow"
},
"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

@ -25,7 +25,7 @@
"audio_only": "Samo zvuk", "audio_only": "Samo zvuk",
"default_homepage": "Zadana početna stranica", "default_homepage": "Zadana početna stranica",
"loop_this_video": "Stavite ovaj video na ponavljanje", "loop_this_video": "Stavite ovaj video na ponavljanje",
"search": "Pretraga", "search": "Pretraga (Ctrl+K)",
"skip_preview": "Preskočite pregled", "skip_preview": "Preskočite pregled",
"skip_non_music": "Preskočite muziku: sekcija bez muzike", "skip_non_music": "Preskočite muziku: sekcija bez muzike",
"skip_self_promo": "Preskočite neplaćenu/samo-promociju", "skip_self_promo": "Preskočite neplaćenu/samo-promociju",
@ -77,8 +77,6 @@
"minimize_chapters_default": "Smanjite poglavlja po zadanom", "minimize_chapters_default": "Smanjite poglavlja po zadanom",
"show_watch_on_youtube": "Prikaži „Gledaj na YouTube-u” dugme", "show_watch_on_youtube": "Prikaži „Gledaj na YouTube-u” dugme",
"different_auth_instance": "Koristite drugu instancu za autentifikaciju", "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", "with_timecode": "Podijelite s vremenskim kodom",
"piped_link": "Piped poveznica", "piped_link": "Piped poveznica",
"follow_link": "Prati poveznicu", "follow_link": "Prati poveznicu",
@ -102,7 +100,16 @@
"minimize_comments": "Minimizirajte komentare", "minimize_comments": "Minimizirajte komentare",
"delete_account": "Izbriši račun", "delete_account": "Izbriši račun",
"minimize_recommendations_default": "Smanjite preporuke po zadanom", "minimize_recommendations_default": "Smanjite preporuke po zadanom",
"reset_preferences": "Vrati postavke na zadano" "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",
"autoplay_next_countdown": "Zadano odbrojavanje do sljedećeg videa (u sekundama)",
"dismiss": "Odbaci"
}, },
"titles": { "titles": {
"register": "Registrirajte se", "register": "Registrirajte se",
@ -117,7 +124,8 @@
"account": "Račun", "account": "Račun",
"player": "Pokretnik", "player": "Pokretnik",
"channels": "Kanali", "channels": "Kanali",
"livestreams": "Prijenosi uživo" "livestreams": "Prijenosi uživo",
"bookmarks": "Bilješci"
}, },
"search": { "search": {
"music_songs": "YT Music: Pjesme", "music_songs": "YT Music: Pjesme",
@ -154,7 +162,9 @@
"watched": "Pogledano", "watched": "Pogledano",
"videos": "Video zapisi", "videos": "Video zapisi",
"live": "{0} Uživo", "live": "{0} Uživo",
"shorts": "Kratki videi" "shorts": "Kratki videi",
"category": "Kategorija",
"all": "Sve"
}, },
"comment": { "comment": {
"pinned_by": "Prikačeno od {author}", "pinned_by": "Prikačeno od {author}",
@ -170,6 +180,8 @@
"cannot_copy": "Nije moguće kopirati!", "cannot_copy": "Nije moguće kopirati!",
"page_not_found": "Stranica nije pronađena", "page_not_found": "Stranica nije pronađena",
"copied": "Kopirano!", "copied": "Kopirano!",
"local_storage": "Ova radnja zahtijeva lokalno pohranjivanje, jesu li kolačići omogućeni?" "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?",
"next_video_countdown": "Reproduciranje sljedećeg videa u {0}"
} }
} }

View File

@ -12,7 +12,8 @@
"instance": "Instància", "instance": "Instància",
"player": "Reproductor", "player": "Reproductor",
"livestreams": "Retransmissió en directe", "livestreams": "Retransmissió en directe",
"channels": "Canals" "channels": "Canals",
"bookmarks": "Marcadors"
}, },
"actions": { "actions": {
"channel_name_desc": "Nom del Canal (Z-A)", "channel_name_desc": "Nom del Canal (Z-A)",
@ -42,7 +43,7 @@
"enabled_codecs": "Còdecs Habilitats (Múltiple)", "enabled_codecs": "Còdecs Habilitats (Múltiple)",
"instances_list": "Llista d'Instàncies", "instances_list": "Llista d'Instàncies",
"instance_selection": "Selecció d'Instàncies", "instance_selection": "Selecció d'Instàncies",
"show_more": "Mostrar Més", "show_more": "Mostrar més",
"yes": "Sí", "yes": "Sí",
"no": "No", "no": "No",
"export_to_json": "Exportar a JSON", "export_to_json": "Exportar a JSON",
@ -102,8 +103,6 @@
"time_code": "Moment (en segons)", "time_code": "Moment (en segons)",
"copy_link": "Copiar l'enllaç", "copy_link": "Copiar l'enllaç",
"follow_link": "Vés a l'enllaç", "follow_link": "Vés a l'enllaç",
"rename_playlist": "Canviar el nom de la llista de reproducció",
"new_playlist_name": "Nom nou de la llista de reproducció",
"store_search_history": "Emmagatzema l'historial de cerca", "store_search_history": "Emmagatzema l'historial de cerca",
"instance_donations": "Donacions a instàncies", "instance_donations": "Donacions a instàncies",
"hide_watched": "Amaga els vídeos vistos de Continguts", "hide_watched": "Amaga els vídeos vistos de Continguts",
@ -114,7 +113,17 @@
"show_watch_on_youtube": "Mostra el botó \"Veure a Youtube\"", "show_watch_on_youtube": "Mostra el botó \"Veure a Youtube\"",
"reply_count": "{count} respostes", "reply_count": "{count} respostes",
"minimize_comments_default": "Minimitzar els comentaris per defecte", "minimize_comments_default": "Minimitzar els comentaris per defecte",
"minimize_comments": "Minimitza els comentaris" "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": { "comment": {
"pinned_by": "Fixat per {author}", "pinned_by": "Fixat per {author}",
@ -139,7 +148,9 @@
"live": "{0} En Directe", "live": "{0} En Directe",
"videos": "Vídeos", "videos": "Vídeos",
"views": "{views} visualitzacions", "views": "{views} visualitzacions",
"shorts": "Curts" "shorts": "Curts",
"all": "Tot",
"category": "Categoria"
}, },
"search": { "search": {
"did_you_mean": "Volies dir: {0}?", "did_you_mean": "Volies dir: {0}?",
@ -169,6 +180,8 @@
"preferences_note": "Nota: les preferències es desen a l'emmagatzematge local del navegador. Si elimineu les dades del navegador, es restabliran.", "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", "page_not_found": "No s'ha torbat la pàgina",
"copied": "Copiat!", "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,10 @@
"instance": "Instance", "instance": "Instance",
"player": "Přehrávač", "player": "Přehrávač",
"livestreams": "Živé přenosy", "livestreams": "Živé přenosy",
"channels": "Kanály" "channels": "Kanály",
"bookmarks": "Záložky",
"channel_groups": "Skupiny kanálů",
"dearrow": "DeArrow"
}, },
"actions": { "actions": {
"loop_this_video": "Přehrávat video ve smyčce", "loop_this_video": "Přehrávat video ve smyčce",
@ -35,20 +38,20 @@
"audio_only": "Pouze zvuk", "audio_only": "Pouze zvuk",
"default_quality": "Výchozí kvalita", "default_quality": "Výchozí kvalita",
"buffering_goal": "Ukládání do vyrovnávací paměti (v sekundách)", "buffering_goal": "Ukládání do vyrovnávací paměti (v sekundách)",
"country_selection": "Výběr země", "country_selection": "Země",
"default_homepage": "Výchozí domovská stránka", "default_homepage": "Výchozí domovská stránka",
"show_comments": "Zobrazit komentáře", "show_comments": "Zobrazit komentáře",
"minimize_description_default": "Automaticky minimalizovat popis", "minimize_description_default": "Automaticky minimalizovat popis",
"store_watch_history": "Ukládat historii sledování", "store_watch_history": "Ukládat historii sledování",
"language_selection": "Výběr jazyka", "language_selection": "Jazyk",
"instances_list": "Seznam instancí", "instances_list": "Seznam instancí",
"enabled_codecs": "Povolené kodeky (několik)", "enabled_codecs": "Povolené kodeky (několik)",
"instance_selection": "Výběr instance", "instance_selection": "Instance",
"show_more": "Zobrazit více", "show_more": "Zobrazit více",
"yes": "Ano", "yes": "Ano",
"no": "Ne", "no": "Ne",
"export_to_json": "Exportovat do JSON", "export_to_json": "Exportovat do JSON",
"import_from_json": "Importovat z JSON/CSV", "import_from_json": "Importovat z JSON",
"auto_play_next_video": "Automaticky přehrát další video", "auto_play_next_video": "Automaticky přehrát další video",
"donations": "Dary na vývoj", "donations": "Dary na vývoj",
"show_description": "Zobrazit popis", "show_description": "Zobrazit popis",
@ -59,7 +62,7 @@
"disable_lbry": "Zakázat LBRY pro streamování", "disable_lbry": "Zakázat LBRY pro streamování",
"enable_lbry_proxy": "Povolit proxy pro LBRY", "enable_lbry_proxy": "Povolit proxy pro LBRY",
"view_ssl_score": "Zobrazit stav SSL", "view_ssl_score": "Zobrazit stav SSL",
"search": "Vyhledat", "search": "Vyhledávání (Ctrl+K)",
"filter": "Filtr", "filter": "Filtr",
"loading": "Načítání...", "loading": "Načítání...",
"clear_history": "Smazat historii", "clear_history": "Smazat historii",
@ -87,7 +90,7 @@
"minimize_recommendations_default": "Ve výchozím nastavení minimalizovat doporučení", "minimize_recommendations_default": "Ve výchozím nastavení minimalizovat doporučení",
"invalidate_session": "Odhlásit se ze všech zařízení", "invalidate_session": "Odhlásit se ze všech zařízení",
"different_auth_instance": "Použít jinou instanci pro autentizaci", "different_auth_instance": "Použít jinou instanci pro autentizaci",
"instance_auth_selection": "Výběr autentizační instance", "instance_auth_selection": "Autentizační instance",
"clone_playlist": "Duplikovat playlist", "clone_playlist": "Duplikovat playlist",
"clone_playlist_success": "Úspěšně duplikováno!", "clone_playlist_success": "Úspěšně duplikováno!",
"download_as_txt": "Stáhnout jako .txt", "download_as_txt": "Stáhnout jako .txt",
@ -102,8 +105,6 @@
"follow_link": "Otevřít odkaz", "follow_link": "Otevřít odkaz",
"copy_link": "Kopírovat odkaz", "copy_link": "Kopírovat odkaz",
"time_code": "Časový kód (v sekundách)", "time_code": "Časový kód (v sekundách)",
"rename_playlist": "Přejmenovat playlist",
"new_playlist_name": "Nový název playlistu",
"show_chapters": "Kapitoly", "show_chapters": "Kapitoly",
"store_search_history": "Ukládat historii vyhledávání", "store_search_history": "Ukládat historii vyhledávání",
"hide_watched": "Skrýt sledovaná videa v kanálu", "hide_watched": "Skrýt sledovaná videa v kanálu",
@ -117,10 +118,35 @@
"show_watch_on_youtube": "Zobrazit tlačítko Sledovat na YouTube", "show_watch_on_youtube": "Zobrazit tlačítko Sledovat na YouTube",
"minimize_chapters_default": "Ve výchozím nastavení skrýt kapitoly", "minimize_chapters_default": "Ve výchozím nastavení skrýt kapitoly",
"no_valid_playlists": "Soubor neobsahuje platné playlisty!", "no_valid_playlists": "Soubor neobsahuje platné playlisty!",
"with_playlist": "Sdílet s playlistem" "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",
"group_name": "Název skupiny",
"create_group": "Vytvořit skupinu",
"auto_display_captions": "Automatické zobrazení titulků",
"playlist_name": "Název playlistu",
"cancel": "Zrušit",
"edit_playlist": "Upravit playlist",
"playlist_description": "Popis playlistu",
"okay": "Okay",
"show_search_suggestions": "Zobrazit našeptávání ve vyhledávání",
"chapters_layout_mobile": "Rozložení kapitol na mobilu",
"enable_dearrow": "Povolit DeArrow",
"delete_automatically": "Automaticky odstranit po",
"generate_qrcode": "Vygenerovat QR kód",
"import_from_json_csv": "Importovat z JSON/CSV",
"download_frame": "Stáhnout snímek"
}, },
"player": { "player": {
"watch_on": "Sledovat na {0}" "watch_on": "Sledovat na {0}",
"failed": "Akce se nezdařila. Chybový kód {0}, pro více informací viz protokol"
}, },
"comment": { "comment": {
"pinned_by": "Připnuto uživatelem {author}", "pinned_by": "Připnuto uživatelem {author}",
@ -139,7 +165,9 @@
}, },
"login": { "login": {
"username": "Uživatelské jméno", "username": "Uživatelské jméno",
"password": "Heslo" "password": "Heslo",
"password_confirm": "Potvrzení hesla",
"passwords_incorrect": "Hesla se neshodují!"
}, },
"video": { "video": {
"videos": "Videa", "videos": "Videa",
@ -149,7 +177,13 @@
"ratings_disabled": "Hodnocení zakázáno", "ratings_disabled": "Hodnocení zakázáno",
"chapters": "Kapitoly", "chapters": "Kapitoly",
"live": "{0} Živě", "live": "{0} Živě",
"shorts": "Shorts" "shorts": "Shorts",
"all": "Vše",
"category": "Kategorie",
"chapters_horizontal": "Horizontální",
"chapters_vertical": "Vertikální",
"license": "Licence",
"visibility": "Viditelnost"
}, },
"search": { "search": {
"did_you_mean": "Mysleli jste: {0}?", "did_you_mean": "Mysleli jste: {0}?",
@ -160,7 +194,8 @@
"playlists": "YouTube: Playlisty", "playlists": "YouTube: Playlisty",
"music_videos": "YT Music: Videa", "music_videos": "YT Music: Videa",
"music_albums": "YT Music: Alba", "music_albums": "YT Music: Alba",
"music_playlists": "YT Music: Playlisty" "music_playlists": "YT Music: Playlisty",
"music_artists": "YT Music: Umělci"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Přihlášeno k odběru: {0}" "subscribed_channels_count": "Přihlášeno k odběru: {0}"
@ -173,6 +208,12 @@
"page_not_found": "Stránka nenalezena", "page_not_found": "Stránka nenalezena",
"copied": "Zkopírováno!", "copied": "Zkopírováno!",
"cannot_copy": "Nelze zkopírovat!", "cannot_copy": "Nelze zkopírovat!",
"local_storage": "Tato akce vyžaduje localStorage, jsou povoleny cookies?" "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",
"hours": "{amount} hodin",
"days": "{amount} dnů",
"weeks": "{amount} týdnů",
"months": "{amount} měsíců"
} }
} }

View File

@ -1,17 +1,17 @@
{ {
"actions": { "actions": {
"skip_outro": "Abspann überspringen", "skip_outro": "Endkarten und Abspann überspringen",
"skip_non_music": "Musik überspringen: Nicht-Musik-Bereich", "skip_non_music": "Musik: Nicht-Musik-Abschnitte überspringen",
"skip_self_promo": "Unbezahlte Werbung/Eigenwerbung überspringen", "skip_self_promo": "Unbezahlte Werbung/Eigenwerbung überspringen",
"skip_interaction": "Interaktionserinnerung überspringen (Abonnieren)", "skip_interaction": "Interaktionserinnerungen überspringen (Daumen hoch, abonnieren, ...)",
"skip_preview": "Vorschau/Rückschau überspringen", "skip_preview": "Vorschau und Rückblick überspringen",
"instances_list": "Liste der Instanzen", "instances_list": "Liste der Instanzen",
"language_selection": "Sprachauswahl", "language_selection": "Sprache",
"store_watch_history": "Wiedergabeverlauf speichern", "store_watch_history": "Wiedergabeverlauf speichern",
"minimize_description_default": "Beschreibung standardmäßig minimieren", "minimize_description_default": "Beschreibung standardmäßig minimieren",
"show_comments": "Kommentare anzeigen", "show_comments": "Kommentare anzeigen",
"default_homepage": "Standard-Startseite", "default_homepage": "Startseite",
"country_selection": "Länderauswahl", "country_selection": "Land",
"buffering_goal": "Pufferungsziel (in Sekunden)", "buffering_goal": "Pufferungsziel (in Sekunden)",
"default_quality": "Standardqualität", "default_quality": "Standardqualität",
"audio_only": "Nur Audio", "audio_only": "Nur Audio",
@ -20,9 +20,9 @@
"dark": "Dunkel", "dark": "Dunkel",
"auto": "Automatisch", "auto": "Automatisch",
"theme": "Farbschema", "theme": "Farbschema",
"skip_intro": "Pausen-/Intro-Animation überspringen", "skip_intro": "Unterbrechungen und Intro-Animation überspringen",
"skip_sponsors": "Sponsoren überspringen", "skip_sponsors": "Gesponsorte Videoabschnitte überspringen",
"enable_sponsorblock": "Sponsorblock einschalten", "enable_sponsorblock": "SponsorBlock verwenden",
"uses_api_from": "Verwendet die API von ", "uses_api_from": "Verwendet die API von ",
"back": "Zurück", "back": "Zurück",
"channel_name_desc": "Kanalname (Z-A)", "channel_name_desc": "Kanalname (Z-A)",
@ -30,53 +30,51 @@
"least_recent": "Am wenigsten neu", "least_recent": "Am wenigsten neu",
"most_recent": "Am Neuesten", "most_recent": "Am Neuesten",
"sort_by": "Sortieren nach:", "sort_by": "Sortieren nach:",
"view_subscriptions": "Abonnements anzeigen", "view_subscriptions": "Abos anzeigen",
"unsubscribe": "Deabonnieren - {count}", "unsubscribe": "Deabonnieren - {count}",
"subscribe": "Abonnieren - {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", "enable_lbry_proxy": "Proxy für LBRY einschalten",
"disable_lbry": "LBRY für Streaming deaktivieren", "disable_lbry": "LBRY für Streaming deaktivieren",
"instance_selection": "Instanzauswahl", "instance_selection": "Instanz",
"show_description": "Beschreibung anzeigen", "show_description": "Beschreibung anzeigen",
"minimize_description": "Beschreibung minimieren", "minimize_description": "Beschreibung minimieren",
"show_recommendations": "Empfehlungen anzeigen", "show_recommendations": "Empfehlungen anzeigen",
"minimize_recommendations": "Empfehlungen minimieren", "minimize_recommendations": "Empfehlungen minimieren",
"donations": "Spenden für die Entwickler", "donations": "Spenden",
"auto_play_next_video": "Nächstes Video automatisch abspielen", "auto_play_next_video": "Nächstes Video automatisch abspielen",
"loop_this_video": "Dieses Video wiederholen", "loop_this_video": "Dieses Video wiederholen",
"import_from_json": "Aus JSON/CSV importieren", "import_from_json": "Aus JSON importieren",
"export_to_json": "Als JSON exportieren", "export_to_json": "Als JSON exportieren",
"show_more": "Mehr anzeigen", "show_more": "Mehr anzeigen",
"no": "Nein", "no": "Nein",
"yes": "Ja", "yes": "Ja",
"loading": "Wird geladen…", "loading": "Wird geladen…",
"filter": "Filtern", "filter": "Filtern",
"search": "Suchen", "search": "Suchen (Strg+K)",
"view_ssl_score": "SSL-Bewertung anzeigen", "view_ssl_score": "SSL-Bewertung anzeigen",
"clear_history": "Verlauf löschen", "clear_history": "Verlauf löschen",
"hide_replies": "Antworten ausblenden", "hide_replies": "Antworten ausblenden",
"load_more_replies": "Mehr Antworten laden", "load_more_replies": "Mehr Antworten laden",
"skip_highlight": "Höhepunkt überspringen", "skip_highlight": "Höhepunkt überspringen",
"skip_filler_tangent": "Lückenfüller überspringen", "skip_filler_tangent": "Lückenfüller überspringen",
"delete_playlist_confirm": "Diese Wiedergabeliste löschen?", "delete_playlist_confirm": "Diese Playlist löschen?",
"remove_from_playlist": "Aus Wiedergabeliste entfernen", "remove_from_playlist": "Aus Playlist entfernen",
"add_to_playlist": "Zur Wiedergabeliste hinzufügen", "add_to_playlist": "Zur Playlist hinzufügen",
"create_playlist": "Wiedergabeliste erstellen", "create_playlist": "Playlist erstellen",
"delete_playlist_video_confirm": "Video aus Wiedergabeliste entfernen?", "delete_playlist_video_confirm": "Video aus Playlist entfernen?",
"delete_playlist": "Wiedergabeliste löschen", "delete_playlist": "Playlist löschen",
"please_select_playlist": "Bitte wählen Sie eine Wiedergabeliste", "please_select_playlist": "Bitte wähle eine Playlist",
"select_playlist": "Wählen Sie eine Wiedergabeliste", "select_playlist": "Wähle eine Playlist",
"show_markers": "Markierungen auf dem Player anzeigen", "show_markers": "Markierungen auf dem Player anzeigen",
"delete_account": "Konto löschen", "delete_account": "Konto löschen",
"logout": "Von diesem Gerät abmelden", "logout": "Von diesem Gerät abmelden",
"minimize_recommendations_default": "Empfehlungen standardmäßig minimieren", "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", "different_auth_instance": "Eine andere Instanz für die Authentifizierung verwenden",
"instance_auth_selection": "Auswahl der Autentifizierungsinstanz", "instance_auth_selection": "Authentifizierungsinstanz",
"clone_playlist": "Wiedergabeliste klonen", "clone_playlist": "Playlist duplizieren",
"clone_playlist_success": "Erfolgreich geklont!", "clone_playlist_success": "Erfolgreich dupliziert!",
"rename_playlist": "Wiedergabeliste umbenennen",
"new_playlist_name": "Neuer Name der Wiedergabeliste",
"piped_link": "Piped-Link", "piped_link": "Piped-Link",
"download_as_txt": "Als .txt herunterladen", "download_as_txt": "Als .txt herunterladen",
"back_to_home": "Zurück zur Startseite", "back_to_home": "Zurück zur Startseite",
@ -84,7 +82,7 @@
"with_timecode": "Mit Zeitstempel teilen", "with_timecode": "Mit Zeitstempel teilen",
"follow_link": "Link öffnen", "follow_link": "Link öffnen",
"copy_link": "Link kopieren", "copy_link": "Link kopieren",
"time_code": "Zeitstempel (in sekunden)", "time_code": "Zeitstempel (in Sekunden)",
"reset_preferences": "Einstellungen zurücksetzen", "reset_preferences": "Einstellungen zurücksetzen",
"confirm_reset_preferences": "Bist du sicher, dass du deine Einstellungen zurücksetzen möchtest?", "confirm_reset_preferences": "Bist du sicher, dass du deine Einstellungen zurücksetzen möchtest?",
"backup_preferences": "Einstellungen sichern", "backup_preferences": "Einstellungen sichern",
@ -92,45 +90,79 @@
"show_chapters": "Kapitel", "show_chapters": "Kapitel",
"source_code": "Quellcode", "source_code": "Quellcode",
"store_search_history": "Suchverlauf speichern", "store_search_history": "Suchverlauf speichern",
"hide_watched": "Gesehene Videos im Feed ausblenden", "hide_watched": "Gesehene Videos im Abo-Feed ausblenden",
"reply_count": "{count} Antworten", "reply_count": "{count} Antworten",
"instance_donations": "Instanz-Spenden", "instance_donations": "Instanz-Spenden",
"documentation": "Dokumentation", "documentation": "Dokumentation",
"status_page": "Status", "status_page": "Status",
"minimize_chapters_default": "Kapitel standardmäßig minimieren", "minimize_chapters_default": "Kapitel standardmäßig minimieren",
"minimize_comments_default": "Kommentare automatisch minimieren", "minimize_comments_default": "Kommentare standardmäßig minimieren",
"minimize_comments": "Kommentare minimieren", "minimize_comments": "Kommentare minimieren",
"no_valid_playlists": "Die Datei enthält keine gültigen Wiedergabelisten!", "no_valid_playlists": "Die Datei enthält keine gültigen Playlists!",
"show_watch_on_youtube": "Schaltfläche „Auf YouTube ansehen“ anzeigen", "show_watch_on_youtube": "Schaltfläche „Auf YouTube ansehen“ anzeigen",
"with_playlist": "Mit Wiedergabeliste teilen" "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",
"group_name": "Gruppenname",
"create_group": "Gruppe erstellen",
"auto_display_captions": "Untertitel automatisch anzeigen",
"cancel": "Abbrechen",
"okay": "Okay",
"edit_playlist": "Playlist bearbeiten",
"playlist_name": "Name der Playlist",
"playlist_description": "Beschreibung der Playlist",
"show_search_suggestions": "Suchvorschläge anzeigen",
"chapters_layout_mobile": "Kapitel-Layout auf Mobilgeräten",
"delete_automatically": "Automatisch löschen nach",
"enable_dearrow": "DeArrow verwenden",
"generate_qrcode": "QR-Code generieren",
"import_from_json_csv": "Aus JSON/CSV importieren",
"download_frame": "Einzelbild (Frame) downloaden"
}, },
"player": { "player": {
"watch_on": "Auf {0} ansehen" "watch_on": "Auf {0} ansehen",
"failed": "Fehlgeschlagen mit Fehlercode {0}, siehe Protokolle für weitere Informationen"
}, },
"titles": { "titles": {
"history": "Verlauf", "history": "Verlauf",
"preferences": "Einstellungen", "preferences": "Einstellungen",
"feed": "Abonnements", "feed": "Abos",
"register": "Registrieren", "register": "Registrieren",
"login": "Anmelden", "login": "Anmelden",
"trending": "Trends", "trending": "Trends",
"subscriptions": "Abonnements", "subscriptions": "Abos",
"playlists": "Wiedergabelisten", "playlists": "Playlists",
"account": "Konto", "account": "Konto",
"player": "Player", "player": "Player",
"instance": "Instanz", "instance": "Instanz",
"livestreams": "Livestreams", "livestreams": "Livestreams",
"channels": "Kanäle" "channels": "Kanäle",
"bookmarks": "Lesezeichen",
"channel_groups": "Kanalgruppen",
"dearrow": "DeArrow"
}, },
"video": { "video": {
"sponsor_segments": "Sponsoren-Segmente", "sponsor_segments": "Sponsoren-Abschnitte",
"watched": "Angesehen", "watched": "Angesehen",
"views": "{views} Aufrufe", "views": "{views} Aufrufe",
"videos": "Videos", "videos": "Videos",
"ratings_disabled": "Bewertungen deaktiviert", "ratings_disabled": "Bewertungen deaktiviert",
"live": "{0} Live", "live": "{0} Live",
"chapters": "Kapitel", "chapters": "Kapitel",
"shorts": "Shorts" "shorts": "Shorts",
"all": "Alle",
"category": "Kategorie",
"chapters_horizontal": "Horizontal",
"chapters_vertical": "Vertikal",
"license": "Lizenz",
"visibility": "Sichtbarkeit"
}, },
"preferences": { "preferences": {
"ssl_score": "SSL-Bewertung", "ssl_score": "SSL-Bewertung",
@ -145,31 +177,40 @@
"pinned_by": "Angeheftet von {author}", "pinned_by": "Angeheftet von {author}",
"user_disabled": "Kommentare wurden in den Einstellungen deaktiviert.", "user_disabled": "Kommentare wurden in den Einstellungen deaktiviert.",
"disabled": "Kommentare wurden vom Autor deaktiviert.", "disabled": "Kommentare wurden vom Autor deaktiviert.",
"loading": "Kommentare werden geladen …" "loading": "Kommentare werden geladen…"
}, },
"login": { "login": {
"password": "Passwort", "password": "Passwort",
"username": "Anmeldename" "username": "Benutzername",
"password_confirm": "Passwort bestätigen",
"passwords_incorrect": "Passwörter stimmen nicht überein!"
}, },
"search": { "search": {
"did_you_mean": "Hast du gemeint: {0}?", "did_you_mean": "Hast du gemeint: {0}?",
"all": "YouTube: Alle", "all": "YouTube: Alle",
"videos": "YouTube: Videos", "videos": "YouTube: Videos",
"channels": "YouTube: Kanäle", "channels": "YouTube: Kanäle",
"playlists": "YouTube: Wiedergabelisten", "playlists": "YouTube: Playlists",
"music_songs": "YT Music: Lieder", "music_songs": "YT Music: Lieder",
"music_videos": "YT Music: Videos", "music_videos": "YT Music: Videos",
"music_albums": "YT Music: Alben", "music_albums": "YT Music: Alben",
"music_playlists": "YT Music: Wiedergabelisten" "music_playlists": "YT Music: Playlists",
"music_artists": "YT Music: Künstler:innen"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Aboniert bei: {0}" "subscribed_channels_count": "Anzahl Abos: {0}"
}, },
"info": { "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 Einstellungen werden lokal in deinem Browser gespeichert. Wenn du deine Browserdaten löschst, werden auch deine Einstellungen zurückgesetzt.",
"page_not_found": "Seite nicht gefunden", "page_not_found": "Seite nicht gefunden",
"copied": "Kopiert!", "copied": "Kopiert!",
"cannot_copy": "Kopieren nicht möglich!", "cannot_copy": "Kopieren nicht möglich!",
"local_storage": "Diese Aktion erfordert „localStorage“, sind Cookies aktiviert?" "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",
"weeks": "{amount} Woche(n)",
"months": "{amount} Monat(en)",
"hours": "{amount} Stunde(n)",
"days": "{amount} Tag(e)"
} }
} }

View File

@ -12,10 +12,14 @@
"instance": "Instance", "instance": "Instance",
"player": "Player", "player": "Player",
"livestreams": "Livestreams", "livestreams": "Livestreams",
"channels": "Channels" "channels": "Channels",
"bookmarks": "Bookmarks",
"channel_groups": "Channel groups",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "Watch on {0}" "watch_on": "Watch on {0}",
"failed": "Failed with error code {0}, see logs for more info"
}, },
"actions": { "actions": {
"subscribe": "Subscribe - {count}", "subscribe": "Subscribe - {count}",
@ -29,6 +33,8 @@
"back": "Back", "back": "Back",
"uses_api_from": "Uses the API from ", "uses_api_from": "Uses the API from ",
"enable_sponsorblock": "Enable Sponsorblock", "enable_sponsorblock": "Enable Sponsorblock",
"skip_button_only": "Show skip button",
"skip_automatically": "Automatically",
"skip_sponsors": "Skip Sponsors", "skip_sponsors": "Skip Sponsors",
"skip_intro": "Skip Intermission/Intro Animation", "skip_intro": "Skip Intermission/Intro Animation",
"skip_outro": "Skip Endcards/Credits", "skip_outro": "Skip Endcards/Credits",
@ -39,30 +45,36 @@
"skip_highlight": "Skip Highlight", "skip_highlight": "Skip Highlight",
"skip_filler_tangent": "Skip Filler Tangent", "skip_filler_tangent": "Skip Filler Tangent",
"show_markers": "Show Markers on Player", "show_markers": "Show Markers on Player",
"min_segment_length": "Minimum Segment Length (in seconds)",
"skip_segment": "Skip Segment",
"enable_dearrow": "Enable DeArrow",
"theme": "Theme", "theme": "Theme",
"auto": "Auto", "auto": "Auto",
"dark": "Dark", "dark": "Dark",
"light": "Light", "light": "Light",
"autoplay_video": "Autoplay Video", "autoplay_video": "Autoplay Video",
"autoplay_next_countdown": "Default Countdown until next video (in seconds)",
"audio_only": "Audio Only", "audio_only": "Audio Only",
"default_quality": "Default Quality", "default_quality": "Default Quality",
"buffering_goal": "Buffering Goal (in seconds)", "buffering_goal": "Buffering Goal (in seconds)",
"country_selection": "Country Selection", "country_selection": "Country",
"default_homepage": "Default Homepage", "default_homepage": "Default Homepage",
"minimize_comments_default": "Minimize Comments by default", "minimize_comments_default": "Minimize Comments by default",
"minimize_description_default": "Minimize Description by default", "minimize_description_default": "Minimize Description by default",
"store_watch_history": "Store Watch History", "store_watch_history": "Store Watch History",
"language_selection": "Language Selection", "language_selection": "Language",
"instances_list": "Instances List", "instances_list": "Instances List",
"enabled_codecs": "Enabled Codecs (Multiple)", "enabled_codecs": "Enabled Codecs (Multiple)",
"instance_selection": "Instance Selection", "instance_selection": "Instance",
"show_more": "Show More", "show_more": "Show More",
"yes": "Yes", "yes": "Yes",
"no": "No", "no": "No",
"export_to_json": "Export to JSON", "export_to_json": "Export to JSON",
"import_from_json": "Import from JSON/CSV", "import_from_json": "Import from JSON",
"import_from_json_csv": "Import from JSON/CSV",
"loop_this_video": "Loop this Video", "loop_this_video": "Loop this Video",
"auto_play_next_video": "Auto Play next Video", "auto_play_next_video": "Auto Play next Video",
"auto_display_captions": "Auto Display Captions",
"donations": "Development donations", "donations": "Development donations",
"minimize_comments": "Minimize Comments", "minimize_comments": "Minimize Comments",
"show_comments": "Comments", "show_comments": "Comments",
@ -73,7 +85,7 @@
"disable_lbry": "Disable LBRY for Streaming", "disable_lbry": "Disable LBRY for Streaming",
"enable_lbry_proxy": "Enable Proxy for LBRY", "enable_lbry_proxy": "Enable Proxy for LBRY",
"view_ssl_score": "View SSL Score", "view_ssl_score": "View SSL Score",
"search": "Search", "search": "Search (Ctrl+K)",
"filter": "Filter", "filter": "Filter",
"loading": "Loading...", "loading": "Loading...",
"clear_history": "Clear History", "clear_history": "Clear History",
@ -91,10 +103,11 @@
"logout": "Logout from this device", "logout": "Logout from this device",
"minimize_recommendations_default": "Minimize Recommendations by default", "minimize_recommendations_default": "Minimize Recommendations by default",
"minimize_chapters_default": "Minimize Chapters by default", "minimize_chapters_default": "Minimize Chapters by default",
"chapters_layout_mobile": "Chapters Layout On Mobile",
"show_watch_on_youtube": "Show Watch on YouTube button", "show_watch_on_youtube": "Show Watch on YouTube button",
"invalidate_session": "Logout all devices", "invalidate_session": "Logout all devices",
"different_auth_instance": "Use a different instance for authentication", "different_auth_instance": "Use a different instance for authentication",
"instance_auth_selection": "Autentication Instance Selection", "instance_auth_selection": "Authentication Instance",
"clone_playlist": "Clone Playlist", "clone_playlist": "Clone Playlist",
"clone_playlist_success": "Successfully cloned!", "clone_playlist_success": "Successfully cloned!",
"download_as_txt": "Download as .txt", "download_as_txt": "Download as .txt",
@ -103,8 +116,9 @@
"backup_preferences": "Backup preferences", "backup_preferences": "Backup preferences",
"restore_preferences": "Restore preferences", "restore_preferences": "Restore preferences",
"back_to_home": "Back to home", "back_to_home": "Back to home",
"rename_playlist": "Rename", "edit_playlist": "Edit",
"new_playlist_name": "New playlist name", "playlist_name": "Playlist name",
"playlist_description": "Playlist description",
"share": "Share", "share": "Share",
"with_timecode": "Share with time code", "with_timecode": "Share with time code",
"piped_link": "Piped link", "piped_link": "Piped link",
@ -112,7 +126,7 @@
"copy_link": "Copy link", "copy_link": "Copy link",
"time_code": "Time code (in seconds)", "time_code": "Time code (in seconds)",
"show_chapters": "Chapters", "show_chapters": "Chapters",
"store_search_history": "Store Search history", "store_search_history": "Store Search History",
"hide_watched": "Hide watched videos in the feed", "hide_watched": "Hide watched videos in the feed",
"documentation": "Documentation", "documentation": "Documentation",
"status_page": "Status", "status_page": "Status",
@ -120,7 +134,19 @@
"instance_donations": "Instance donations", "instance_donations": "Instance donations",
"reply_count": "{count} replies", "reply_count": "{count} replies",
"no_valid_playlists": "The file doesn't contain valid playlists!", "no_valid_playlists": "The file doesn't contain valid playlists!",
"with_playlist": "Share with playlist" "with_playlist": "Share with playlist",
"bookmark_playlist": "Bookmark",
"playlist_bookmarked": "Bookmarked",
"dismiss": "Dismiss",
"show_less": "Show less",
"create_group": "Create group",
"group_name": "Group name",
"cancel": "Cancel",
"okay": "Okay",
"show_search_suggestions": "Show search suggestions",
"delete_automatically": "Delete automatically after",
"generate_qrcode": "Generate QR Code",
"download_frame": "Download frame"
}, },
"comment": { "comment": {
"pinned_by": "Pinned by {author}", "pinned_by": "Pinned by {author}",
@ -139,7 +165,9 @@
}, },
"login": { "login": {
"username": "Username", "username": "Username",
"password": "Password" "password": "Password",
"password_confirm": "Confirm password",
"passwords_incorrect": "Passwords don't match!"
}, },
"video": { "video": {
"videos": "Videos", "videos": "Videos",
@ -149,7 +177,13 @@
"ratings_disabled": "Ratings Disabled", "ratings_disabled": "Ratings Disabled",
"chapters": "Chapters", "chapters": "Chapters",
"live": "{0} Live", "live": "{0} Live",
"shorts": "Shorts" "shorts": "Shorts",
"all": "All",
"category": "Category",
"license": "License",
"visibility": "Visibility",
"chapters_horizontal": "Horizontal",
"chapters_vertical": "Vertical"
}, },
"search": { "search": {
"did_you_mean": "Did you mean: {0}?", "did_you_mean": "Did you mean: {0}?",
@ -160,7 +194,8 @@
"music_songs": "YT Music: Songs", "music_songs": "YT Music: Songs",
"music_videos": "YT Music: Videos", "music_videos": "YT Music: Videos",
"music_albums": "YT Music: Albums", "music_albums": "YT Music: Albums",
"music_playlists": "YT Music: Playlists" "music_playlists": "YT Music: Playlists",
"music_artists": "YT Music: Artists"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Subscribed to: {0}" "subscribed_channels_count": "Subscribed to: {0}"
@ -170,6 +205,12 @@
"page_not_found": "Page not found", "page_not_found": "Page not found",
"copied": "Copied!", "copied": "Copied!",
"cannot_copy": "Can't copy!", "cannot_copy": "Can't copy!",
"local_storage": "This action requires localStorage, are cookies enabled?" "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",
"hours": "{amount} hour(s)",
"days": "{amount} day(s)",
"weeks": "{amount} week(s)",
"months": "{amount} month(s)"
} }
} }

View File

@ -12,10 +12,14 @@
"player": "Ludilo", "player": "Ludilo",
"instance": "Nodo", "instance": "Nodo",
"channels": "Kanaloj", "channels": "Kanaloj",
"livestreams": "Tujelsendoj" "livestreams": "Tujelsendoj",
"bookmarks": "Legosignoj",
"channel_groups": "Kanalaroj",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "Vidi en {0}" "watch_on": "Vidi en {0}",
"failed": "Fiaskis kun erarkodo {0}, vidu protokolojn por pli da informo"
}, },
"actions": { "actions": {
"subscribe": "Aboni - {count}", "subscribe": "Aboni - {count}",
@ -38,22 +42,21 @@
"light": "Hela", "light": "Hela",
"autoplay_video": "Aŭtomate Ludi Videon", "autoplay_video": "Aŭtomate Ludi Videon",
"audio_only": "Nur Sono", "audio_only": "Nur Sono",
"default_quality": "Defaŭlta Kvalito", "default_quality": "Implicita Kvalito",
"country_selection": "Landa Elekto", "country_selection": "Lando",
"default_homepage": "Defaŭlta Ĉefpaĝo", "default_homepage": "Implicita Ĉefpaĝo",
"show_comments": "Montri Komentojn", "show_comments": "Montri Komentojn",
"language_selection": "Lingva Elekto", "language_selection": "Lingvo",
"donations": "Donacoj por programado", "donations": "Donacoj por programado",
"show_more": "Montri Pli", "show_more": "Montri pli",
"yes": "Jes", "yes": "Jes",
"no": "Ne", "no": "Ne",
"show_chapters": "Sekcioj", "show_chapters": "Sekcioj",
"filter": "Filtri", "filter": "Filtri",
"search": "Serĉi", "search": "Serĉi (Ctrl+K)",
"hide_replies": "Kaŝi Respondojn", "hide_replies": "Kaŝi Respondojn",
"add_to_playlist": "Aldoni al ludlisto", "add_to_playlist": "Aldoni al ludlisto",
"delete_playlist": "Forigi Ludliston", "delete_playlist": "Forigi Ludliston",
"rename_playlist": "Renomi ludliston",
"download_as_txt": "Elŝuti kiel .txt", "download_as_txt": "Elŝuti kiel .txt",
"piped_link": "Piped-ligilo", "piped_link": "Piped-ligilo",
"copy_link": "Kopii ligilon", "copy_link": "Kopii ligilon",
@ -66,7 +69,6 @@
"remove_from_playlist": "Forigi el ludlisto", "remove_from_playlist": "Forigi el ludlisto",
"create_playlist": "Krei Ludliston", "create_playlist": "Krei Ludliston",
"delete_account": "Forigi Konton", "delete_account": "Forigi Konton",
"new_playlist_name": "Nomo de nova ludlisto",
"reply_count": "{count} respondoj", "reply_count": "{count} respondoj",
"load_more_replies": "Ŝargi pli da Respondoj", "load_more_replies": "Ŝargi pli da Respondoj",
"share": "Konigi", "share": "Konigi",
@ -77,13 +79,13 @@
"export_to_json": "Elporti JSON-n", "export_to_json": "Elporti JSON-n",
"loop_this_video": "Ripetadi ĉi tiun Videon", "loop_this_video": "Ripetadi ĉi tiun Videon",
"enable_lbry_proxy": "Ebligi Prokurilon por LBRY", "enable_lbry_proxy": "Ebligi Prokurilon por LBRY",
"import_from_json": "Importi el JSON/CSV", "import_from_json": "Importi el JSON",
"show_description": "Montri Priskribon", "show_description": "Montri Priskribon",
"instances_list": "Listo de Nodoj", "instances_list": "Listo de Nodoj",
"auto_play_next_video": "Aŭtomate Ludi sekvan Videon", "auto_play_next_video": "Aŭtomate Ludi sekvan Videon",
"show_recommendations": "Montri Rekomendojn", "show_recommendations": "Montri Rekomendojn",
"reset_preferences": "Restarigi agordojn", "reset_preferences": "Restarigi agordojn",
"instance_selection": "Noda Elekto", "instance_selection": "Nodo",
"view_ssl_score": "Vidu SSL-Poentaron", "view_ssl_score": "Vidu SSL-Poentaron",
"backup_preferences": "Savkopii agordojn", "backup_preferences": "Savkopii agordojn",
"disable_lbry": "Malebligi LBRY-n por Elsendfluo", "disable_lbry": "Malebligi LBRY-n por Elsendfluo",
@ -91,7 +93,7 @@
"store_search_history": "Konservi Ŝerĉhistorion", "store_search_history": "Konservi Ŝerĉhistorion",
"hide_watched": "Kaŝi viditajn videojn en la fluo", "hide_watched": "Kaŝi viditajn videojn en la fluo",
"minimize_recommendations": "Plejetigi Rekomendojn", "minimize_recommendations": "Plejetigi Rekomendojn",
"instance_auth_selection": "Elekto de Aŭtentokontrola Nodo", "instance_auth_selection": "Aŭtentokontrola nodo",
"restore_preferences": "Restarigi agordojn", "restore_preferences": "Restarigi agordojn",
"status_page": "Stato", "status_page": "Stato",
"please_select_playlist": "Bonvolu elekti ludliston", "please_select_playlist": "Bonvolu elekti ludliston",
@ -113,14 +115,38 @@
"skip_interaction": "Preterpasi Interagan Memorigon (Aboni)", "skip_interaction": "Preterpasi Interagan Memorigon (Aboni)",
"store_watch_history": "Konservi Vidhistorion", "store_watch_history": "Konservi Vidhistorion",
"logout": "Elsaluti el ĉi tiu aparato", "logout": "Elsaluti el ĉi tiu aparato",
"minimize_description_default": "Defaŭlte Plejetigi Priskribon", "minimize_description_default": "Implicite Plejetigi Priskribon",
"minimize_recommendations_default": "Defaŭlte Plejetigi Rekomendojn", "minimize_recommendations_default": "Implicite Plejetigi Rekomendojn",
"minimize_comments_default": "Defaŭlte Plejetigi Komentojn", "minimize_comments_default": "Implicite Plejetigi Komentojn",
"minimize_comments": "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": "Defaŭlte plejetigi ĉapitrojn", "minimize_chapters_default": "Implicite plejetigi ĉapitrojn",
"no_valid_playlists": "La dosiero ne enhavas validajn ludlistojn!", "no_valid_playlists": "La dosiero ne enhavas validajn ludlistojn!",
"with_playlist": "Konigi kun ludlisto" "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)",
"group_name": "Nomo de la aro",
"create_group": "Krei aron",
"auto_display_captions": "Aŭtomate montri subtekstojn",
"playlist_name": "Nomo de la ludlisto",
"edit_playlist": "Redakti ludliston",
"okay": "Bone",
"playlist_description": "Priskribo de la ludlisto",
"cancel": "Nuligi",
"show_search_suggestions": "Montri serĉ-sugestojn",
"chapters_layout_mobile": "Aranĝo de ĉapitroj en poŝtelefono",
"delete_automatically": "Aŭtomate forigi post",
"enable_dearrow": "Ebligi DeArrow",
"generate_qrcode": "Generi QR-kodon",
"import_from_json_csv": "Importi el JSON/CSV",
"download_frame": "Elŝuti bildon"
}, },
"video": { "video": {
"chapters": "Sekcioj", "chapters": "Sekcioj",
@ -128,9 +154,15 @@
"live": "{0} Realtempe", "live": "{0} Realtempe",
"views": "{views} spektoj", "views": "{views} spektoj",
"sponsor_segments": "Sponsoraj Segmentoj", "sponsor_segments": "Sponsoraj Segmentoj",
"watched": "Viditaj", "watched": "Spektita",
"ratings_disabled": "Taksadoj Malebligitaj", "ratings_disabled": "Taksadoj Malebligitaj",
"shorts": "Mallongaj" "shorts": "Mallongaj",
"all": "Ĉiuj",
"category": "Kategorio",
"chapters_horizontal": "Horizontala",
"chapters_vertical": "Vertikala",
"license": "Permesilo",
"visibility": "Videbleco"
}, },
"search": { "search": {
"music_albums": "YT Music: Albumoj", "music_albums": "YT Music: Albumoj",
@ -141,18 +173,27 @@
"music_videos": "YT Music: Videoj", "music_videos": "YT Music: Videoj",
"music_songs": "YT Music: Muzikaĵoj", "music_songs": "YT Music: Muzikaĵoj",
"all": "YouTube: Ĉio", "all": "YouTube: Ĉio",
"did_you_mean": "Ĉu vi volis diri «{0}»?" "did_you_mean": "Ĉu vi volis diri «{0}»?",
"music_artists": "YT Music: Artistoj"
}, },
"info": { "info": {
"copied": "Kopiita!", "copied": "Kopiita!",
"cannot_copy": "Ne povas kopii!", "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.", "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?" "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",
"hours": "{amount} horo(j)",
"days": "{amount} tago(j)",
"weeks": "{amount} semajno(j)",
"months": "{amount} monato(j)"
}, },
"login": { "login": {
"username": "Uzantnomo", "username": "Uzantnomo",
"password": "Pasvorto" "password": "Pasvorto",
"password_confirm": "Konfirmu la pasvorton",
"passwords_incorrect": "Pasvortoj ne kongruas!"
}, },
"preferences": { "preferences": {
"version": "Versio", "version": "Versio",

View File

@ -7,7 +7,13 @@
"ratings_disabled": "Valoraciones Desactivadas", "ratings_disabled": "Valoraciones Desactivadas",
"chapters": "Capítulos", "chapters": "Capítulos",
"live": "{0} Directo", "live": "{0} Directo",
"shorts": "Cortos" "shorts": "Cortos",
"all": "Todos",
"category": "Categoría",
"chapters_horizontal": "Horizontal",
"chapters_vertical": "Vertical",
"license": "Licencia",
"visibility": "Visibilidad"
}, },
"preferences": { "preferences": {
"ssl_score": "Puntuación SSL", "ssl_score": "Puntuación SSL",
@ -32,20 +38,20 @@
"donations": "Donaciones para desarrollo", "donations": "Donaciones para desarrollo",
"auto_play_next_video": "Reproducción automática del siguiente vídeo", "auto_play_next_video": "Reproducción automática del siguiente vídeo",
"loop_this_video": "Poner en bucle este vídeo", "loop_this_video": "Poner en bucle este vídeo",
"import_from_json": "Importar desde JSON/CSV", "import_from_json": "Importar desde JSON",
"export_to_json": "Exportar a JSON", "export_to_json": "Exportar a JSON",
"no": "No", "no": "No",
"yes": "Sí", "yes": "Sí",
"show_more": "Mostrar más", "show_more": "Mostrar más",
"instance_selection": "Selección de instancias", "instance_selection": "Instancia",
"enabled_codecs": "Códecs habilitados (múltiples)", "enabled_codecs": "Códecs habilitados (múltiples)",
"instances_list": "Lista de instancias", "instances_list": "Lista de instancias",
"language_selection": "Selección de lenguajes", "language_selection": "Idioma",
"store_watch_history": "Recordar historial de visualización", "store_watch_history": "Recordar historial de visualización",
"minimize_description_default": "Minimizar la descripción por defecto", "minimize_description_default": "Minimizar la descripción por defecto",
"show_comments": "Mostrar comentarios", "show_comments": "Mostrar comentarios",
"default_homepage": "Página de inicio predeterminada", "default_homepage": "Página de inicio predeterminada",
"country_selection": "Selección de países", "country_selection": "País",
"buffering_goal": "Objetivo de amortiguación (en segundos)", "buffering_goal": "Objetivo de amortiguación (en segundos)",
"default_quality": "Calidad predeterminada", "default_quality": "Calidad predeterminada",
"audio_only": "Sólo audio", "audio_only": "Sólo audio",
@ -74,7 +80,7 @@
"subscribe": "Suscribirme - {count}", "subscribe": "Suscribirme - {count}",
"loading": "Cargando…", "loading": "Cargando…",
"filter": "Filtrar", "filter": "Filtrar",
"search": "Buscar", "search": "Buscar (Ctrl+K)",
"view_ssl_score": "Ver la puntuación SSL", "view_ssl_score": "Ver la puntuación SSL",
"minimize_recommendations": "Minimizar recomendaciones", "minimize_recommendations": "Minimizar recomendaciones",
"show_recommendations": "Mostrar recomendaciones", "show_recommendations": "Mostrar recomendaciones",
@ -89,8 +95,8 @@
"create_playlist": "Crear una lista de reproducción", "create_playlist": "Crear una lista de reproducción",
"add_to_playlist": "Añadir a la lista de reproducción", "add_to_playlist": "Añadir a la lista de reproducción",
"delete_playlist_video_confirm": "¿Eliminar vídeo de lista de reproducción?", "delete_playlist_video_confirm": "¿Eliminar vídeo de lista de reproducción?",
"please_select_playlist": "Seleccione una lista de reproducción", "please_select_playlist": "Por favor, selecciona una lista de reproducción",
"select_playlist": "Seleccione una lista de reproducción", "select_playlist": "Selecciona una lista de reproducción",
"show_markers": "Mostrar Marcadores en Reproductor", "show_markers": "Mostrar Marcadores en Reproductor",
"delete_account": "Eliminar Cuenta", "delete_account": "Eliminar Cuenta",
"different_auth_instance": "Usar una instancia diferente para autenticación", "different_auth_instance": "Usar una instancia diferente para autenticación",
@ -99,10 +105,8 @@
"logout": "Cerrar sesión en este dispositivo", "logout": "Cerrar sesión en este dispositivo",
"minimize_recommendations_default": "Minimizar Recomendaciones por defecto", "minimize_recommendations_default": "Minimizar Recomendaciones por defecto",
"invalidate_session": "Cerrar sesión en todos los dispositivos", "invalidate_session": "Cerrar sesión en todos los dispositivos",
"instance_auth_selection": "Selección de la Instancia de Autentificación", "instance_auth_selection": "Instancia de autenticación",
"download_as_txt": "Descargar como .txt", "download_as_txt": "Descargar como .txt",
"rename_playlist": "Cambiar el nombre de la lista de reproducción",
"new_playlist_name": "Nuevo nombre de la lista de reproducción",
"share": "Compartir", "share": "Compartir",
"with_timecode": "Compartir con código de tiempo", "with_timecode": "Compartir con código de tiempo",
"piped_link": "Enlace de Piped", "piped_link": "Enlace de Piped",
@ -115,7 +119,7 @@
"restore_preferences": "Restablecer las preferencias", "restore_preferences": "Restablecer las preferencias",
"back_to_home": "Volver a la página de inicio", "back_to_home": "Volver a la página de inicio",
"show_chapters": "Capítulos", "show_chapters": "Capítulos",
"store_search_history": "Guardar historial de búsqueda", "store_search_history": "Guardar el historial de las búsquedas",
"source_code": "Código fuente", "source_code": "Código fuente",
"documentation": "Documentación", "documentation": "Documentación",
"instance_donations": "Donaciones para instancia", "instance_donations": "Donaciones para instancia",
@ -127,10 +131,34 @@
"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", "minimize_chapters_default": "Minimiza capítulos por defecto",
"no_valid_playlists": "¡El archivo no contiene listas de reproducción válidas!", "no_valid_playlists": "¡El archivo no contiene listas de reproducción válidas!",
"with_playlist": "Compartir con lista de reproducción" "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": "Descartar",
"group_name": "Nombre del grupo",
"create_group": "Crear grupo",
"auto_display_captions": "Mostrar automáticamente subtítulos",
"edit_playlist": "Editar lista de reproducción",
"okay": "Vale",
"playlist_name": "Nombre de la lista de reproducción",
"playlist_description": "Descripción de la lista de reproducción",
"cancel": "Cancelar",
"show_search_suggestions": "Mostrar sugerencias de búsqueda",
"chapters_layout_mobile": "Disposición de capítulos en móvil",
"delete_automatically": "Borrar automáticamente después de",
"enable_dearrow": "Activar DeArrow",
"generate_qrcode": "Generar código QR",
"import_from_json_csv": "Importar desde JSON/CSV",
"download_frame": "Descargar fotograma"
}, },
"titles": { "titles": {
"feed": "Fuente web", "feed": "Contenido",
"subscriptions": "Suscripciones", "subscriptions": "Suscripciones",
"history": "Historial", "history": "Historial",
"trending": "En tendencias", "trending": "En tendencias",
@ -142,14 +170,20 @@
"instance": "Instancia", "instance": "Instancia",
"player": "Reproductor", "player": "Reproductor",
"livestreams": "Directos", "livestreams": "Directos",
"channels": "Canales" "channels": "Canales",
"bookmarks": "Marcadores",
"channel_groups": "Grupos de canales",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "Ver en {0}" "watch_on": "Ver en {0}",
"failed": "Falló con el código de error {0}, consulta los registros para más información"
}, },
"login": { "login": {
"password": "Contraseña", "password": "Contraseña",
"username": "Nombre de usuario" "username": "Nombre de usuario",
"passwords_incorrect": "¡Las contraseñas no coinciden!",
"password_confirm": "Confirma la contraseña"
}, },
"search": { "search": {
"did_you_mean": "¿Quisiste decir {0}?", "did_you_mean": "¿Quisiste decir {0}?",
@ -160,7 +194,8 @@
"videos": "YouTube: Vídeos", "videos": "YouTube: Vídeos",
"channels": "YouTube: Canales", "channels": "YouTube: Canales",
"playlists": "YouTube: Listas de reproducción", "playlists": "YouTube: Listas de reproducción",
"music_albums": "YT Music: Álbumes" "music_albums": "YT Music: Álbumes",
"music_artists": "YT Music: Artistas"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Suscrito a: {0}" "subscribed_channels_count": "Suscrito a: {0}"
@ -170,6 +205,12 @@
"page_not_found": "Página no encontrada", "page_not_found": "Página no encontrada",
"copied": "¡Copiado!", "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»?" "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",
"hours": "{amount} hora(s)",
"days": "{amount} día(s)",
"weeks": "{amount} semana(s)",
"months": "{amount} mes(es)"
} }
} }

View File

@ -45,7 +45,7 @@
"import_from_json": "وارد کردن از JSON/CSV", "import_from_json": "وارد کردن از JSON/CSV",
"export_to_json": "دادن خروجی با فرمت JSON", "export_to_json": "دادن خروجی با فرمت JSON",
"show_description": "نمایش توضیحات ویدئو", "show_description": "نمایش توضیحات ویدئو",
"donations": "کمک های مالی", "donations": "کمک های مالی برای توسعه",
"clear_history": "تخلیه تاریخچه", "clear_history": "تخلیه تاریخچه",
"loop_this_video": "تکرار شدن این ویدئو", "loop_this_video": "تکرار شدن این ویدئو",
"auto_play_next_video": "پخش خودکار ویدئو بعدی", "auto_play_next_video": "پخش خودکار ویدئو بعدی",
@ -57,16 +57,43 @@
"hide_replies": "پنهان کردن پاسخ ها", "hide_replies": "پنهان کردن پاسخ ها",
"load_more_replies": "بارگذاری پاسخ های بیشتر", "load_more_replies": "بارگذاری پاسخ های بیشتر",
"disable_lbry": "غیرفعال کردن LBRY برای استریم ها", "disable_lbry": "غیرفعال کردن LBRY برای استریم ها",
"enable_lbry_proxy": "فعال کردن پراکسی برای LBRY" "enable_lbry_proxy": "فعال کردن پراکسی برای LBRY",
"delete_playlist": "حذف لیست پخش",
"show_markers": "نشانگر را روی پخش کننده نشان دهد",
"select_playlist": "انتخاب لیست پخش",
"add_to_playlist": "افزودن به لیست پخش",
"create_playlist": "ایجاد لیست پخش",
"delete_playlist_confirm": "این لیست پخش حذف شود؟",
"delete_account": "حذف اکانت",
"logout": "خروج از این دستگاه",
"delete_playlist_video_confirm": "ویدیو از لیست پخش حذف شود؟",
"autoplay_next_countdown": "پیش‌فرض شمارش‌معکوس پخش ویدیوی بعدی ( به ثانیه )",
"please_select_playlist": "انتخاب لیست پخش",
"minimize_comments_default": "کوچک کردن کامنت‌ها به صورت پیش فرض",
"minimize_comments": "کوچک کردن کامنت (نظرات)",
"remove_from_playlist": "حذف از لیست پخش",
"skip_button_only": "دکمه‌ی گذر را نشان بده",
"skip_automatically": "خودکار",
"skip_segment": "گذر از بخش",
"minimize_recommendations_default": "به طور پیش فرض پیشنهادها را حداقل کنید.",
"min_segment_length": "حداقل طول بخش (به ثانیه)"
}, },
"titles": { "titles": {
"history": "سابقه", "history": "تاریخچه",
"preferences": "تنظیمات", "preferences": "تنظیمات",
"feed": "خوراک", "feed": "خوراک",
"register": "ثبت نام", "register": "ثبت نام",
"login": "ورود", "login": "ورود",
"trending": "محبوب", "trending": "محبوب",
"subscriptions": "اشتراک‌ها" "subscriptions": "اشتراک‌ها",
"account": "حساب کاربری",
"instance": "نمونه",
"playlists": "لیست پخش",
"player": "پخش کننده",
"bookmarks": "نشان‌دارها",
"livestreams": "پخش زنده",
"channels": "کانال‌ها",
"channel_groups": "گروه های کانال"
}, },
"player": { "player": {
"watch_on": "تماشا روی {0}" "watch_on": "تماشا روی {0}"
@ -86,7 +113,9 @@
"instance_locations": "محل های سرویس", "instance_locations": "محل های سرویس",
"has_cdn": "CDN دارد؟", "has_cdn": "CDN دارد؟",
"ssl_score": "امتیاز SSL", "ssl_score": "امتیاز SSL",
"instance_name": "نام سرویس" "instance_name": "نام سرویس",
"version": "نسخه",
"registered_users": "کاربران تایید شده"
}, },
"comment": { "comment": {
"pinned_by": "سنجاق شده توسط {author}" "pinned_by": "سنجاق شده توسط {author}"

View File

@ -101,7 +101,6 @@
"show_watch_on_youtube": "Näytä Katso YouTubessa -painike", "show_watch_on_youtube": "Näytä Katso YouTubessa -painike",
"different_auth_instance": "Käytä eri instanssia todennukseen", "different_auth_instance": "Käytä eri instanssia todennukseen",
"download_as_txt": "Lataa .txt-tiedostona", "download_as_txt": "Lataa .txt-tiedostona",
"rename_playlist": "Nimeä soittolista uudelleen",
"show_chapters": "Luvut", "show_chapters": "Luvut",
"minimize_comments": "Minimoi kommentit", "minimize_comments": "Minimoi kommentit",
"minimize_comments_default": "Minimoi kommentit oletusarvoisesti", "minimize_comments_default": "Minimoi kommentit oletusarvoisesti",
@ -118,7 +117,6 @@
"hide_watched": "Piilota katsotut videot syötteessä", "hide_watched": "Piilota katsotut videot syötteessä",
"time_code": "Aikakoodi (sekunteina)", "time_code": "Aikakoodi (sekunteina)",
"follow_link": "Avaa linkki", "follow_link": "Avaa linkki",
"new_playlist_name": "Soittolistan uusi nimi",
"invalidate_session": "Kirjaudu ulos kaikista laitteista", "invalidate_session": "Kirjaudu ulos kaikista laitteista",
"logout": "Kirjaudu ulos tästä laitteesta", "logout": "Kirjaudu ulos tästä laitteesta",
"backup_preferences": "Varmuuskopiointiasetukset", "backup_preferences": "Varmuuskopiointiasetukset",

View File

@ -12,7 +12,9 @@
"instance": "Instance", "instance": "Instance",
"player": "Lecteur", "player": "Lecteur",
"livestreams": "Diffusions en direct", "livestreams": "Diffusions en direct",
"channels": "Chaînes" "channels": "Chaînes",
"bookmarks": "Marque-pages",
"channel_groups": "Groupes de chaînes"
}, },
"actions": { "actions": {
"subscribe": "S'abonner - {count}", "subscribe": "S'abonner - {count}",
@ -66,7 +68,7 @@
"yes": "Oui", "yes": "Oui",
"loading": "Chargement…", "loading": "Chargement…",
"filter": "Filtrer", "filter": "Filtrer",
"search": "Rechercher", "search": "Rechercher (Ctrl+K)",
"view_ssl_score": "Afficher le score SSL", "view_ssl_score": "Afficher le score SSL",
"clear_history": "Effacer l'historique", "clear_history": "Effacer l'historique",
"load_more_replies": "Charger plus de réponses", "load_more_replies": "Charger plus de réponses",
@ -102,8 +104,6 @@
"piped_link": "Lien vers Piped", "piped_link": "Lien vers Piped",
"follow_link": "Ouvrir le lien", "follow_link": "Ouvrir le lien",
"time_code": "Horodatage (en secondes)", "time_code": "Horodatage (en secondes)",
"rename_playlist": "Renommer la liste de lecture",
"new_playlist_name": "Nouveau nom de la liste de lecture",
"show_chapters": "Chapitres", "show_chapters": "Chapitres",
"store_search_history": "Mémoriser l'historique de recherche", "store_search_history": "Mémoriser l'historique de recherche",
"documentation": "Documentation", "documentation": "Documentation",
@ -116,7 +116,27 @@
"minimize_comments": "Minimiser les commentaires", "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", "minimize_chapters_default": "Minimiser les chapitres par défaut",
"no_valid_playlists": "Le fichier ne contient pas de listes de lecture valides !" "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",
"okay": "OK",
"edit_playlist": "Éditer la liste de lecture",
"playlist_name": "Nom de la liste de lecture",
"auto_display_captions": "Afficher sous-titres automatiquement",
"dismiss": "Rejeter",
"cancel": "Annuler",
"playlist_description": "Description de la liste de lecture",
"create_group": "Créer un groupe",
"group_name": "Nom du groupe",
"autoplay_next_countdown": "Temps par défaut avant la prochaine vidéo (en secondes)",
"chapters_layout_mobile": "Format des chapitres sur mobile",
"show_search_suggestions": "Afficher les suggestions de recherche"
}, },
"player": { "player": {
"watch_on": "Regarder sur {0}" "watch_on": "Regarder sur {0}"
@ -129,7 +149,11 @@
"ratings_disabled": "Évaluations désactivées", "ratings_disabled": "Évaluations désactivées",
"chapters": "Chapitres", "chapters": "Chapitres",
"live": "{0} en direct", "live": "{0} en direct",
"shorts": "Courtes" "shorts": "Courtes",
"all": "Tout",
"category": "Catégorie",
"chapters_horizontal": "Horizontal",
"chapters_vertical": "Vertical"
}, },
"preferences": { "preferences": {
"ssl_score": "Score SSL", "ssl_score": "Score SSL",
@ -172,6 +196,8 @@
"page_not_found": "Page non trouvée", "page_not_found": "Page non trouvée",
"copied": "Copié !", "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 ?" "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 ?",
"next_video_countdown": "Lecture de la prochaine vidéo dans {0}s"
} }
} }

47
src/locales/gl.json Normal file
View File

@ -0,0 +1,47 @@
{
"titles": {
"register": "Crear conta",
"feed": "Cronoloxía",
"preferences": "Preferencias",
"history": "Historial",
"trending": "En voga",
"account": "Conta",
"player": "Reprodutor",
"login": "Acceder",
"instance": "Instancia",
"bookmarks": "Marcadores",
"subscriptions": "Subscricións",
"playlists": "Listas",
"livestreams": "En directo",
"channels": "Canles",
"channel_groups": "Grupos de canles"
},
"player": {
"watch_on": "Ver en {0}",
"failed": "Fallou con código do erro {0}, mira o rexistro para máis info"
},
"actions": {
"subscribe": "Subscribirse - {count}",
"sort_by": "Orde por:",
"least_recent": "Máis antigo",
"most_recent": "Máis recente",
"channel_name_asc": "Nome da canle (A-Z)",
"unsubscribe": "Retirar subscrición - {count}",
"view_subscriptions": "Ver Subscricións",
"back": "Volver",
"uses_api_from": "Usa a API desde ",
"enable_sponsorblock": "Activar Sponsoblock",
"skip_button_only": "Mostrar botón omitir",
"skip_automatically": "Automáticamente",
"channel_name_desc": "Nome da canle (Z-A)",
"skip_sponsors": "Omitir Sponsors",
"show_markers": "Mostrar Marcadores no Reprodutor",
"skip_segment": "Omitir Segmento",
"dark": "Escuro",
"min_segment_length": "Lonxitude mínima do segmento (en segundos)",
"theme": "Decorado",
"auto": "Auto",
"light": "Claro",
"autoplay_video": "Reprodución automática"
}
}

View File

@ -12,10 +12,14 @@
"playlists": "רשימות נגינה", "playlists": "רשימות נגינה",
"instance": "עותק", "instance": "עותק",
"livestreams": "שידורים חיים", "livestreams": "שידורים חיים",
"channels": "ערוצים" "channels": "ערוצים",
"bookmarks": "סימניות",
"channel_groups": "קבוצות ערוץ",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "לצפות ב־{0}" "watch_on": "לצפות ב־{0}",
"failed": "חל כשל עם קוד שגיאה {0}, מידע נוסף ביומנים"
}, },
"actions": { "actions": {
"subscribe": "מינוי - {count}", "subscribe": "מינוי - {count}",
@ -43,20 +47,20 @@
"audio_only": "שמע בלבד", "audio_only": "שמע בלבד",
"default_quality": "איכות ברירת מחדל", "default_quality": "איכות ברירת מחדל",
"buffering_goal": "יעד שמירה למטמון (בשניות)", "buffering_goal": "יעד שמירה למטמון (בשניות)",
"country_selection": "בחירת מדינה", "country_selection": "מדינה",
"default_homepage": "עמוד הבית כברירת מחדל", "default_homepage": "עמוד הבית כברירת מחדל",
"show_comments": "הצגת תגובות", "show_comments": "הצגת תגובות",
"minimize_description_default": "מזעור התגובות כברירת מחדל", "minimize_description_default": "מזעור התגובות כברירת מחדל",
"store_watch_history": "שחזור היסטוריית הצפייה", "store_watch_history": "שחזור היסטוריית הצפייה",
"language_selection": "בחירת שפה", "language_selection": "שפה",
"instances_list": "רשימת עותקים", "instances_list": "רשימת עותקים",
"enabled_codecs": "מפענחים פעילים (מגוון)", "enabled_codecs": "מפענחים פעילים (מגוון)",
"instance_selection": "בחירת עותק", "instance_selection": "עותק",
"show_more": "להציג עוד", "show_more": "להציג יותר",
"yes": "כן", "yes": "כן",
"no": "לא", "no": "לא",
"export_to_json": "ייצוא ל־JSON", "export_to_json": "ייצוא ל־JSON",
"import_from_json": "ייבוא מ־JSON/CSV", "import_from_json": "ייבוא מ־JSON",
"show_markers": "הצגת סמנים בנגן", "show_markers": "הצגת סמנים בנגן",
"auto_play_next_video": "לנגן את הסרטון הבא אוטומטית", "auto_play_next_video": "לנגן את הסרטון הבא אוטומטית",
"donations": "תרומות בפיתוח", "donations": "תרומות בפיתוח",
@ -77,7 +81,7 @@
"logout": "יציאה מהחשבון במכשיר הזה", "logout": "יציאה מהחשבון במכשיר הזה",
"minimize_recommendations_default": "מזעור המלצות כברירת מחדל", "minimize_recommendations_default": "מזעור המלצות כברירת מחדל",
"invalidate_session": "להוציא את כל המכשירים מהחשבון", "invalidate_session": "להוציא את כל המכשירים מהחשבון",
"instance_auth_selection": "בחירת עותק אימות", "instance_auth_selection": "עותק אימות",
"clone_playlist": "שכפול רשימת נגינה", "clone_playlist": "שכפול רשימת נגינה",
"clone_playlist_success": "שוכפל בהצלחה!", "clone_playlist_success": "שוכפל בהצלחה!",
"download_as_txt": "הורדה כ־‎.txt", "download_as_txt": "הורדה כ־‎.txt",
@ -99,11 +103,9 @@
"disable_lbry": "השבתת הזרמה עם LBRY", "disable_lbry": "השבתת הזרמה עם LBRY",
"enable_lbry_proxy": "הפעלת מתווך ל־LBRY", "enable_lbry_proxy": "הפעלת מתווך ל־LBRY",
"view_ssl_score": "הצגת דירוג SSL", "view_ssl_score": "הצגת דירוג SSL",
"search": "חיפוש", "search": "חיפוש (Ctrl+K)",
"loop_this_video": "ניגון הסרטון בלולאה", "loop_this_video": "ניגון הסרטון בלולאה",
"minimize_recommendations": "מזעור המלצות", "minimize_recommendations": "מזעור המלצות",
"rename_playlist": "שינוי שם רשימת נגינה",
"new_playlist_name": "שם לרשימת נגינה חדשה",
"show_chapters": "פרקים", "show_chapters": "פרקים",
"skip_intro": "דילוג על הפוגה/הנפשת הקדמה", "skip_intro": "דילוג על הפוגה/הנפשת הקדמה",
"skip_outro": "דילוג על כרטיסי סיום/קרדיטים", "skip_outro": "דילוג על כרטיסי סיום/קרדיטים",
@ -120,7 +122,31 @@
"minimize_chapters_default": "מזעור הפרקים כברירת מחדל", "minimize_chapters_default": "מזעור הפרקים כברירת מחדל",
"show_watch_on_youtube": "הצגת כפתור לצפייה ב־YouTube", "show_watch_on_youtube": "הצגת כפתור לצפייה ב־YouTube",
"no_valid_playlists": "הקובץ לא מכיל רשימות נגינה תקפות!", "no_valid_playlists": "הקובץ לא מכיל רשימות נגינה תקפות!",
"with_playlist": "שיתוף עם רשימת נגינה" "with_playlist": "שיתוף עם רשימת נגינה",
"playlist_bookmarked": "נוסף לסימניות",
"bookmark_playlist": "סימנייה",
"skip_button_only": "הצגת כפתור דילוג",
"min_segment_length": "אורך מקטע מזערי (בשניות)",
"skip_segment": "דילוג על מקטע",
"skip_automatically": "אוטומטית",
"show_less": "להציג פחות",
"autoplay_next_countdown": "ספירה לאחור כברירת מחדל עד לסרטון הבא (בשניות)",
"dismiss": "התעלמות",
"create_group": "יצירת קבוצה",
"group_name": "שם הקבוצה",
"auto_display_captions": "הצגת כתוביות אוטומטית",
"cancel": "ביטול",
"edit_playlist": "עריכת רשימת נגינה",
"playlist_name": "שם רשימת הנגינה",
"playlist_description": "תיאור רשימת הנגינה",
"okay": "אישור",
"show_search_suggestions": "הצגת הצעות חיפוש",
"chapters_layout_mobile": "פריסת פרקים בנייד",
"delete_automatically": "למחוק אוטומטית לאחר",
"enable_dearrow": "הפעלת DeArrow",
"generate_qrcode": "יצירת קוד QR",
"import_from_json_csv": "ייבוא מ־JSON/CSV",
"download_frame": "הורדת תמונית"
}, },
"comment": { "comment": {
"pinned_by": "ננעץ על ידי {author}", "pinned_by": "ננעץ על ידי {author}",
@ -139,7 +165,9 @@
}, },
"login": { "login": {
"username": "שם משתמש", "username": "שם משתמש",
"password": "סיסמה" "password": "סיסמה",
"password_confirm": "אישור סיסמה",
"passwords_incorrect": "הסיסמאות שונות זו מזו!"
}, },
"video": { "video": {
"videos": "סרטונים", "videos": "סרטונים",
@ -149,7 +177,13 @@
"ratings_disabled": "הדירוגים מושבתים", "ratings_disabled": "הדירוגים מושבתים",
"chapters": "פרקים", "chapters": "פרקים",
"live": "{0} בשידור חי", "live": "{0} בשידור חי",
"shorts": "קצרצרים" "shorts": "קצרצרים",
"all": "הכול",
"category": "קטגוריה",
"chapters_horizontal": "אופקית",
"chapters_vertical": "אנכית",
"visibility": "חשיפה",
"license": "רישיון"
}, },
"search": { "search": {
"did_you_mean": "האם התכוונת לביטוי {0}?", "did_you_mean": "האם התכוונת לביטוי {0}?",
@ -160,14 +194,21 @@
"music_songs": "YT Music: שירים", "music_songs": "YT Music: שירים",
"music_videos": "YT Music: סרטונים", "music_videos": "YT Music: סרטונים",
"music_albums": "YT Music: אלבומים", "music_albums": "YT Music: אלבומים",
"music_playlists": "YT Music: רשימות נגינה" "music_playlists": "YT Music: רשימות נגינה",
"music_artists": "YT Music: אומנים"
}, },
"info": { "info": {
"preferences_note": "לתשומת לבך: ההעדפות נשמרות באחסון המקומי של הדפדפן שלך. מחיקת נתוני הדפדפן שלך תאפס אותם.", "preferences_note": "לתשומת לבך: ההעדפות נשמרות באחסון המקומי של הדפדפן שלך. מחיקת נתוני הדפדפן שלך תאפס אותם.",
"page_not_found": "העמוד לא נמצא", "page_not_found": "העמוד לא נמצא",
"copied": "הועתק!", "copied": "הועתק!",
"cannot_copy": "לא ניתן להעתיק!", "cannot_copy": "לא ניתן להעתיק!",
"local_storage": "פעולה זו דורשת אחסון מקומי (localStorage), האם עוגיות פעילות?" "local_storage": "פעולה זו דורשת אחסון מקומי (localStorage), האם עוגיות פעילות?",
"register_no_email_note": "לא מומלץ להשתמש בכתובת דוא״ל כשם משתמש. להמשיך בכל זאת?",
"next_video_countdown": "הסרטון הבא יתנגן בעוד {0} שניות",
"days": "{amount} ימים",
"weeks": "{amount} שבועות",
"months": "{amount} חודשים",
"hours": "{amount} שעות"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "נרשמת אל: {0}" "subscribed_channels_count": "נרשמת אל: {0}"

View File

@ -9,7 +9,10 @@
"feed": "फ़ीड", "feed": "फ़ीड",
"playlists": "प्लेलिस्ट", "playlists": "प्लेलिस्ट",
"livestreams": "लाइव स्ट्रीम", "livestreams": "लाइव स्ट्रीम",
"channels": "चैनल" "channels": "चैनल",
"player": "चालक",
"account": "खाता",
"instance": "इंस्टैंस"
}, },
"actions": { "actions": {
"subscribe": "सदस्यता लें - {count}", "subscribe": "सदस्यता लें - {count}",
@ -17,7 +20,7 @@
"unsubscribe": "सदस्यता ले ली है - {count}", "unsubscribe": "सदस्यता ले ली है - {count}",
"no": "नहीं", "no": "नहीं",
"hide_replies": "जवाब छिपाएं", "hide_replies": "जवाब छिपाएं",
"search": "खोजें", "search": "खोजें (Ctrl+K)",
"loop_this_video": "इस वीडियो को लूप करें", "loop_this_video": "इस वीडियो को लूप करें",
"loading": "लोड हो रहा है...", "loading": "लोड हो रहा है...",
"show_description": "विवरण दिखाएं", "show_description": "विवरण दिखाएं",
@ -38,17 +41,17 @@
"autoplay_video": "ऑटोप्ले वीडियो", "autoplay_video": "ऑटोप्ले वीडियो",
"audio_only": "सिर्फ़ ध्वनि", "audio_only": "सिर्फ़ ध्वनि",
"default_quality": "डिफ़ॉल्ट गुणवत्ता", "default_quality": "डिफ़ॉल्ट गुणवत्ता",
"country_selection": "देश चयन", "country_selection": "देश",
"show_comments": "टिप्पणियाँ दिखाएँ", "show_comments": "टिप्पणियाँ दिखाएँ",
"store_watch_history": "स्टोर देखने का इतिहास", "store_watch_history": "स्टोर देखने का इतिहास",
"language_selection": "भाषा चयन", "language_selection": "भाषा",
"instances_list": "इंस्टेंस सूची", "instances_list": "इंस्टेंस सूची",
"instance_selection": "इंस्टेंस चयन", "instance_selection": "इंस्टेंस",
"show_more": "और दिखाओ", "show_more": "और दिखाओ",
"export_to_json": "JSON में निर्यात करें", "export_to_json": "JSON में निर्यात करें",
"import_from_json": "JSON/CSV से आयात करें", "import_from_json": "JSON/CSV से आयात करें",
"auto_play_next_video": "अगला वीडियो ऑटोप्ले करें", "auto_play_next_video": "अगला वीडियो ऑटोप्ले करें",
"donations": "दान", "donations": "विकास दान",
"minimize_recommendations": "सिफारिशों को कम करें", "minimize_recommendations": "सिफारिशों को कम करें",
"show_recommendations": "सिफारिशें दिखाएं", "show_recommendations": "सिफारिशें दिखाएं",
"disable_lbry": "स्ट्रीमिंग के लिए LBRY अक्षम करें", "disable_lbry": "स्ट्रीमिंग के लिए LBRY अक्षम करें",
@ -59,19 +62,24 @@
"load_more_replies": "और जवाब लोड करें", "load_more_replies": "और जवाब लोड करें",
"enabled_codecs": "सक्षम कोडेक्स (एकाधिक)", "enabled_codecs": "सक्षम कोडेक्स (एकाधिक)",
"buffering_goal": "बफरिंग गोल (सेकंड में)", "buffering_goal": "बफरिंग गोल (सेकंड में)",
"delete_playlist_confirm": "क्या आप वाकई इस प्लेलिस्ट को हटाना चाहते हैं?", "delete_playlist_confirm": "प्लेलिस्ट को मिटाना है?",
"add_to_playlist": "प्लेलिस्ट में जोड़ें", "add_to_playlist": "प्लेलिस्ट में जोड़ें",
"remove_from_playlist": "प्लेलिस्ट से निकाले", "remove_from_playlist": "प्लेलिस्ट से निकाले",
"delete_playlist_video_confirm": "क्या आप वाकई इस प्लेलिस्ट से इस वीडियो को निकालना चाहेंगे?", "delete_playlist_video_confirm": "वीडियो को प्लेलिस्ट से निकालना है?",
"create_playlist": "प्लेलिस्ट बनायें", "create_playlist": "प्लेलिस्ट बनायें",
"select_playlist": "एक प्लेलिस्ट चुनें", "select_playlist": "एक प्लेलिस्ट चुनें",
"please_select_playlist": "कृपया एक प्लेलिस्ट चुनें", "please_select_playlist": "कृपया एक प्लेलिस्ट चुनें",
"delete_playlist": "प्लेलिस्ट हटाएं" "delete_playlist": "प्लेलिस्ट हटाएं",
"enable_sponsorblock": "विज्ञापन प्रतिबंध करें",
"default_homepage": "स्वतः निर्धारित मुख्यपृष्ठ",
"sort_by": "वर्गीकरण:",
"skip_automatically": "स्वतः",
"delete_account": "खाता मिटाएँ"
}, },
"video": { "video": {
"views": "{views} बार देखा गया", "views": "{views} बार देखा गया",
"videos": "वीडियो", "videos": "वीडियो",
"watched": "पहले ही देखा", "watched": "पहले ही देखा हुआ",
"ratings_disabled": "रेटिंग अक्षम", "ratings_disabled": "रेटिंग अक्षम",
"chapters": "चैप्टर", "chapters": "चैप्टर",
"live": "{0} लाइव" "live": "{0} लाइव"

View File

@ -4,10 +4,16 @@
"watched": "Gledano", "watched": "Gledano",
"views": "{views} gledanja", "views": "{views} gledanja",
"videos": "Videa", "videos": "Videa",
"ratings_disabled": "Ocjenjivanje isključeno", "ratings_disabled": "Ocjene su isključene",
"chapters": "Poglavlja", "chapters": "Poglavlja",
"live": "{0} uživo", "live": "{0} uživo",
"shorts": "Kratka videa" "shorts": "Kratka videa",
"all": "Sva",
"category": "Kategorija",
"chapters_horizontal": "Vodoravno",
"chapters_vertical": "Okomito",
"license": "Licenca",
"visibility": "Vidljivost"
}, },
"preferences": { "preferences": {
"ssl_score": "SSL ocjena", "ssl_score": "SSL ocjena",
@ -20,13 +26,13 @@
}, },
"comment": { "comment": {
"pinned_by": "Prikvačio korisnik {author}", "pinned_by": "Prikvačio korisnik {author}",
"disabled": "Prijenosnik onemogućuje komentare.", "disabled": "Prijenosnik je isključio komentare.",
"loading": "Učitavanje komentara...", "loading": "Učitavanje komentara...",
"user_disabled": "Komentari su isključeni u postavkama." "user_disabled": "Komentari su isključeni u postavkama."
}, },
"actions": { "actions": {
"enable_lbry_proxy": "Uključi proxy za LBRY", "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_default": "Standardno sakrij opis",
"minimize_description": "Sakrij opis", "minimize_description": "Sakrij opis",
"show_description": "Prikaži opis", "show_description": "Prikaži opis",
@ -40,14 +46,14 @@
"no": "Ne", "no": "Ne",
"yes": "Da", "yes": "Da",
"show_more": "Prikaži više", "show_more": "Prikaži više",
"instance_selection": "Izbor instance", "instance_selection": "Instanca",
"enabled_codecs": "Uključeni kodeki (višestruki)", "enabled_codecs": "Uključeni kodeki (moguće je odabrati nekoliko kodeka)",
"instances_list": "Popis instanci", "instances_list": "Popis instanci",
"language_selection": "Izbor jezika", "language_selection": "Jezik",
"store_watch_history": "Spremi povijest gledanja", "store_watch_history": "Spremi povijest gledanja",
"show_comments": "Prikaži komentare", "show_comments": "Prikaži komentare",
"default_homepage": "Standardna početna stranica", "default_homepage": "Standardna početna stranica",
"country_selection": "Izbor zemlje", "country_selection": "Zemlja",
"buffering_goal": "Cilj međuspremnika (u sekundama)", "buffering_goal": "Cilj međuspremnika (u sekundama)",
"default_quality": "Standardna kvaliteta", "default_quality": "Standardna kvaliteta",
"audio_only": "Samo zvuk", "audio_only": "Samo zvuk",
@ -69,36 +75,36 @@
"view_subscriptions": "Pogledaj pretplate", "view_subscriptions": "Pogledaj pretplate",
"unsubscribe": "Otkaži pretplatu {count}", "unsubscribe": "Otkaži pretplatu {count}",
"subscribe": "Pretplati se {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_outro": "Preskoči odjavnu špicu",
"skip_intro": "Preskoči pauzu i uvodnu animaciju", "skip_intro": "Preskoči pauzu i uvodnu animaciju",
"skip_sponsors": "Preskoči sponzore", "skip_sponsors": "Preskoči sponzore",
"enable_sponsorblock": "Uključi blok sponsora", "enable_sponsorblock": "Uključi blok sponsora",
"loading": "Učitavanje…", "loading": "Učitavanje…",
"filter": "Filtar", "filter": "Filtar",
"search": "Pretraga", "search": "Pretraga (Ctrl+K)",
"view_ssl_score": "Pogledaj SSL ocjenu", "view_ssl_score": "Pogledaj SSL ocjenu",
"hide_replies": "Sakrij odgovore", "hide_replies": "Sakrij odgovore",
"load_more_replies": "Prikaži više odgovora", "load_more_replies": "Prikaži više odgovora",
"clear_history": "Obriši povijest", "clear_history": "Obriši povijest",
"skip_highlight": "Preskoči isticanje", "skip_highlight": "Preskoči isticanje",
"skip_filler_tangent": "Preskoči nebitne međudijelove", "skip_filler_tangent": "Preskoči prazne umetke",
"delete_playlist_confirm": "Izbrisati ovaj popis snimaka?", "delete_playlist_confirm": "Izbrisati ovu playlistu?",
"remove_from_playlist": "Ukloni iz popisa snimaka", "remove_from_playlist": "Ukloni iz playliste",
"create_playlist": "Stvori popis snimaka", "create_playlist": "Stvori playlistu",
"delete_playlist": "Izbriši popis snimaka", "delete_playlist": "Izbriši playlistu",
"add_to_playlist": "Dodaj u popis snimaka", "add_to_playlist": "Dodaj u playlistu",
"select_playlist": "Odaberi popis snimaka", "select_playlist": "Odaberi playlistu",
"please_select_playlist": "Odaberi popis snimaka", "please_select_playlist": "Odaberi playlistu",
"delete_playlist_video_confirm": "Ukloniti video iz popisa snimaka?", "delete_playlist_video_confirm": "Ukloniti video iz playliste?",
"show_markers": "Prikaži oznake na Pokretaču", "show_markers": "Prikaži oznake na playeru",
"delete_account": "Izbriši račun", "delete_account": "Izbriši račun",
"logout": "Odjavi se s ovog uređaja", "logout": "Odjavi se s ovog uređaja",
"minimize_recommendations_default": "Standardno sakrij preporuke", "minimize_recommendations_default": "Standardno sakrij preporuke",
"invalidate_session": "Odjavi sve uređaje", "invalidate_session": "Odjavi sve uređaje",
"different_auth_instance": "Koristi drugu instancu za autentifikaciju", "different_auth_instance": "Koristi drugu instancu za autentifikaciju",
"instance_auth_selection": "Odabir instance autentifikacije", "instance_auth_selection": "Instanca autentifikacije",
"clone_playlist": "Dupliciraj popis snimaka", "clone_playlist": "Dupliciraj playlistu",
"clone_playlist_success": "Dupliciranje uspjelo!", "clone_playlist_success": "Dupliciranje uspjelo!",
"download_as_txt": "Preuzmi kao .txt", "download_as_txt": "Preuzmi kao .txt",
"reset_preferences": "Resetiraj postavke", "reset_preferences": "Resetiraj postavke",
@ -111,14 +117,12 @@
"confirm_reset_preferences": "Stvarno želiš resetirati tvoje postavke?", "confirm_reset_preferences": "Stvarno želiš resetirati tvoje postavke?",
"backup_preferences": "Spremi sigurnosnu kopiju postavki", "backup_preferences": "Spremi sigurnosnu kopiju postavki",
"with_timecode": "Dijeli s vremenskim kodom", "with_timecode": "Dijeli s vremenskim kodom",
"rename_playlist": "Preimenuj popis snimaka",
"new_playlist_name": "Ime novog popisa snimaka",
"share": "Dijeli", "share": "Dijeli",
"show_chapters": "Poglavlja", "show_chapters": "Poglavlja",
"documentation": "Dokumentacija", "documentation": "Dokumentacija",
"source_code": "Izvorni kod", "source_code": "Izvorni kod",
"instance_donations": "Donacije instance", "instance_donations": "Donacije instance",
"store_search_history": "Spremi povijest pretrage", "store_search_history": "Povijest pretrage trgovine",
"hide_watched": "Sakrij gledana videa u novostima", "hide_watched": "Sakrij gledana videa u novostima",
"status_page": "Stanje", "status_page": "Stanje",
"reply_count": "{count} odgovora", "reply_count": "{count} odgovora",
@ -126,8 +130,30 @@
"minimize_comments": "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", "minimize_chapters_default": "Standardno sakrij poglavlja",
"no_valid_playlists": "Datoteka ne sadrži ispravne popise snimaka!", "no_valid_playlists": "Datoteka ne sadrži ispravne playliste!",
"with_playlist": "Dijeli s popisom snimaka" "with_playlist": "Dijeli s playlistom",
"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",
"create_group": "Stvori grupu",
"group_name": "Ime grupe",
"auto_display_captions": "Automatski prikaži titlove",
"cancel": "Odustani",
"okay": "U redu",
"edit_playlist": "Uredi playlistu",
"playlist_name": "Ime playliste",
"playlist_description": "Opis playliste",
"chapters_layout_mobile": "Raspored poglavlja na mobilnim uređajima",
"show_search_suggestions": "Prikaži prijedloge pretrage",
"delete_automatically": "Automatski izbriši nakon",
"enable_dearrow": "Aktiviraj DeArrow",
"generate_qrcode": "Generiraj QR kod"
}, },
"player": { "player": {
"watch_on": "Gledaj na {0}" "watch_on": "Gledaj na {0}"
@ -140,30 +166,36 @@
"register": "Registracija", "register": "Registracija",
"login": "Prijava", "login": "Prijava",
"trending": "U trendu", "trending": "U trendu",
"playlists": "Popisi snimaka", "playlists": "Playliste",
"account": "Račun", "account": "Račun",
"instance": "Instanca", "instance": "Instanca",
"player": "Pokretač", "player": "Player",
"channels": "Kanali", "channels": "Kanali",
"livestreams": "Prijenosi uživo" "livestreams": "Prijenosi uživo",
"bookmarks": "Zabilješke",
"channel_groups": "Grupe kanala",
"dearrow": "DeArrow"
}, },
"login": { "login": {
"password": "Lozinka", "password": "Lozinka",
"username": "Korisničko ime" "username": "Korisničko ime",
"password_confirm": "Potvrdi lozinku",
"passwords_incorrect": "Lozinke se ne poklapaju!"
}, },
"search": { "search": {
"did_you_mean": "Misliš li: {0}?", "did_you_mean": "Misliš li: {0}?",
"all": "YouTube: Sve", "all": "YouTube: Sve",
"videos": "YouTube: Videa", "videos": "YouTube: Videa",
"channels": "YouTube: Kanali", "channels": "YouTube: Kanali",
"playlists": "YouTube: Popisi snimaka", "playlists": "YouTube: Playliste",
"music_songs": "YT Music: Pjesme", "music_songs": "YT Music: Pjesme",
"music_videos": "YT Music: Videa", "music_videos": "YT Music: Videa",
"music_albums": "YT Music: Albumi", "music_albums": "YT Music: Albumi",
"music_playlists": "YT Music: Popisi snimaka" "music_playlists": "YT Music: Playliste",
"music_artists": "YT Music: Izvođači"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Pretplata na: {0}" "subscribed_channels_count": "Broj pretplata: {0}"
}, },
"information": { "information": {
"preferences_note": "Napomena: postavke se spremaju u lokalno spremište preglednika. Brisanje podataka preglednika resetira postavke." "preferences_note": "Napomena: postavke se spremaju u lokalno spremište preglednika. Brisanje podataka preglednika resetira postavke."
@ -173,6 +205,12 @@
"page_not_found": "Stranica nije pronađena", "page_not_found": "Stranica nije pronađena",
"copied": "Kopirano!", "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?" "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",
"hours": "{amount} h",
"days": "{amount} dan(a)",
"weeks": "{amount} tj",
"months": "{amount} mj"
} }
} }

View File

@ -8,7 +8,11 @@
"subscriptions": "Feliratkozások", "subscriptions": "Feliratkozások",
"playlists": "Lejátszási listák", "playlists": "Lejátszási listák",
"trending": "Felkapott", "trending": "Felkapott",
"account": "Fiók" "account": "Fiók",
"player": "Lejátszó",
"instance": "Szerver",
"livestreams": "Élő adások",
"channels": "Csatornák"
}, },
"actions": { "actions": {
"subscribe": "Feliratkozás - {count}", "subscribe": "Feliratkozás - {count}",
@ -51,7 +55,7 @@
"instance_selection": "Példány kiválasztása", "instance_selection": "Példány kiválasztása",
"skip_filler_tangent": "Témától eltérő töltelék/viccek", "skip_filler_tangent": "Témától eltérő töltelék/viccek",
"loop_this_video": "Videó ismétlése", "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", "minimize_description": "Leírás minimalizálása",
"show_recommendations": "Javaslatok megjelenítése", "show_recommendations": "Javaslatok megjelenítése",
"enable_lbry_proxy": "Proxy engedélyezése a LBRY számára", "enable_lbry_proxy": "Proxy engedélyezése a LBRY számára",
@ -85,7 +89,33 @@
"different_auth_instance": "Másik példány használata a hitelesítéshez", "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", "instance_auth_selection": "Autentikációs példány kiválasztása",
"clone_playlist": "Lejátszási lista klónozá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",
"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",
"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": { "video": {
"ratings_disabled": "Értékelések Letiltva", "ratings_disabled": "Értékelések Letiltva",
@ -129,5 +159,15 @@
"disabled": "A hozzászólásokat a feltöltő letiltotta.", "disabled": "A hozzászólásokat a feltöltő letiltotta.",
"user_disabled": "A beállításoknál a megjegyzések le vannak tiltva.", "user_disabled": "A beállításoknál a megjegyzések le vannak tiltva.",
"loading": "Kommentek betöltése..." "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ó"
} }
} }

190
src/locales/hy.json Normal file
View File

@ -0,0 +1,190 @@
{
"actions": {
"skip_automatically": "Ավտոմատվաց",
"subscribe": "Բաժանորդ - {count}",
"uses_api_from": "Օգտագործում է API -ից ",
"enable_sponsorblock": "Միացնել հովանավորների արգելափակումը",
"skip_intro": "Բաց թողնել ընդմիջում/ներածական անիմացիա",
"skip_outro": "Բաց թողնել վերջնական քարտերը/վարկերը",
"skip_preview": "Բաց թողնել նախադիտումը/վերանայումը",
"skip_interaction": "Բաց թողնել փոխազդեցության հիշեցումը (բաժանորդագրվել)",
"skip_self_promo": "Բաց թողնել չվճարված/ինքնագովազդ",
"skip_highlight": "Բաց թողնել ընդգծումը",
"skip_filler_tangent": "Բաց թողնել ավելորդ շոշափող",
"show_markers": "Ցույց տալ մարկերները նվագարկչի վրա",
"skip_segment": "Բաց թողնել հատվածը",
"theme": "Թեմա",
"auto": "Ավտոմատ",
"dark": "Մութ",
"audio_only": "Միայն աուդիո",
"buffering_goal": "Բուֆերային նպատակ (վայրկյաններով)",
"country_selection": "Երկրի ընտրություն",
"default_homepage": "Կանխադրված գլխավոր էջ",
"minimize_comments_default": "Նվազացնել մեկնաբանությունները",
"channel_name_asc": "Ալիքի անունը (A-Z)",
"unsubscribe": "Չեղարկել",
"view_subscriptions": "Դիտել բաժանորդագրությունները",
"least_recent": "Նվազագույնը վերջին",
"sort_by": "Դասավորել ըստ:",
"most_recent": "Ամենավերջին",
"back": "Ետ",
"channel_name_desc": "Ալիքի անունը (Z-A)",
"autoplay_video": "Տեսանյութի ավտոմատ նվագարկում",
"autoplay_next_countdown": "Կանխադրված հետհաշվարկ մինչև հաջորդ տեսանյութը (վայրկյանների ընթացքում)",
"skip_button_only": "Ցույց տալ բաց թողնել կոճակը",
"skip_sponsors": "Բաց թողնել հովանավորներին",
"min_segment_length": "Սեգմենտի նվազագույն երկարությունը (վայրկյաններով)",
"skip_non_music": "Բաց թողնել երաժշտությունը. ոչ երաժշտական բաժին",
"default_quality": "Կանխադրված որակ",
"light": "Լույս",
"store_watch_history": "Խանութի դիտումների պատմությունը",
"language_selection": "Լեզվի ընտրություն",
"instances_list": "Դեպքերի ցուցակ",
"minimize_description_default": "Նվազեցնել նկարագրությունը",
"enabled_codecs": "Միացված կոդեկներ (բազմաթիվ)",
"show_more": "Ցույց տալ ավելին",
"yes": "Այո՛",
"no": "Ոչ",
"loop_this_video": "Կրկնել այս տեսանյութը",
"auto_play_next_video": "Ավտոմատ նվագարկել հաջորդ տեսանյութը",
"donations": "Զարգացման նվիրատվություններ",
"show_comments": "Ցույց տալ մեկնաբանությունները",
"minimize_description": "Նվազեգնել նկարագրությունը",
"show_description": "Ցույց տալ նկարագրությունը",
"show_recommendations": "Նվազեցնել առաջարկությունները",
"disable_lbry": "Անջատել LBRY-ն եթերի համար",
"enable_lbry_proxy": "Միացնել պռոքսի LBRY-ի համար",
"view_ssl_score": "Դիտեք SSL միավորը",
"search": "Որոնում (Ctrl+K)",
"hide_replies": "Թաքցնել պատասխանները",
"add_to_playlist": "Ավելացնել տեսացանկին",
"remove_from_playlist": "Հեռացնել տեսացանկից",
"delete_playlist_video_confirm": "Հեռացնե՞լ տեսանյութը տեսացանկից:",
"create_playlist": "Ստեղծել տեսացանկ",
"delete_playlist": "Ջնջել տեսացանկը",
"delete_playlist_confirm": "Ջնջե՞լ այս տեսացանկը:",
"please_select_playlist": "Խնդրում ենք ընտրել տեսացանկ",
"delete_account": "Հաշիվը ջնջել",
"logout": "Դուրս գալ այս սարքից",
"minimize_chapters_default": "Նվազեցնել գլուխները",
"show_watch_on_youtube": "Ցույց տալ <<դիտել YouTube-ում >> կոճակը",
"invalidate_session": "Դուրս գալ բոլոր սարքերից",
"instance_auth_selection": "Նույնականացման օրինակի ընտրություն",
"clone_playlist_success": "Հաջողությամբ կլոնավորվեց:",
"download_as_txt": "Ներբեռնեք որպես .txt",
"reset_preferences": "Վերակայել նախապատվությունները",
"confirm_reset_preferences": "Իսկապե՞ս ուզում եք վերակայել ձեր նախապատվությունները:",
"backup_preferences": "Պահուստային նախապատվություններ",
"restore_preferences": "Վերականգնել նախապատվությունները",
"follow_link": "Հետևել հղմանը",
"instance_donations": "Օրինակների նվիրատվություններ",
"reply_count": "{count} պատասխան",
"no_valid_playlists": "Ֆայլը վավեր տեսացանկկեր չի պարունակում:",
"with_playlist": "Կիսվեք տեսացանկով",
"bookmark_playlist": "Էջանիշ",
"playlist_bookmarked": "Էջանշված",
"show_less": "Ցույց տալ ավելի քիչ",
"create_group": "Ստեղծել խումբ",
"minimize_recommendations": "Նվազեցնել առաջարկությունները",
"filter": "Զտել",
"instance_selection": "Օրինակի ընտրություն",
"import_from_json": "Ներմուծում JSON/CSV-ից",
"export_to_json": "Արտահանել JSON",
"minimize_comments": "Նվազագույնի հասցնել մեկնաբանությունները",
"clear_history": "Մաքրել պատմություն",
"loading": "Բեռնվում է...",
"with_timecode": "Կիսվեք ժամանակի կոդով",
"load_more_replies": "Բեռնել ավելի շատ պատասխաններ",
"select_playlist": "Ընտրեք տեսացանկ",
"minimize_recommendations_default": "Նվազեցնել առաջարկությունները",
"clone_playlist": "Կլոնավորել տեսացանկը",
"back_to_home": "Վերադարձ դեպի հիմնական էջ",
"different_auth_instance": "Նույնականացման համար օգտագործեք այլ օրինակ",
"share": "Կիսվել",
"copy_link": "Պատճենել հղումը",
"dismiss": "Հեռացնել",
"piped_link": "Piped հղում",
"time_code": "Ժամանակի կոդը (վայրկյաններով)",
"store_search_history": "Խանութի որոնման պատմություն",
"show_chapters": "Գլուխներ",
"hide_watched": "Թաքցնել դիտված տեսանյութերը լրահոսում",
"status_page": "Կարգավիճակ",
"documentation": "Փաստաթղթեր",
"source_code": "Աղբյուրի կոդը",
"group_name": "Խմբի անվանումը"
},
"titles": {
"trending": "Թրենդային",
"login": "Մտնել",
"register": "Գրանցվել",
"feed": "Սկիզբ",
"preferences": "Նախապատվություններ",
"history": "Պատմություն",
"subscriptions": "Հետևում եմ",
"playlists": "Տեսացանկեր",
"account": "Հաշիվ",
"instance": "Օրինակ",
"player": "նվագարկիչ",
"livestreams": "Ուղիղ Եթերներ",
"channels": "Ալիքներ",
"bookmarks": "Էջանիշեր",
"channel_groups": "Ալիքի խմբեր"
},
"player": {
"watch_on": "Դիտեք {0}-ով"
},
"preferences": {
"registered_users": "Գրանցված օգտվողներ",
"version": "Տարբերակ",
"up_to_date": "Մինչ օրս",
"ssl_score": "SSL միավոր",
"instance_locations": "Օրինակների տեղադրություններ",
"instance_name": "Օրինակի անվանումը",
"has_cdn": "Ունի CDN:"
},
"login": {
"username": "Օգտագործողի անունը",
"password": "Գաղտնաբառ"
},
"video": {
"videos": "Տեսանյութեր",
"views": "{views} դիտում",
"watched": "Դիտվաց",
"sponsor_segments": "Հովանավորների հատվածներ",
"ratings_disabled": "Վարկանիշներն անջատված են",
"chapters": "Գլուխներ",
"live": "{0} Եթեր",
"shorts": "Shorts",
"all": "Բոլորը",
"category": "Կարգ"
},
"search": {
"did_you_mean": "Դուք նկատի ունեիք՝ {0}:",
"all": "YouTube: Բոլորը",
"videos": "YouTube: Տեսանյութեր",
"channels": "YouTube: Ալիքներ",
"playlists": "YouTube՝ տեսացանկ",
"music_songs": "YT Երաժշտություն. երգեր",
"music_videos": "YT Երաժշտություն. Տեսանյութեր",
"music_albums": "YT Երաժշտություն. Ալբոմներ",
"music_playlists": "YT Երաժշտություն. տեսացանկ"
},
"subscriptions": {
"subscribed_channels_count": "Բաժանորդագրված է՝ {0}"
},
"comment": {
"loading": "Մեկնաբանությունների բեռնում...",
"pinned_by": "Ամրացված է {author}-ի կողմից",
"disabled": "Մեկնաբանություններն անջատված են վերբեռնողի կողմից:",
"user_disabled": "Մեկնաբանություններն անջատված են կարգավորումներում:"
},
"info": {
"preferences_note": "Նշում. նախապատվությունները պահվում են ձեր բրաուզերի տեղական պահեստում: Ձեր բրաուզերի տվյալները ջնջելով դրանք կվերակայվեն:",
"page_not_found": "Էջը չի գտնվել",
"copied": "Պատճենվել է",
"cannot_copy": "Հնարավոր չէ պատճենել:",
"local_storage": "Այս գործողության համար պահանջվում է տեղական պահեստ, քուքի ֆայլերը միացվա՞ծ են:",
"register_no_email_note": "Էլեկտրոնային փոստի օգտագործումը որպես օգտվողի անուն խորհուրդ չի տրվում: Շարունակե՞լ, այնուամենայնիվ:",
"next_video_countdown": "Հաջորդ տեսանյութը նվագարկվում է {0} վրկ-ում"
}
}

View File

@ -12,10 +12,14 @@
"account": "Akun", "account": "Akun",
"player": "Pemain", "player": "Pemain",
"livestreams": "Siaran Langsung", "livestreams": "Siaran Langsung",
"channels": "Saluran" "channels": "Saluran",
"bookmarks": "Markah",
"channel_groups": "Grup saluran",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "Tonton di {0}" "watch_on": "Tonton di {0}",
"failed": "Gagal dengan kode kesalahan {0}, lihat catatan untuk info lebih lanjut"
}, },
"actions": { "actions": {
"subscribe": "Berlangganan - {count}", "subscribe": "Berlangganan - {count}",
@ -41,18 +45,18 @@
"audio_only": "Audio Saja", "audio_only": "Audio Saja",
"default_quality": "Kualitas Bawaan", "default_quality": "Kualitas Bawaan",
"buffering_goal": "Tujuan Buffering (dalam detik)", "buffering_goal": "Tujuan Buffering (dalam detik)",
"country_selection": "Pemilihan Negara", "country_selection": "Negara",
"default_homepage": "Halaman Beranda Bawaan", "default_homepage": "Halaman Beranda Bawaan",
"show_comments": "Tampilkan Komentar", "show_comments": "Tampilkan Komentar",
"minimize_description_default": "Kecilkan Deskripsi secara default", "minimize_description_default": "Kecilkan Deskripsi secara default",
"language_selection": "Pemilihan Bahasa", "language_selection": "Bahasa",
"instances_list": "Daftar Instansi", "instances_list": "Daftar Instansi",
"enabled_codecs": "Kodek yang Diaktifkan (Beberapa)", "enabled_codecs": "Kodek yang Diaktifkan (Beberapa)",
"instance_selection": "Pemilihan Instansi", "instance_selection": "Instansi",
"show_more": "Tampilkan Lebih Banyak", "show_more": "Tampilkan Lebih Banyak",
"yes": "Iya", "yes": "Iya",
"no": "Tidak", "no": "Tidak",
"import_from_json": "Impor dari JSON/CSV", "import_from_json": "Impor dari JSON",
"loop_this_video": "Ulangi Video ini", "loop_this_video": "Ulangi Video ini",
"auto_play_next_video": "Mainkan video berikutnya secara otomatis", "auto_play_next_video": "Mainkan video berikutnya secara otomatis",
"donations": "Donasi pengembangan", "donations": "Donasi pengembangan",
@ -63,7 +67,7 @@
"disable_lbry": "Nonaktifkan LBRY untuk Streaming", "disable_lbry": "Nonaktifkan LBRY untuk Streaming",
"enable_lbry_proxy": "Aktifkan Proksi untuk LBRY", "enable_lbry_proxy": "Aktifkan Proksi untuk LBRY",
"view_ssl_score": "Tampilkan Skor SSL", "view_ssl_score": "Tampilkan Skor SSL",
"search": "Telusuri", "search": "Telusuri (Ctrl+K)",
"filter": "Saring", "filter": "Saring",
"loading": "Memuat...", "loading": "Memuat...",
"clear_history": "Hapus Riwayat", "clear_history": "Hapus Riwayat",
@ -89,7 +93,7 @@
"logout": "Keluar dari perangkat ini", "logout": "Keluar dari perangkat ini",
"minimize_recommendations_default": "Kecilkan Rekomendasi secara bawaan", "minimize_recommendations_default": "Kecilkan Rekomendasi secara bawaan",
"invalidate_session": "Keluarkan semua perangkat", "invalidate_session": "Keluarkan semua perangkat",
"instance_auth_selection": "Pemilihan Instansi Otentikasi", "instance_auth_selection": "Instance Otentikasi",
"different_auth_instance": "Gunakan instansi lain untuk otentikasi", "different_auth_instance": "Gunakan instansi lain untuk otentikasi",
"clone_playlist_success": "Berhasil disalin!", "clone_playlist_success": "Berhasil disalin!",
"clone_playlist": "Salin Daftar Putar", "clone_playlist": "Salin Daftar Putar",
@ -98,8 +102,6 @@
"restore_preferences": "Pulihkan preferensi", "restore_preferences": "Pulihkan preferensi",
"confirm_reset_preferences": "Apakah Anda yakin ingin mengatur ulang preferensi Anda?", "confirm_reset_preferences": "Apakah Anda yakin ingin mengatur ulang preferensi Anda?",
"backup_preferences": "Cadangkan preferensi", "backup_preferences": "Cadangkan preferensi",
"rename_playlist": "Ubah nama daftar putar",
"new_playlist_name": "Nama daftar putar baru",
"share": "Bagikan", "share": "Bagikan",
"with_timecode": "Bagikan dengan kode waktu", "with_timecode": "Bagikan dengan kode waktu",
"piped_link": "Tautan Piped", "piped_link": "Tautan Piped",
@ -120,7 +122,31 @@
"show_watch_on_youtube": "Tampilkan tombol Tonton di YouTube", "show_watch_on_youtube": "Tampilkan tombol Tonton di YouTube",
"minimize_chapters_default": "Kecilkan Bab secara bawaan", "minimize_chapters_default": "Kecilkan Bab secara bawaan",
"no_valid_playlists": "Berkas ini tidak berisi daftar putar yang valid!", "no_valid_playlists": "Berkas ini tidak berisi daftar putar yang valid!",
"with_playlist": "Bagikan dengan daftar putar" "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",
"create_group": "Buat grup",
"group_name": "Nama grup",
"auto_display_captions": "Tampilkan Takarir Secara Otomatis",
"cancel": "Batal",
"playlist_description": "Deskripsi daftar putar",
"edit_playlist": "Sunting daftar putar",
"playlist_name": "Nama daftar putar",
"okay": "Oke",
"show_search_suggestions": "Tampilkan saran pencarian",
"chapters_layout_mobile": "Tata Letak Bab di Ponsel",
"delete_automatically": "Hapus secara otomatis setelah",
"enable_dearrow": "Aktifkan DeArrow",
"generate_qrcode": "Buat Kode QR",
"import_from_json_csv": "Impor dari JSON/CSV",
"download_frame": "Unduh bingkai"
}, },
"comment": { "comment": {
"pinned_by": "Dipasangi pin oleh {author}", "pinned_by": "Dipasangi pin oleh {author}",
@ -139,7 +165,9 @@
}, },
"login": { "login": {
"username": "Nama Pengguna", "username": "Nama Pengguna",
"password": "Kata Sandi" "password": "Kata Sandi",
"password_confirm": "Konfirmasi kata sandi",
"passwords_incorrect": "Kata sandi tidak cocok!"
}, },
"video": { "video": {
"videos": "Video", "videos": "Video",
@ -149,7 +177,13 @@
"ratings_disabled": "Penilaian Dinonaktifkan", "ratings_disabled": "Penilaian Dinonaktifkan",
"chapters": "Bagian", "chapters": "Bagian",
"live": "{0} Langsung", "live": "{0} Langsung",
"shorts": "Shorts" "shorts": "Shorts",
"all": "Semua",
"category": "Kategori",
"chapters_horizontal": "Horisontal",
"chapters_vertical": "Vertikal",
"license": "Lisensi",
"visibility": "Visibilitas"
}, },
"search": { "search": {
"did_you_mean": "Apakah Anda bermaksud: {0}?", "did_you_mean": "Apakah Anda bermaksud: {0}?",
@ -160,7 +194,8 @@
"music_videos": "YT Music: Video", "music_videos": "YT Music: Video",
"music_albums": "YT Music: Album", "music_albums": "YT Music: Album",
"music_playlists": "YT Music: Daftar Putar", "music_playlists": "YT Music: Daftar Putar",
"all": "YouTube: Semua" "all": "YouTube: Semua",
"music_artists": "YT Music: Artis"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Berlangganan ke: {0}" "subscribed_channels_count": "Berlangganan ke: {0}"
@ -173,6 +208,12 @@
"preferences_note": "Catatan: preferensi disimpan dalam penyimpanan lokal peramban Anda. Menghapus data peramban Anda akan mengatur ulang.", "preferences_note": "Catatan: preferensi disimpan dalam penyimpanan lokal peramban Anda. Menghapus data peramban Anda akan mengatur ulang.",
"copied": "Disalin!", "copied": "Disalin!",
"cannot_copy": "Tidak dapat menyalin!", "cannot_copy": "Tidak dapat menyalin!",
"local_storage": "Tindakan ini membutuhkan localStorage, apakah kuki diaktifkan?" "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",
"weeks": "{amount} minggu",
"hours": "{amount} jam",
"days": "{amount} hari",
"months": "{amount} bulan"
} }
} }

View File

@ -12,7 +12,9 @@
"account": "Reikningur", "account": "Reikningur",
"instance": "Tilvik", "instance": "Tilvik",
"livestreams": "Útsendingar í beinni", "livestreams": "Útsendingar í beinni",
"channels": "Rásir" "channels": "Rásir",
"bookmarks": "Bókamerki",
"channel_groups": "Rásarhópar"
}, },
"actions": { "actions": {
"sort_by": "Raða eftir:", "sort_by": "Raða eftir:",
@ -94,8 +96,6 @@
"instance_donations": "Framlög til netþjóns", "instance_donations": "Framlög til netþjóns",
"status_page": "Staða", "status_page": "Staða",
"source_code": "Frumkóði", "source_code": "Frumkóði",
"rename_playlist": "Endurnefna spilunarlista",
"new_playlist_name": "Nýtt heiti spilunarlista",
"share": "Deila", "share": "Deila",
"with_timecode": "Deilа með tímakóða", "with_timecode": "Deilа með tímakóða",
"piped_link": "Hlekkur Piped", "piped_link": "Hlekkur Piped",

View File

@ -1,12 +1,12 @@
{ {
"actions": { "actions": {
"instances_list": "Elenco delle istanze", "instances_list": "Elenco delle istanze",
"language_selection": "Selezione della lingua", "language_selection": "Lingua",
"store_watch_history": "Conserva la cronologia di visualizzazione", "store_watch_history": "Conserva la cronologia di visualizzazione",
"minimize_description_default": "Minimizza la descrizione per impostazione predefinita", "minimize_description_default": "Minimizza la descrizione per impostazione predefinita",
"show_comments": "Mostra i commenti", "show_comments": "Mostra i commenti",
"default_homepage": "Pagina iniziale predefinita", "default_homepage": "Pagina iniziale predefinita",
"country_selection": "Selezione del paese", "country_selection": "Paese",
"buffering_goal": "Obiettivo di buffering (in secondi)", "buffering_goal": "Obiettivo di buffering (in secondi)",
"default_quality": "Qualità predefinita", "default_quality": "Qualità predefinita",
"audio_only": "Solo audio", "audio_only": "Solo audio",
@ -37,7 +37,7 @@
"enable_lbry_proxy": "Abilita il proxy per LBRY", "enable_lbry_proxy": "Abilita il proxy per LBRY",
"disable_lbry": "Disabilita LBRY per lo streaming", "disable_lbry": "Disabilita LBRY per lo streaming",
"show_description": "Mostra la descrizione", "show_description": "Mostra la descrizione",
"minimize_description": "Minimizza la descrizione per impostazione predefinita", "minimize_description": "Minimizza descrizione",
"minimize_recommendations": "Minimizza consigli", "minimize_recommendations": "Minimizza consigli",
"show_recommendations": "Mostra consigli", "show_recommendations": "Mostra consigli",
"donations": "Donazioni per lo sviluppo", "donations": "Donazioni per lo sviluppo",
@ -48,10 +48,10 @@
"no": "No", "no": "No",
"yes": "Sì", "yes": "Sì",
"show_more": "Mostra di più", "show_more": "Mostra di più",
"instance_selection": "Selezione dell'istanza", "instance_selection": "Istanza",
"loading": "Caricamento…", "loading": "Caricamento…",
"filter": "Filtra", "filter": "Filtra",
"search": "Cerca", "search": "Cerca (Ctrl+K)",
"view_ssl_score": "Visualizza il punteggio SSL", "view_ssl_score": "Visualizza il punteggio SSL",
"clear_history": "Cancella la cronologia", "clear_history": "Cancella la cronologia",
"load_more_replies": "Carica più risposte", "load_more_replies": "Carica più risposte",
@ -72,7 +72,7 @@
"minimize_recommendations_default": "Riduci al minimo le raccomandazioni per impostazione predefinita", "minimize_recommendations_default": "Riduci al minimo le raccomandazioni per impostazione predefinita",
"different_auth_instance": "Usa un'istanza diversa per l'autenticazione", "different_auth_instance": "Usa un'istanza diversa per l'autenticazione",
"invalidate_session": "Disconnetti su tutti i dispositivi", "invalidate_session": "Disconnetti su tutti i dispositivi",
"instance_auth_selection": "Selezione dell'istanza di autenticazione", "instance_auth_selection": "Istanza di autenticazione",
"clone_playlist_success": "Clonato con successo!", "clone_playlist_success": "Clonato con successo!",
"clone_playlist": "Clona la playlist", "clone_playlist": "Clona la playlist",
"download_as_txt": "Scarica come .txt", "download_as_txt": "Scarica come .txt",
@ -87,8 +87,6 @@
"time_code": "Tempo (in secondi)", "time_code": "Tempo (in secondi)",
"follow_link": "Apri il collegamento", "follow_link": "Apri il collegamento",
"with_timecode": "Condividi con marca temporale", "with_timecode": "Condividi con marca temporale",
"new_playlist_name": "Nuovo nome dalla playlist",
"rename_playlist": "Rinomina la playlist",
"show_chapters": "Capitoli", "show_chapters": "Capitoli",
"store_search_history": "Memorizza la cronologia delle ricerche", "store_search_history": "Memorizza la cronologia delle ricerche",
"status_page": "Stato", "status_page": "Stato",
@ -101,7 +99,25 @@
"minimize_comments_default": "Minimizza i commenti per impostazione predefinita", "minimize_comments_default": "Minimizza i commenti per impostazione predefinita",
"minimize_comments": "Minimizza i commenti", "minimize_comments": "Minimizza i commenti",
"minimize_chapters_default": "Minimizza i capitoli per impostazione predefinita", "minimize_chapters_default": "Minimizza i capitoli per impostazione predefinita",
"no_valid_playlists": "Il file non contiene playlist valide!" "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)",
"cancel": "Annulla",
"okay": "Va bene",
"edit_playlist": "Modifica playlist",
"playlist_name": "Nome playilist",
"playlist_description": "Descrizione playlist",
"group_name": "Nome gruppo",
"create_group": "Crea gruppo",
"show_search_suggestions": "Mostra suggerimenti di ricerca",
"dismiss": "Chiudi"
}, },
"player": { "player": {
"watch_on": "Guarda su {0}" "watch_on": "Guarda su {0}"
@ -119,7 +135,9 @@
"instance": "Istanza", "instance": "Istanza",
"player": "Riproduttore", "player": "Riproduttore",
"livestreams": "Streaming live", "livestreams": "Streaming live",
"channels": "Canali" "channels": "Canali",
"bookmarks": "Segnalibri",
"dearrow": "DeArrow"
}, },
"video": { "video": {
"sponsor_segments": "Segmenti sponsor", "sponsor_segments": "Segmenti sponsor",
@ -129,7 +147,12 @@
"ratings_disabled": "Valutazioni disabilitate", "ratings_disabled": "Valutazioni disabilitate",
"live": "{0} Diretta", "live": "{0} Diretta",
"chapters": "Capitoli", "chapters": "Capitoli",
"shorts": "Short" "shorts": "Short",
"all": "Tutti",
"category": "Categoria",
"chapters_horizontal": "Orizzontale",
"chapters_vertical": "Verticale",
"license": "Licenza"
}, },
"preferences": { "preferences": {
"ssl_score": "Valutazione SSL", "ssl_score": "Valutazione SSL",
@ -159,7 +182,8 @@
"channels": "YouTube: Canali", "channels": "YouTube: Canali",
"music_songs": "YT Music: Canzoni", "music_songs": "YT Music: Canzoni",
"all": "YouTube: Tutto", "all": "YouTube: Tutto",
"videos": "YouTube: Video" "videos": "YouTube: Video",
"music_artists": "YT Music: Artisti"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Abbonato a: {0}" "subscribed_channels_count": "Abbonato a: {0}"
@ -172,6 +196,8 @@
"preferences_note": "Nota: le preferenze sono salvate nella memoria locale del tuo browser. L'eliminazione dei dati del tuo browser le ripristinerà.", "preferences_note": "Nota: le preferenze sono salvate nella memoria locale del tuo browser. L'eliminazione dei dati del tuo browser le ripristinerà.",
"copied": "Copiato!", "copied": "Copiato!",
"cannot_copy": "Impossibile copiare!", "cannot_copy": "Impossibile copiare!",
"local_storage": "Questa azione richiede localStorage, i cookie sono abilitati?" "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

@ -12,10 +12,13 @@
"player": "プレイヤー", "player": "プレイヤー",
"instance": "インスタンス", "instance": "インスタンス",
"channels": "チャンネル", "channels": "チャンネル",
"livestreams": "ライブ配信" "livestreams": "ライブ配信",
"bookmarks": "ブックマーク",
"channel_groups": "グループ",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "{0}で見る" "watch_on": "{0}で視聴"
}, },
"actions": { "actions": {
"subscribe": "チャンネル登録 - {count}", "subscribe": "チャンネル登録 - {count}",
@ -27,37 +30,37 @@
"channel_name_asc": "チャンネル名 (AからZ)", "channel_name_asc": "チャンネル名 (AからZ)",
"channel_name_desc": "チャンネル名 (ZからA)", "channel_name_desc": "チャンネル名 (ZからA)",
"back": "戻る", "back": "戻る",
"uses_api_from": "API使用元 ", "uses_api_from": "API 提供元 ",
"enable_sponsorblock": "SponsorBlockを有効化", "enable_sponsorblock": "SponsorBlock を使用",
"skip_sponsors": "広告をスキップ", "skip_sponsors": "広告をスキップ",
"skip_intro": "インターミッション/イントロアニメーションをスキップする", "skip_intro": "合間/導入アニメをスキップ",
"skip_outro": "エンドカード/クレジットをスキップする", "skip_outro": "終了シーン/クレジットをスキップ",
"skip_preview": "プレビュー/要約をスキップ", "skip_preview": "予告/あらすじをスキップ",
"skip_interaction": "インタラクションリマインダーをスキップする (チャンネル登録)", "skip_interaction": "登録など操作を頼む自己宣伝をスキップ",
"skip_self_promo": "無償/自己プロモーションをスキップ", "skip_self_promo": "無償または自己の宣伝をスキップ",
"skip_non_music": "音楽: 非音楽部分をスキップ", "skip_non_music": "音楽: 非音楽部分をスキップ",
"theme": "テーマ", "theme": "テーマ",
"auto": "自動", "auto": "自動",
"dark": "ダーク", "dark": "ダーク",
"light": "ライト", "light": "ライト",
"autoplay_video": "動画を自動再生", "autoplay_video": "動画を自動再生",
"audio_only": "音声のみ", "audio_only": "音声のみのモード",
"default_quality": "デフォルトの画質", "default_quality": "標準の画質",
"buffering_goal": "バッファリング目標値 (秒)", "buffering_goal": "バッファリング目標値 (秒)",
"country_selection": "国の選択", "country_selection": "国",
"default_homepage": "デフォルトのホームページ", "default_homepage": "ホームに表示するページ",
"show_comments": "コメントを表示", "show_comments": "コメントを表示",
"minimize_description_default": "デフォルトで詳細を最小化する", "minimize_description_default": "最初から説明を最小化",
"store_watch_history": "再生履歴を保存する", "store_watch_history": "再生履歴を保存",
"language_selection": "言語の選択", "language_selection": "言語",
"instances_list": "インスタンス一覧", "instances_list": "インスタンス一覧",
"enabled_codecs": "コーデックの有効化 (複数選択)", "enabled_codecs": "コーデックの有効化 (複数選択)",
"instance_selection": "インスタンスを選択", "instance_selection": "インスタンス",
"show_more": "もっと見る", "show_more": "もっと見る",
"yes": "はい", "yes": "はい",
"no": "いいえ", "no": "いいえ",
"export_to_json": "JSONに出力", "export_to_json": "JSONに出力",
"import_from_json": "JSON/CSVを読み込む", "import_from_json": "JSONから読み込む",
"loop_this_video": "この動画をループ再生", "loop_this_video": "この動画をループ再生",
"auto_play_next_video": "次の動画を自動再生", "auto_play_next_video": "次の動画を自動再生",
"donations": "開発者に寄付", "donations": "開発者に寄付",
@ -66,60 +69,82 @@
"minimize_recommendations": "おすすめを最小化", "minimize_recommendations": "おすすめを最小化",
"show_recommendations": "おすすめを見る", "show_recommendations": "おすすめを見る",
"disable_lbry": "ストリーミングのLBRYを無効化", "disable_lbry": "ストリーミングのLBRYを無効化",
"enable_lbry_proxy": "LBRYプロキシをオン", "enable_lbry_proxy": "LBRYにプロキシを使用",
"view_ssl_score": "SSLスコアを見る", "view_ssl_score": "SSLの評価を表示",
"search": "検索", "search": "検索 (Ctrl+K)",
"filter": "フィルター", "filter": "フィルター",
"loading": "読み込み中…", "loading": "読み込み中…",
"clear_history": "再生履歴を削除", "clear_history": "再生履歴を削除",
"hide_replies": "返信を非表示", "hide_replies": "返信を非表示",
"load_more_replies": "リプライをもっと見る", "load_more_replies": "返信をもっと見る",
"skip_filler_tangent": "無関係なコンテンツをスキップ", "skip_filler_tangent": "無関係な談話をスキップ",
"skip_highlight": "ハイライトをスキップ", "skip_highlight": "ハイライトをスキップ",
"add_to_playlist": "再生リストに追加する", "add_to_playlist": "再生リストに追加",
"create_playlist": "再生リストを作成", "create_playlist": "再生リストを作成",
"remove_from_playlist": "再生リストから削除", "remove_from_playlist": "再生リストから削除",
"delete_playlist_video_confirm": "再生リストからこの動画を削除しますか?", "delete_playlist_video_confirm": "再生リストからこの動画を削除しますか?",
"delete_playlist": "再生リストを削除", "delete_playlist": "再生リストを削除",
"please_select_playlist": "再生リストを選択してください", "please_select_playlist": "再生リストを選択してください",
"show_markers": "プレイヤーにマーカーを表示", "show_markers": "プレイヤーに目印の区切りを表示",
"select_playlist": "再生リストを選択", "select_playlist": "再生リストを選択",
"delete_playlist_confirm": "再生リストを削除しますか?", "delete_playlist_confirm": "再生リストを削除しますか?",
"delete_account": "アカウントを削除する", "delete_account": "アカウントを削除",
"store_search_history": "検索履歴を保存する", "store_search_history": "検索履歴を保存",
"show_chapters": "チャプター", "show_chapters": "チャプター",
"status_page": "状態", "status_page": "状態",
"source_code": "ソースコード", "source_code": "ソースコード",
"instance_donations": "インスタンスに寄付", "instance_donations": "インスタンスに寄付",
"minimize_comments": "コメントを最小化する", "minimize_comments": "コメントを最小化",
"share": "共有", "share": "共有",
"with_timecode": "タイムコード付きで共有", "with_timecode": "時間指定",
"different_auth_instance": "認証に別のインスタンスを使", "different_auth_instance": "認証に別のインスタンスを使",
"download_as_txt": ".txtでダウンロード", "download_as_txt": ".txtでダウンロード",
"logout": "このデバイスでログアウト", "logout": "この端末からログアウト",
"minimize_recommendations_default": "デフォルトでおすすめを最小化する", "minimize_recommendations_default": "最初からおすすめを最小化",
"hide_watched": "再生済みの動画をフィードに表示しない", "hide_watched": "再生済みの動画をフィードに表示しない",
"minimize_chapters_default": "デフォルトでチャプターを最小化する", "minimize_chapters_default": "最初からチャプターを最小化",
"show_watch_on_youtube": "\"YouTubeで見る\"ボタンを表示する", "show_watch_on_youtube": "「YouTubeで視聴」ボタンを表示",
"invalidate_session": "すべてのデバイスでログアウトする", "invalidate_session": "すべての端末からログアウト",
"instance_auth_selection": "認証インスタンスの選択", "instance_auth_selection": "認証インスタンス",
"clone_playlist_success": "複製に成功しました!", "clone_playlist_success": "複製に成功しました!",
"backup_preferences": "設定をバックアップする", "backup_preferences": "設定をバックアップ",
"restore_preferences": "設定を復元する", "restore_preferences": "設定を復元",
"back_to_home": "ホームに戻る", "back_to_home": "ホームに戻る",
"copy_link": "リンクコピーする", "copy_link": "リンクコピー",
"time_code": "タイムコード (秒)", "time_code": "タイムコード (秒)",
"documentation": "ドキュメント", "documentation": "ドキュメント",
"reset_preferences": "設定をリセット", "reset_preferences": "設定を初期化",
"confirm_reset_preferences": "設定をリセットしますか?", "confirm_reset_preferences": "設定をリセットしますか?",
"rename_playlist": "プレイリスト名を変更する",
"piped_link": "Pipedリンク", "piped_link": "Pipedリンク",
"new_playlist_name": "新しいプレイリスト名", "follow_link": "リンクを開く",
"follow_link": "リンクに従う",
"reply_count": "{count} 件の返信", "reply_count": "{count} 件の返信",
"clone_playlist": "プレイリストを複製", "clone_playlist": "再生リストを複製",
"minimize_comments_default": "デフォルトでコメントを最小化する", "minimize_comments_default": "最初からコメントを最小化",
"no_valid_playlists": "ファイルに有効なプレイリストが含まれていません。" "no_valid_playlists": "このファイルは有効な再生リストではありません!",
"playlist_bookmarked": "ブックマーク完了",
"bookmark_playlist": "ブックマーク",
"with_playlist": "再生リストで共有",
"skip_automatically": "自動",
"skip_button_only": "スキップボタン表示",
"skip_segment": "ここをスキップ",
"min_segment_length": "最小の区切りの長さ (秒)",
"show_less": "少なく見る",
"autoplay_next_countdown": "次の動画の再生までの時間 (秒)",
"dismiss": "却下",
"create_group": "グループ作成",
"group_name": "グループ名",
"auto_display_captions": "自動で字幕を表示",
"okay": "OK",
"cancel": "キャンセル",
"edit_playlist": "再生リストを編集",
"playlist_name": "再生リスト名",
"playlist_description": "再生リストの説明",
"chapters_layout_mobile": "モバイル端末でのチャプターの配置",
"show_search_suggestions": "検索語句の候補を表示",
"delete_automatically": "指定時間後に自動削除",
"enable_dearrow": "DeArrow を使用",
"generate_qrcode": "QRコードの生成",
"import_from_json_csv": "JSON/CSVから読み込む"
}, },
"comment": { "comment": {
"pinned_by": "{author} によって固定", "pinned_by": "{author} によって固定",
@ -130,25 +155,33 @@
"preferences": { "preferences": {
"instance_name": "インスタンス名", "instance_name": "インスタンス名",
"instance_locations": "インスタンスの場所", "instance_locations": "インスタンスの場所",
"has_cdn": "CDNは存在する?", "has_cdn": "CDNの有無",
"ssl_score": "SSLスコア", "ssl_score": "SSLの評価",
"registered_users": "登録済みユーザー", "registered_users": "登録利用者数",
"version": "バージョン", "version": "バージョン",
"up_to_date": "最新?" "up_to_date": "最新?"
}, },
"login": { "login": {
"username": "ユーザー名", "username": "ユーザー名",
"password": "パスワード" "password": "パスワード",
"password_confirm": "パスワードの確認",
"passwords_incorrect": "パスワードが一致しません!"
}, },
"video": { "video": {
"videos": "動画", "videos": "動画",
"views": "{views} 回再生", "views": "{views} 回再生",
"watched": "再生済み", "watched": "再生済み",
"sponsor_segments": "スポンサーによる広告", "sponsor_segments": "広告シーン数",
"ratings_disabled": "評価は無効化されています", "ratings_disabled": "評価は無効化されています",
"chapters": "チャプター", "chapters": "チャプター",
"live": "{0} ライブ配信", "live": "{0} ライブ配信",
"shorts": "ショート" "shorts": "ショート",
"all": "すべて",
"category": "分類",
"chapters_horizontal": "横方向",
"chapters_vertical": "縦方向",
"visibility": "公開状態",
"license": "ライセンス"
}, },
"search": { "search": {
"did_you_mean": "もしかして: {0}", "did_you_mean": "もしかして: {0}",
@ -159,14 +192,21 @@
"music_songs": "YT Music: 音楽", "music_songs": "YT Music: 音楽",
"music_videos": "YT Music: 動画", "music_videos": "YT Music: 動画",
"music_albums": "YT Music: アルバム", "music_albums": "YT Music: アルバム",
"music_playlists": "YT Music: 再生リスト" "music_playlists": "YT Music: 再生リスト",
"music_artists": "YT Music: アーティスト"
}, },
"info": { "info": {
"page_not_found": "ページが見つかりません", "page_not_found": "ページが見つかりません",
"copied": "コピーしました!", "copied": "コピーしました!",
"cannot_copy": "コピーできません!", "cannot_copy": "コピーできません!",
"preferences_note": "注意: 設定はブラウザのローカルストレージに保存されます。ブラウザのデータを削除するとリセットされます。", "preferences_note": "注意: 設定は、お使いのブラウザの保存領域に保存されます。ブラウザのデータを削除すると初期化されます。",
"local_storage": "この操作にはlocalStorageが必要です。Cookieは有効ですか" "local_storage": "この操作にはlocalStorageが必要です。Cookieは有効ですか",
"register_no_email_note": "ユーザー名としてのメールアドレスの使用は推奨しません。それでも続けますか?",
"next_video_countdown": "{0} 秒後に次の動画",
"days": "{amount}日",
"weeks": "{amount}週",
"hours": "{amount}時間",
"months": "{amount}月"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "チャンネル登録: {0}" "subscribed_channels_count": "チャンネル登録: {0}"

View File

@ -6,7 +6,14 @@
"history": "Amazray", "history": "Amazray",
"subscriptions": "Ijerriden", "subscriptions": "Ijerriden",
"account": "Amiḍan", "account": "Amiḍan",
"channels": "Ibuda" "channels": "Ibuda",
"playlists": "Tabdert n tɣuri",
"instance": "Tummant",
"player": "Ameɣri",
"livestreams": "Azuzer usrid",
"bookmarks": "Ticraḍ",
"feed": "Amultaɣ",
"trending": "Tiddin"
}, },
"actions": { "actions": {
"dark": "Ubrik", "dark": "Ubrik",
@ -19,16 +26,148 @@
"delete_playlist_confirm": "Kkes tabdart-a n tɣuri?", "delete_playlist_confirm": "Kkes tabdart-a n tɣuri?",
"share": "Bḍu", "share": "Bḍu",
"documentation": "Tasemlit", "documentation": "Tasemlit",
"status_page": "État" "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",
"follow_link": "Ḍfer aseɣwen",
"show_chapters": "Ixfawen",
"show_watch_on_youtube": "Sken taqeffalt Wali ɣef YouTube",
"reset_preferences": "Wennez ismenyifen",
"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": { "preferences": {
"version": "Lqem" "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": { "login": {
"username": "Nom d'utilisateur", "username": "Nom d'utilisateur",
"password": "Awal n uɛeddi" "password": "Awal n uɛeddi"
}, },
"video": { "video": {
"videos": "Tividyutin" "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

@ -8,7 +8,7 @@
"show_comments": "댓글 보이기", "show_comments": "댓글 보이기",
"unsubscribe": "구독 취소 - {count}", "unsubscribe": "구독 취소 - {count}",
"view_subscriptions": "구독 보기", "view_subscriptions": "구독 보기",
"least_recent": "가장 오래된", "least_recent": "오래된",
"theme": "테마", "theme": "테마",
"auto": "자동", "auto": "자동",
"channel_name_desc": "채널 이름 (Z-A)", "channel_name_desc": "채널 이름 (Z-A)",
@ -26,16 +26,16 @@
"no": "아니요", "no": "아니요",
"loop_this_video": "이 동영상 반복", "loop_this_video": "이 동영상 반복",
"auto_play_next_video": "다음 동영상 자동 재생", "auto_play_next_video": "다음 동영상 자동 재생",
"minimize_description": "설명 최소화", "minimize_description": "설명 가리기",
"show_recommendations": "추천 동영상 표시", "show_recommendations": "추천 동영상 표시",
"sort_by": "정렬:", "sort_by": "정렬:",
"most_recent": "가장 최신", "most_recent": "최신",
"channel_name_asc": "채널 이름 (A-Z)", "channel_name_asc": "채널 이름 (A-Z)",
"subscribe": "구독 - {count}", "subscribe": "구독 - {count}",
"audio_only": "오디오만", "audio_only": "오디오만",
"skip_sponsors": "스폰서 스킵", "skip_sponsors": "스폰서 스킵",
"dark": "다크", "dark": "다크",
"minimize_description_default": "기본적으로 설명 최소화", "minimize_description_default": "설명 가리기를 기본 설정값으로",
"enabled_codecs": "활성화된 코덱 (여러 개)", "enabled_codecs": "활성화된 코덱 (여러 개)",
"instance_selection": "인스턴스 선택", "instance_selection": "인스턴스 선택",
"import_from_json": "JSON/CSV에서 가져오기", "import_from_json": "JSON/CSV에서 가져오기",
@ -50,11 +50,11 @@
"show_description": "설명 표시", "show_description": "설명 표시",
"disable_lbry": "LBRY 스트리밍 비활성화", "disable_lbry": "LBRY 스트리밍 비활성화",
"enable_lbry_proxy": "LBRY 프록시 활성화", "enable_lbry_proxy": "LBRY 프록시 활성화",
"minimize_recommendations": "추천 동영상 최소화", "minimize_recommendations": "추천 동영상 가리기",
"loading": "로딩...", "loading": "로딩...",
"view_ssl_score": "SSL 점수 보기", "view_ssl_score": "SSL 점수 보기",
"skip_intro": "중간 휴식/인트로 애니메이션 스킵", "skip_intro": "중간 휴식/인트로 애니메이션 스킵",
"search": "검색", "search": "검색 (Ctrl+K)",
"clear_history": "기록 지우기", "clear_history": "기록 지우기",
"skip_highlight": "하이라이트 스킵", "skip_highlight": "하이라이트 스킵",
"select_playlist": "재생목록 선택", "select_playlist": "재생목록 선택",
@ -69,8 +69,6 @@
"logout": "이 기기에서 로그아웃", "logout": "이 기기에서 로그아웃",
"show_chapters": "챕터", "show_chapters": "챕터",
"download_as_txt": ".txt로 다운로드", "download_as_txt": ".txt로 다운로드",
"new_playlist_name": "새 재생목록 이름",
"rename_playlist": "재생목록 이름 변경",
"share": "공유", "share": "공유",
"copy_link": "링크 복사", "copy_link": "링크 복사",
"time_code": "시작 시간 (초)", "time_code": "시작 시간 (초)",
@ -84,7 +82,7 @@
"restore_preferences": "설정 복원", "restore_preferences": "설정 복원",
"backup_preferences": "설정 백업", "backup_preferences": "설정 백업",
"back_to_home": "홈으로 가기", "back_to_home": "홈으로 가기",
"minimize_recommendations_default": "기본적으로 추천 동영상 최소화", "minimize_recommendations_default": "추천 동영상 가리기를 기본 설정값으로",
"invalidate_session": "모든 기기에서 로그아웃", "invalidate_session": "모든 기기에서 로그아웃",
"instance_auth_selection": "인증 인스턴스 선택", "instance_auth_selection": "인증 인스턴스 선택",
"different_auth_instance": "인증에 다른 인스턴스 사용", "different_auth_instance": "인증에 다른 인스턴스 사용",
@ -95,7 +93,17 @@
"documentation": "문서", "documentation": "문서",
"source_code": "소스 코드", "source_code": "소스 코드",
"instance_donations": "인스턴스 기부", "instance_donations": "인스턴스 기부",
"status_page": "상태" "status_page": "상태",
"bookmark_playlist": "북마크",
"dismiss": "무시",
"minimize_comments_default": "댓글 가리기를 기본 설정값으로",
"minimize_comments": "댓글 가리기",
"show_watch_on_youtube": "YouTube에서 보기’ 버튼 표시",
"minimize_chapters_default": "챕터 가리기를 기본 설정값으로",
"autoplay_next_countdown": "다음 영상 재생까지 대기 시간(초)",
"skip_button_only": "스킵 버튼 표시",
"skip_automatically": "자동",
"show_less": "덜 보기"
}, },
"titles": { "titles": {
"feed": "피드", "feed": "피드",
@ -108,7 +116,10 @@
"playlists": "재생목록", "playlists": "재생목록",
"account": "계정", "account": "계정",
"instance": "인스턴스", "instance": "인스턴스",
"player": "플레이어" "player": "플레이어",
"bookmarks": "북마크",
"livestreams": "실시간",
"channels": "채널"
}, },
"player": { "player": {
"watch_on": "에서 보기 {0}" "watch_on": "에서 보기 {0}"
@ -140,7 +151,8 @@
"ratings_disabled": "등급 비활성화됨", "ratings_disabled": "등급 비활성화됨",
"live": "{0} 라이브", "live": "{0} 라이브",
"shorts": "Shorts", "shorts": "Shorts",
"chapters": "챕터" "chapters": "챕터",
"category": "카테고리"
}, },
"search": { "search": {
"did_you_mean": "이것을 찾으셨나요: {0}?", "did_you_mean": "이것을 찾으셨나요: {0}?",

View File

@ -80,7 +80,6 @@
"reply_count": "{count} atsakymai", "reply_count": "{count} atsakymai",
"show_chapters": "Skirsniai", "show_chapters": "Skirsniai",
"piped_link": "Piped nuoroda", "piped_link": "Piped nuoroda",
"rename_playlist": "Pervardyti grojaraštį",
"follow_link": "Sekti nuorodą", "follow_link": "Sekti nuorodą",
"store_search_history": "Išsaugoti paieškos istoriją", "store_search_history": "Išsaugoti paieškos istoriją",
"hide_watched": "Slėpti žiūrėtus vaizdo įrašus sklaidos kanale", "hide_watched": "Slėpti žiūrėtus vaizdo įrašus sklaidos kanale",
@ -88,7 +87,6 @@
"status_page": "Būsena", "status_page": "Būsena",
"copy_link": "Kopijuoti nuorodą", "copy_link": "Kopijuoti nuorodą",
"share": "Dalintis", "share": "Dalintis",
"new_playlist_name": "Naujas grojaraščio pavadinimas",
"minimize_recommendations_default": "Sumažinti rekomendacijas automatiškai", "minimize_recommendations_default": "Sumažinti rekomendacijas automatiškai",
"instance_donations": "Perdavimo šaltinio parama", "instance_donations": "Perdavimo šaltinio parama",
"instance_auth_selection": "Autentifikavimo perdavimo šaltinio pasirinkimas", "instance_auth_selection": "Autentifikavimo perdavimo šaltinio pasirinkimas",

View File

@ -7,7 +7,9 @@
"feed": "ഫീഡ്", "feed": "ഫീഡ്",
"login": "പ്രവേശിക്കുക", "login": "പ്രവേശിക്കുക",
"subscriptions": "സബ്സ്ക്രിപ്ഷനുകൾ", "subscriptions": "സബ്സ്ക്രിപ്ഷനുകൾ",
"playlists": "പ്ലേലിസ്റ്റുകൾ" "playlists": "പ്ലേലിസ്റ്റുകൾ",
"player": "കളിക്കാരൻ",
"account": "അക്കോണട്"
}, },
"actions": { "actions": {
"view_subscriptions": "സബ്സ്ക്രിപ്ഷനുകൾ കാണുക", "view_subscriptions": "സബ്സ്ക്രിപ്ഷനുകൾ കാണുക",
@ -54,7 +56,7 @@
"yes": "അതെ", "yes": "അതെ",
"show_more": "കൂടുതൽ കാണിക്കുക", "show_more": "കൂടുതൽ കാണിക്കുക",
"buffering_goal": "ബഫറിംഗ് ലക്ഷ്യം(സെക്കൻഡുകളിൽ)", "buffering_goal": "ബഫറിംഗ് ലക്ഷ്യം(സെക്കൻഡുകളിൽ)",
"import_from_json": "JSON/CSV നിന്ന് ഇറക്കുമതി ചെയ്യൂ", "import_from_json": "JSONിൽ നിന്ന് ഇറക്കുമതി ചെയ്യൂ",
"export_to_json": "JSON-ലേക്ക് എക്സ്പ്പോർട്ട് ചെയ്യുക", "export_to_json": "JSON-ലേക്ക് എക്സ്പ്പോർട്ട് ചെയ്യുക",
"instance_selection": "ഇൻസ്റ്റ്ൻസ് തിരഞ്ഞെടുക്കുക", "instance_selection": "ഇൻസ്റ്റ്ൻസ് തിരഞ്ഞെടുക്കുക",
"loading": "ലഭ്യമാക്കുന്നു...", "loading": "ലഭ്യമാക്കുന്നു...",
@ -72,16 +74,33 @@
"select_playlist": "ഒരു പ്ലേലിസ്റ്റ് തിരഞ്ഞെടുക്കൂ", "select_playlist": "ഒരു പ്ലേലിസ്റ്റ് തിരഞ്ഞെടുക്കൂ",
"please_select_playlist": "ഒരു പ്ലേലിസ്റ്റ് തിരഞ്ഞെടുക്കുക", "please_select_playlist": "ഒരു പ്ലേലിസ്റ്റ് തിരഞ്ഞെടുക്കുക",
"delete_playlist_video_confirm": "ഈ പ്ലേലിസ്റ്റിൽ നിന്ന് ഈ വീഡിയോ ഒഴിവാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ?", "delete_playlist_video_confirm": "ഈ പ്ലേലിസ്റ്റിൽ നിന്ന് ഈ വീഡിയോ ഒഴിവാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ?",
"delete_playlist_confirm": "ഈ പ്ലേലിസ്റ്റ് ഇല്ലാതാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടൊ?" "delete_playlist_confirm": "ഈ പ്ലേലിസ്റ്റ് ഇല്ലാതാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടൊ?",
"skip_automatically": "സ്വയമേവ",
"show_watch_on_youtube": "YouTubeിൽ കാണാം എന്ന button കാണിക്കുകാ",
"time_code": "സമയമായം (സെക്കന്റുകളിൽ)",
"show_less": "കുറച്ച്മാതറം",
"with_timecode": "സമയമായവുമായി പങ്കുവെക്കുക",
"back_to_home": "തിരിച്ച് വീട്ടിലേക്ക്",
"dismiss": "തിരിച്ച്",
"create_group": "കൂട്ടം ഉണടാക്കുകാ",
"okay": "ശരി",
"invalidate_session": "എല ടിവയഡിൽ നിന്നും ലൊഗൗഠ അവുകാ",
"enabled_codecs": "ഉപയോഗിക്കുന്ന കോടെക്കൃകൽ",
"share": "പങ്കുവെക്കുക",
"documentation": "സഹായക്കുറിപ്പുകൽ",
"cancel": "റദ്ദാക്കുക",
"generate_qrcode": "QR Code ഉണ്ടാക്കുക",
"import_from_json_csv": "JSON/CSVിൽ നിന്ന് ഇറക്കുമതി ചെയ്യൂ"
}, },
"player": { "player": {
"watch_on": "കാണുക {0}" "watch_on": "{0}ിൻ കാണൃകാ"
}, },
"video": { "video": {
"watched": "കണ്ടതാണ്", "watched": "കണ്ടതാണ്",
"views": "{views} കാഴ്ചകൾ", "views": "{views} കാഴ്ചകൾ",
"videos": "വിഡിയോകൾ", "videos": "വിഡിയോകൾ",
"sponsor_segments": "സ്പോൺസർമാരുടെ ഭാഗങ്ങൾ" "sponsor_segments": "സ്പോൺസർമാരുടെ ഭാഗങ്ങൾ",
"all": "എല്ലാം"
}, },
"comment": { "comment": {
"pinned_by": "പിൻ ചെയ്തിരിക്കുന്നത് {author}" "pinned_by": "പിൻ ചെയ്തിരിക്കുന്നത് {author}"
@ -96,5 +115,8 @@
"login": { "login": {
"password": "രഹസ്യവാക്ക്", "password": "രഹസ്യവാക്ക്",
"username": "ഉപയോക്തൃനാമം" "username": "ഉപയോക്തൃനാമം"
},
"search": {
"did_you_mean": "{0} ആണൊ ഉദേശിച്ചെ?"
} }
} }

View File

@ -81,8 +81,6 @@
"confirm_reset_preferences": "Tilbakestill alle innstillingene?", "confirm_reset_preferences": "Tilbakestill alle innstillingene?",
"restore_preferences": "Gjenopprett innstillinger", "restore_preferences": "Gjenopprett innstillinger",
"show_chapters": "Kapitler", "show_chapters": "Kapitler",
"rename_playlist": "Gi spillelisten ny navn",
"new_playlist_name": "Nytt spillelistenavn",
"share": "Del", "share": "Del",
"with_timecode": "Del med tidskode", "with_timecode": "Del med tidskode",
"piped_link": "Piped-lenke", "piped_link": "Piped-lenke",

View File

@ -1,108 +1,130 @@
{ {
"actions": { "actions": {
"skip_sponsors": "Sponsors Overslaan", "skip_sponsors": "Sponsors overslaan",
"skip_outro": "Eindkaarten/Credits overslaan", "skip_outro": "Eindkaarten/credits overslaan",
"add_to_playlist": "Aan Afspeellijst Toevoegen", "add_to_playlist": "Toevoegen aan afspeellijst",
"sort_by": "Sorteer op:", "sort_by": "Sorteren op:",
"buffering_goal": "Bufferdoel (in seconden)", "buffering_goal": "Bufferdoel (in seconden)",
"country_selection": "Land Selectie", "country_selection": "Land",
"show_recommendations": "Aanbevelingen Weergeven", "show_recommendations": "Aanbevelingen tonen",
"disable_lbry": "LBRY voor Streamen Uitschakelen", "disable_lbry": "LBRY voor streamen uitschakelen",
"enable_lbry_proxy": "Proxy voor LBRY Inschakelen", "enable_lbry_proxy": "Proxy voor LBRY Inschakelen",
"view_ssl_score": "SSL-score Bekijken", "view_ssl_score": "SSL-score Bekijken",
"search": "Zoeken", "search": "Zoeken (Ctrl+K)",
"filter": "Filter", "filter": "Filter",
"skip_filler_tangent": "Opvultangens Overslaan", "skip_filler_tangent": "Opvultangens Overslaan",
"theme": "Thema", "theme": "Thema",
"subscribe": "Abonneren - {count}", "subscribe": "Abonneren - {count}",
"skip_non_music": "Muziek Overslaan: Niet-muzieksectie", "skip_non_music": "Muziek Overslaan: Niet-muzieksectie",
"show_comments": "Toon Reacties", "show_comments": "Opmerkingen tonen",
"skip_self_promo": "Onbetaalde/Zelf-promotie Overslaan", "skip_self_promo": "Onbetaalde-/zelfpromotie overslaan",
"skip_highlight": "Markering Overslaan", "skip_highlight": "Markering Overslaan",
"skip_interaction": "Interactieherinnering Overslaan (Abonneren)", "skip_interaction": "Interactieherinnering overslaan (abonneren)",
"show_more": "Toon Meer", "show_more": "Meer tonen",
"unsubscribe": "Afmelden - {count}", "unsubscribe": "Afmelden - {count}",
"view_subscriptions": "Abonnementen Bekijken", "view_subscriptions": "Abonnementen bekijken",
"enable_sponsorblock": "Sponsorblok Inschakelen", "enable_sponsorblock": "Sponsorblok inschakelen",
"skip_preview": "Voorbeeld/Samenvatting Overslaan", "skip_preview": "Voorbeschouwing/samenvatting overslaan",
"auto": "Auto", "auto": "Auto",
"dark": "Donker", "dark": "Donker",
"light": "Licht", "light": "Licht",
"default_quality": "Standaard Kwaliteit", "default_quality": "Standaard Kwaliteit",
"loop_this_video": "Deze Video Herhalen", "loop_this_video": "Deze video herhalen",
"donations": "Ontwikkelingsdonaties", "donations": "Ontwikkelingsdonaties",
"minimize_description": "Beschrijving Minimaliseren", "minimize_description": "Omschrijving minimaliseren",
"show_description": "Toon Beschrijving", "show_description": "Omschrijving tonen",
"minimize_recommendations": "Aanbevelingen Minimaliseren", "minimize_recommendations": "Aanbevelingen minimaliseren",
"most_recent": "Meest Recente", "most_recent": "Meest recent",
"least_recent": "Minst Recente", "least_recent": "Minst recent",
"channel_name_asc": "Kanaalnaam (A-Z)", "channel_name_asc": "Kanaalnaam (A-Z)",
"channel_name_desc": "Kanaalnaam (Z-A)", "channel_name_desc": "Kanaalnaam (Z-A)",
"back": "Terug", "back": "Terug",
"uses_api_from": "Gebruikt de API van ", "uses_api_from": "API gebruiken van ",
"skip_intro": "Pauze/Intro-animatie Overslaan", "skip_intro": "Onderbrekings-/intro-animatie overslaan",
"autoplay_video": "Video Automatisch Afspelen", "autoplay_video": "Video Automatisch Afspelen",
"store_watch_history": "Kijkgeschiedenis Opslaan", "store_watch_history": "Kijkgeschiedenis Opslaan",
"loading": "Laden...", "loading": "Laden...",
"audio_only": "Alleen Audio", "audio_only": "Alleen Audio",
"default_homepage": "Standaard Startpagina", "default_homepage": "Standaard Startpagina",
"minimize_description_default": "Beschrijving Standaard Minimaliseren", "minimize_description_default": "Omschrijving standaard minimaliseren",
"language_selection": "Taalkeuze", "language_selection": "Taal",
"instances_list": "Instantieslijst", "instances_list": "Instantieslijst",
"yes": "Ja", "yes": "Ja",
"export_to_json": "Exporteren naar JSON", "export_to_json": "Exporteren naar JSON",
"hide_replies": "Verberg Antwoorden", "hide_replies": "Reacties verbergen",
"enabled_codecs": "Ingeschakelde Codecs (Meerdere)", "enabled_codecs": "Ingeschakelde Codecs (Meerdere)",
"no": "Nee", "no": "Nee",
"auto_play_next_video": "Volgende Video Automatisch Afspelen", "auto_play_next_video": "Volgende video automatisch afspelen",
"remove_from_playlist": "Uit Afspeellijst Verwijderen", "remove_from_playlist": "Uit afspeellijst verwijderen",
"select_playlist": "Selecteer een Afspeellijst", "select_playlist": "Afspeellijst selecteren",
"delete_playlist_confirm": "Deze afspeellijst verwijderen?", "delete_playlist_confirm": "Deze afspeellijst verwijderen?",
"please_select_playlist": "Kies een afspeellijst a.u.b.", "please_select_playlist": "Selecteer een afspeellijst",
"instance_selection": "Instantie Selectie", "instance_selection": "Instantie",
"import_from_json": "Importeren uit JSON/CSV", "import_from_json": "Importeren uit JSON",
"clear_history": "Geschiedenis Wissen", "clear_history": "Geschiedenis wissen",
"load_more_replies": "Laad meer Antwoorden", "load_more_replies": "Meer reacties laden",
"delete_playlist_video_confirm": "Video van playlist verwijderen?", "delete_playlist_video_confirm": "Video uit deze afspeellijst verwijderen?",
"create_playlist": "Afspeellijst Maken", "create_playlist": "Afspeellijst aanmaken",
"delete_playlist": "Afspeellijst Verwijderen", "delete_playlist": "Afspeellijst verwijderen",
"show_markers": "Toon markeringen op Speler", "show_markers": "Laat markeringen op speler zien",
"store_search_history": "Zoekgeschiedenis opslaan", "store_search_history": "Zoekgeschiedenis bijhouden",
"minimize_chapters_default": "Hoofdstukken standaard minimaliseren", "minimize_chapters_default": "Hoofdstukken standaard minimaliseren",
"show_watch_on_youtube": "Toon Bekijk op YouTube knop", "show_watch_on_youtube": "De knop Bekijken op YouTube tonen",
"restore_preferences": "Voorkeuren herstellen", "restore_preferences": "Voorkeuren herstellen",
"with_timecode": "Delen met tijdcode", "with_timecode": "Delen met tijdcode",
"piped_link": "Piped link", "piped_link": "Piped-link",
"follow_link": "Volg link", "follow_link": "Volglink",
"copy_link": "Link kopiëren", "copy_link": "Link kopiëren",
"hide_watched": "Verberg bekeken video's in de feed", "hide_watched": "Bekeken video's in feed verbergen",
"minimize_comments": "Opmerkingen minimaliseren", "minimize_comments": "Opmerkingen minimaliseren",
"instance_auth_selection": "Autenticatie Instantie Selectie", "instance_auth_selection": "Authenticatie-instantie",
"clone_playlist": "Afspeellijst klonen", "clone_playlist": "Afspeellijst dupliceren",
"download_as_txt": "Downloaden als .txt", "download_as_txt": "Downloaden als .txt",
"rename_playlist": "Afspeellijst hernoemen",
"new_playlist_name": "Nieuwe afspeellijstnaam",
"share": "Delen", "share": "Delen",
"documentation": "Documentatie", "documentation": "Documentatie",
"status_page": "Status", "status_page": "Status",
"time_code": "Tijdcode (in seconden)", "time_code": "Tijdcode (in seconden)",
"show_chapters": "Hoofdstukken", "show_chapters": "Hoofdstukken",
"source_code": "Broncode", "source_code": "Broncode",
"instance_donations": "Instantie donaties", "instance_donations": "Instantiedonaties",
"reply_count": "{count} antwoorden", "reply_count": "{count} reacties",
"no_valid_playlists": "Het bestand bevat geen geldige afspeellijsten!", "no_valid_playlists": "Het bestand bevat geen geldige afspeellijsten!",
"clone_playlist_success": "Succesvol gekloond!", "clone_playlist_success": "Dupliceren gelukt!",
"reset_preferences": "Voorkeuren opnieuw instellen", "reset_preferences": "Voorkeuren resetten",
"back_to_home": "Terug naar de start", "back_to_home": "Terug naar de start",
"minimize_comments_default": "Opmerkingen standaard minimaliseren", "minimize_comments_default": "Opmerkingen standaard minimaliseren",
"delete_account": "Account Verwijderen", "delete_account": "Account verwijderen",
"logout": "Uitloggen van dit apparaat", "logout": "Uitloggen op dit apparaat",
"minimize_recommendations_default": "Aanbevelingen standaard minimaliseren", "minimize_recommendations_default": "Aanbevelingen standaard minimaliseren",
"confirm_reset_preferences": "Weet u zeker dat u uw voorkeuren opnieuw wilt instellen?", "confirm_reset_preferences": "Weet u zeker dat u uw voorkeuren wilt resetten?",
"backup_preferences": "Back-up voorkeuren", "backup_preferences": "Back-up-voorkeuren",
"invalidate_session": "Alle apparaten afmelden", "invalidate_session": "Uitloggen op alle apparaten",
"different_auth_instance": "Gebruik een andere instantie voor authenticatie", "different_auth_instance": "Gebruik een andere instantie voor authenticatie",
"with_playlist": "Delen met afspeellijst" "with_playlist": "Delen met afspeellijst",
"playlist_bookmarked": "Bladwijzer gemaakt",
"bookmark_playlist": "Bladwijzer",
"skip_automatically": "Automatisch",
"skip_button_only": "Overslaan-knop tonen",
"min_segment_length": "Minimale segmentlengte (in seconden)",
"skip_segment": "segment overslaan",
"show_less": "Minder tonen",
"autoplay_next_countdown": "Standaard aftellen tot de volgende video (in seconden)",
"dismiss": "Afwijzen",
"enable_dearrow": "DeArrow inschakelen",
"playlist_description": "Afspeellijst­omschrijving",
"cancel": "Annuleren",
"show_search_suggestions": "Zoeksuggesties tonen",
"delete_automatically": "Automatisch verwijderen na",
"okay": "Oké",
"playlist_name": "Afspeellijst­naam",
"edit_playlist": "Afspeellijst bewerken",
"create_group": "Groep aanmaken",
"group_name": "Groep­naam",
"generate_qrcode": "QR-code genereren",
"chapters_layout_mobile": "Mobiele lay-out voor hoofdstukken",
"auto_display_captions": "Ondertiteling automatisch tonen",
"import_from_json_csv": "Importeren uit JSON/CSV",
"download_frame": "Beeld downloaden"
}, },
"titles": { "titles": {
"register": "Registreren", "register": "Registreren",
@ -117,25 +139,32 @@
"instance": "Instantie", "instance": "Instantie",
"player": "Speler", "player": "Speler",
"livestreams": "Livestreams", "livestreams": "Livestreams",
"channels": "Kanalen" "channels": "Kanalen",
"bookmarks": "Bladwijzers",
"dearrow": "DeArrow",
"channel_groups": "Kanaal­groepen"
}, },
"player": { "player": {
"watch_on": "Kijk op {0}" "watch_on": "Kijken op {0}",
"failed": "Mislukt met foutcode {0}, zie logboeken voor meer informatie"
}, },
"search": { "search": {
"videos": "YouTube: Video's", "videos": "YouTube: Video's",
"channels": "YouTube: Kanalen", "channels": "YouTube: Kanalen",
"playlists": "YouTube: Afspeellijsten", "playlists": "YouTube: Afspeellijsten",
"music_songs": "YT Muziek: Liedjes", "music_songs": "YT Music: Nummers",
"music_videos": "YT Muziek: Video's", "music_videos": "YT Music: Video's",
"music_albums": "YT Muziek: Albums", "music_albums": "YT Music: Albums",
"music_playlists": "YT Muziek: Afspeellijsten", "music_playlists": "YT Music: Afspeellijsten",
"did_you_mean": "Bedoelde u: {0}?", "did_you_mean": "Bedoelde u: {0}?",
"all": "YouTube: Alles" "all": "YouTube: Alles",
"music_artists": "YT Music: Artiesten"
}, },
"login": { "login": {
"username": "Gebruikersnaam", "username": "Gebruikersnaam",
"password": "Wachtwoord" "password": "Wachtwoord",
"password_confirm": "Wachtwoord bevestigen",
"passwords_incorrect": "Wachtwoorden komen niet overeen!"
}, },
"video": { "video": {
"videos": "Video's", "videos": "Video's",
@ -143,31 +172,43 @@
"chapters": "Hoofdstukken", "chapters": "Hoofdstukken",
"watched": "Gekeken", "watched": "Gekeken",
"sponsor_segments": "Sponsorsegmenten", "sponsor_segments": "Sponsorsegmenten",
"ratings_disabled": "Beoordelingen Uitgeschakeld", "ratings_disabled": "Beoordelingen uitgeschakeld",
"live": "{0} Live", "live": "{0} live",
"shorts": "Shorts" "shorts": "Shorts",
"category": "Categorie",
"all": "Alle",
"chapters_horizontal": "Horizontaal",
"chapters_vertical": "Verticaal",
"license": "Licentie",
"visibility": "Zichtbaarheid"
}, },
"preferences": { "preferences": {
"has_cdn": "Heeft CDN?", "has_cdn": "Heeft CDN?",
"registered_users": "Geregistreerde Gebruikers", "registered_users": "Geregistreerde gebruikers",
"instance_name": "Instantie Naam", "instance_name": "Instantienaam",
"instance_locations": "Locaties van Instanties", "instance_locations": "Instantielocaties",
"version": "Versie", "version": "Versie",
"up_to_date": "Bijgewerkt?", "up_to_date": "Bijgewerkt?",
"ssl_score": "SSL-score" "ssl_score": "SSL-score"
}, },
"comment": { "comment": {
"pinned_by": "Vastgemaakt door {author}", "pinned_by": "Vastgemaakt door {author}",
"user_disabled": "Reacties zijn uitgeschakeld in de instellingen.", "user_disabled": "Opmerkingen zijn uitgeschakeld in de instellingen.",
"loading": "Reacties laden...", "loading": "Opmerkingen laden…",
"disabled": "Reacties zijn uitgeschakeld door de uploader." "disabled": "Opmerkingen zijn uitgeschakeld door de uploader."
}, },
"info": { "info": {
"preferences_note": "Let op: voorkeuren worden opgeslagen in de lokale opslag van uw browser. Als u uw browsergegevens verwijdert, worden ze opnieuw ingesteld.", "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!", "copied": "Gekopieerd!",
"cannot_copy": "Kan niet kopiëren!", "cannot_copy": "Kan niet kopiëren!",
"page_not_found": "Pagina niet gevonden", "page_not_found": "Pagina niet gevonden",
"local_storage": "Deze actie vereist lokale opslag, zijn cookies ingeschakeld?" "local_storage": "Deze actie vereist lokale opslag, zijn cookies ingeschakeld?",
"register_no_email_note": "Een e-mailadres als gebruikersnaam gebruiken wordt afgeraden. Toch doorgaan?",
"next_video_countdown": "Volgende video wordt afgespeeld over {0}s",
"days": "{amount} dag(en)",
"weeks": "{amount} week/weken",
"months": "{amount} maand(en)",
"hours": "{amount} uur"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Geabonneerd op: {0}" "subscribed_channels_count": "Geabonneerd op: {0}"

210
src/locales/oc.json Normal file
View File

@ -0,0 +1,210 @@
{
"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",
"channel_groups": "Grops de cadenas",
"dearrow": "DeArrow"
},
"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": "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",
"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": "Instà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": "Instància",
"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",
"with_timecode": "Partejar amb còdi orari",
"piped_link": "Ligam cap a Piped",
"show_chapters": "Capítols",
"country_selection": "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",
"create_group": "Crear un grop",
"group_name": "Nom del grop",
"cancel": "Anullar",
"okay": "Dacòrd",
"edit_playlist": "Modificar la lista de lectura",
"playlist_name": "Nom de la lista de lectura",
"playlist_description": "Descripcion de la lista de lectura",
"auto_display_captions": "Afichatge auto dels sostítols",
"show_search_suggestions": "Mostrar las suggestions de recèrcas",
"chapters_layout_mobile": "Disposicion capítols sus mobil",
"enable_dearrow": "Activar DeArrow",
"import_from_json_csv": "Importar dun JSON/CSV"
},
"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",
"chapters_horizontal": "Orizontal",
"chapters_vertical": "Vertical",
"license": "Licéncia",
"visibility": "Visibilitat"
},
"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",
"hours": "{amount} ora(s)",
"months": "{amount} mes(es)",
"days": "{amount} jorn(s)",
"weeks": "{amount} setmana(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",
"music_artists": "YT Music : Artistas"
},
"subscriptions": {
"subscribed_channels_count": "Abonat a : {0}"
}
}

View File

@ -12,7 +12,10 @@
"account": "ଆକାଉଣ୍ଟ", "account": "ଆକାଉଣ୍ଟ",
"player": "ପ୍ଲେୟାର", "player": "ପ୍ଲେୟାର",
"livestreams": "ସିଧାପ୍ରସାରଣ ଗୁଡ଼ିକ", "livestreams": "ସିଧାପ୍ରସାରଣ ଗୁଡ଼ିକ",
"channels": "ସ୍ରୋତ ଗୁଡ଼ିକ" "channels": "ସ୍ରୋତ ଗୁଡ଼ିକ",
"bookmarks": "ବୁକମାର୍କଗୁଡିକ",
"channel_groups": "ଚ୍ୟାନେଲ୍ ଗୋଷ୍ଠୀଗୁଡିକ",
"dearrow": "ଡି ତୀର"
}, },
"player": { "player": {
"watch_on": "{0} ରେ ଦେଖନ୍ତୁ" "watch_on": "{0} ରେ ଦେଖନ୍ତୁ"
@ -32,9 +35,7 @@
"minimize_recommendations": "ସୁପାରିଶକୁ କମ୍ କରନ୍ତୁ", "minimize_recommendations": "ସୁପାରିଶକୁ କମ୍ କରନ୍ତୁ",
"show_recommendations": "ସୁପାରିଶଗୁଡିକ ଦେଖାନ୍ତୁ", "show_recommendations": "ସୁପାରିଶଗୁଡିକ ଦେଖାନ୍ତୁ",
"disable_lbry": "ଷ୍ଟ୍ରିମିଂ ପାଇଁ LBRY ଅକ୍ଷମ କରନ୍ତୁ", "disable_lbry": "ଷ୍ଟ୍ରିମିଂ ପାଇଁ LBRY ଅକ୍ଷମ କରନ୍ତୁ",
"search": "ସନ୍ଧାନ କରନ୍ତୁ", "search": "ସନ୍ଧାନ କରନ୍ତୁ (Ctrl+K)",
"rename_playlist": "ପ୍ଲେ ଲିଷ୍ଟର ନାମ ପରିବର୍ତ୍ତନ କରନ୍ତୁ",
"new_playlist_name": "ନୂତନ ପ୍ଲେଲିଷ୍ଟ ନାମ",
"channel_name_asc": "ସ୍ରୋତ ର ନାମ (A-Z)", "channel_name_asc": "ସ୍ରୋତ ର ନାମ (A-Z)",
"least_recent": "ସର୍ବନିମ୍ନ ସାମ୍ପ୍ରତିକ", "least_recent": "ସର୍ବନିମ୍ନ ସାମ୍ପ୍ରତିକ",
"channel_name_desc": "ସ୍ରୋତ ର ନାମ (Z-A)", "channel_name_desc": "ସ୍ରୋତ ର ନାମ (Z-A)",
@ -57,14 +58,14 @@
"autoplay_video": "ଅଟୋପ୍ଲେ ଭିଡିଓ", "autoplay_video": "ଅଟୋପ୍ଲେ ଭିଡିଓ",
"enabled_codecs": "ସକ୍ଷମ କୋଡେକସ୍ (ଏକାଧିକ)", "enabled_codecs": "ସକ୍ଷମ କୋଡେକସ୍ (ଏକାଧିକ)",
"audio_only": "କେବଳ ସ୍ୱର", "audio_only": "କେବଳ ସ୍ୱର",
"language_selection": "ଭାଷା ଚୟନ", "language_selection": "ଭାଷା",
"show_more": "ଅଧିକ ଦେଖାନ୍ତୁ", "show_more": "ଅଧିକ ଦେଖାନ୍ତୁ",
"buffering_goal": "ବଫରିଂ ଲକ୍ଷ୍ୟ (ସେକେଣ୍ଡରେ)", "buffering_goal": "ବଫରିଂ ଲକ୍ଷ୍ୟ (ସେକେଣ୍ଡରେ)",
"country_selection": "ଦେଶ ଚୟନ", "country_selection": "ଦେଶ",
"minimize_description_default": "ଡିଫଲ୍ଟ ଭାବରେ ବର୍ଣ୍ଣନାକୁ କମ୍ କରନ୍ତୁ", "minimize_description_default": "ଡିଫଲ୍ଟ ଭାବରେ ବର୍ଣ୍ଣନାକୁ କମ୍ କରନ୍ତୁ",
"store_watch_history": "ଦେଖିଥିବା ଭିଡିଓ ଗୁଡ଼ିକର ଇତିହାସ ରଖନ୍ତୁ", "store_watch_history": "ଦେଖିଥିବା ଭିଡିଓ ଗୁଡ଼ିକର ଇତିହାସ ରଖନ୍ତୁ",
"instances_list": "ଉଦାହରଣ ତାଲିକା", "instances_list": "ଉଦାହରଣ ତାଲିକା",
"instance_selection": "ଇନଷ୍ଟାନ୍ସ ଚୟନ", "instance_selection": "ଇନଷ୍ଟାନ୍ସ",
"yes": "ହଁ", "yes": "ହଁ",
"import_from_json": "JSON / CSV ରୁ ଆମଦାନୀ କରନ୍ତୁ", "import_from_json": "JSON / CSV ରୁ ଆମଦାନୀ କରନ୍ତୁ",
"no": "ନାହିଁ", "no": "ନାହିଁ",
@ -98,7 +99,7 @@
"minimize_recommendations_default": "ଡିଫଲ୍ଟ ଭାବରେ ସୁପାରିଶକୁ କମ୍ କରନ୍ତୁ", "minimize_recommendations_default": "ଡିଫଲ୍ଟ ଭାବରେ ସୁପାରିଶକୁ କମ୍ କରନ୍ତୁ",
"invalidate_session": "ସମସ୍ତ ଡିଭାଇସ୍ ରୁ ଲଗଆଉଟ୍ କରନ୍ତୁ", "invalidate_session": "ସମସ୍ତ ଡିଭାଇସ୍ ରୁ ଲଗଆଉଟ୍ କରନ୍ତୁ",
"download_as_txt": ".Txt ଭାବରେ ଡାଉନଲୋଡ୍ କରନ୍ତୁ", "download_as_txt": ".Txt ଭାବରେ ଡାଉନଲୋଡ୍ କରନ୍ତୁ",
"instance_auth_selection": "ପ୍ରାମାଣିକିକରଣ ଇନଷ୍ଟାନ୍ସ ଚୟନ", "instance_auth_selection": "ପ୍ରାମାଣିକିକରଣ ଇନଷ୍ଟାନ୍ସ",
"confirm_reset_preferences": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ଆପଣଙ୍କର ପସନ୍ଦଗୁଡିକ ପୁନଃ ସେଟ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି?", "confirm_reset_preferences": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ଆପଣଙ୍କର ପସନ୍ଦଗୁଡିକ ପୁନଃ ସେଟ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି?",
"status_page": "ସ୍ଥିତି", "status_page": "ସ୍ଥିତି",
"different_auth_instance": "ପ୍ରାମାଣିକିକରଣ ପାଇଁ ଏକ ଭିନ୍ନ ଉଦାହରଣ ବ୍ୟବହାର କରନ୍ତୁ", "different_auth_instance": "ପ୍ରାମାଣିକିକରଣ ପାଇଁ ଏକ ଭିନ୍ନ ଉଦାହରଣ ବ୍ୟବହାର କରନ୍ତୁ",
@ -119,7 +120,30 @@
"documentation": "ଡକ୍ୟୁମେଣ୍ଟେସନ୍", "documentation": "ଡକ୍ୟୁମେଣ୍ଟେସନ୍",
"instance_donations": "ଇନଷ୍ଟାଣ୍ଟ ଦାନ ଗୁଡ଼ିକ", "instance_donations": "ଇନଷ୍ଟାଣ୍ଟ ଦାନ ଗୁଡ଼ିକ",
"minimize_chapters_default": "ଡିଫଲ୍ଟ ଭାବରେ ଅଧ୍ୟାୟଗୁଡ଼ିକୁ କମ୍ କରନ୍ତୁ", "minimize_chapters_default": "ଡିଫଲ୍ଟ ଭାବରେ ଅଧ୍ୟାୟଗୁଡ଼ିକୁ କମ୍ କରନ୍ତୁ",
"no_valid_playlists": "ଫାଇଲ୍ ଟି ବୈଧ ପ୍ଲେଲିଷ୍ଟ ଧାରଣ କରେ ନାହିଁ!" "no_valid_playlists": "ଫାଇଲ୍ ଟି ବୈଧ ପ୍ଲେଲିଷ୍ଟ ଧାରଣ କରେ ନାହିଁ!",
"with_playlist": "ପ୍ଲେଲିଷ୍ଟ ସହିତ ଅଂଶୀଦାର କରନ୍ତୁ",
"bookmark_playlist": "ବୁକମାର୍କ",
"playlist_bookmarked": "ବୁକମାର୍କ ହୋଇଛି",
"min_segment_length": "ସର୍ବନିମ୍ନ ସେଗମେଣ୍ଟ ଲମ୍ବ (ସେକେଣ୍ଡରେ)",
"skip_button_only": "ସ୍କିପ୍ ବଟନ୍ ଦେଖାନ୍ତୁ",
"skip_automatically": "ସ୍ୱୟଂଚାଳିତ ଭାବରେ",
"skip_segment": "ସେଗମେଣ୍ଟକୁ ଏଡ଼ାଇଦିଅ",
"show_less": "କମ୍ ଦେଖାନ୍ତୁ",
"autoplay_next_countdown": "ପରବର୍ତ୍ତୀ ଭିଡିଓ ପର୍ଯ୍ୟନ୍ତ ଡିଫଲ୍ଟ କାଉଣ୍ଟଡାଉନ୍ (ସେକେଣ୍ଡରେ)",
"dismiss": "ବରଖାସ୍ତ",
"create_group": "ଗୋଷ୍ଠୀ ସୃଷ୍ଟି କରନ୍ତୁ",
"group_name": "ଗୋଷ୍ଠୀ ନାମ",
"enable_dearrow": "DeArrow କୁ ସକ୍ରିୟ କରନ୍ତୁ",
"show_search_suggestions": "ସନ୍ଧାନ ପ୍ରସ୍ତାବଗୁଡ଼ିକୁ ଦର୍ଶାନ୍ତୁ",
"delete_automatically": "ଏହା ପରେ ସ୍ୱୟଂଚାଳିତ ଭାବରେ ଅପସାରଣ କରନ୍ତୁ",
"okay": "ଠିକ୍ ଅଛି",
"edit_playlist": "ପ୍ଲେଲିଷ୍ଟକୁ ସମ୍ପାଦନ କରନ୍ତୁ",
"playlist_name": "ପ୍ଲେ-ଲିଷ୍ଟ ନାମ",
"generate_qrcode": "QR କୋଡ ସୃଷ୍ଟି କରନ୍ତୁ",
"chapters_layout_mobile": "ମୋବାଇଲରେ ଅଧ୍ୟାୟ ବିନ୍ୟାସ",
"auto_display_captions": "ଶୀର୍ଷକଗୁଡ଼ିକୁ ସ୍ୱୟଂଚାଳିତ ଭାବରେ ଦର୍ଶାନ୍ତୁ",
"playlist_description": "ପ୍ଲେଲିଷ୍ଟ ବର୍ଣ୍ଣନା",
"cancel": "ବାତିଲ କରନ୍ତୁ"
}, },
"comment": { "comment": {
"loading": "ମନ୍ତବ୍ୟ ଲୋଡ୍ ହେଉଛି ...", "loading": "ମନ୍ତବ୍ୟ ଲୋଡ୍ ହେଉଛି ...",
@ -135,7 +159,13 @@
"videos": "ଭିଡିଓ ଗୁଡିକ", "videos": "ଭିଡିଓ ଗୁଡିକ",
"ratings_disabled": "ମୂଲ୍ୟାୟନ ଅକ୍ଷମ ହୋଇଛି", "ratings_disabled": "ମୂଲ୍ୟାୟନ ଅକ୍ଷମ ହୋଇଛି",
"chapters": "ଅଧ୍ୟାୟ ଗୁଡ଼ିକ", "chapters": "ଅଧ୍ୟାୟ ଗୁଡ଼ିକ",
"live": "{0} ସିଧାପ୍ରସାରଣ" "live": "{0} ସିଧାପ୍ରସାରଣ",
"all": "ସମସ୍ତ",
"category": "ବର୍ଗ",
"visibility": "ଦୃଶ୍ୟମାନତା",
"license": "ଲାଇସେନ୍ସ",
"chapters_horizontal": "ଭୂସମାନ୍ତର",
"chapters_vertical": "ଭୂଲମ୍ବ"
}, },
"search": { "search": {
"did_you_mean": "ଆପଣ କହିବାକୁ ଚାହୁଁଛନ୍ତି କି: {0}?", "did_you_mean": "ଆପଣ କହିବାକୁ ଚାହୁଁଛନ୍ତି କି: {0}?",
@ -146,7 +176,8 @@
"videos": "ୟୁଟ୍ୟୁବ୍: ଭିଡିଓଗୁଡିକ", "videos": "ୟୁଟ୍ୟୁବ୍: ଭିଡିଓଗୁଡିକ",
"channels": "ୟୁଟ୍ୟୁବ୍: ଚ୍ୟାନେଲଗୁଡିକ", "channels": "ୟୁଟ୍ୟୁବ୍: ଚ୍ୟାନେଲଗୁଡିକ",
"music_songs": "ୟୁଟିଉବ୍ ସଙ୍ଗୀତ: ଗୀତ ଗୁଡ଼ିକ", "music_songs": "ୟୁଟିଉବ୍ ସଙ୍ଗୀତ: ଗୀତ ଗୁଡ଼ିକ",
"playlists": "ୟୁଟ୍ୟୁବ୍: ପ୍ଲେଲିଷ୍ଟଗୁଡିକ" "playlists": "ୟୁଟ୍ୟୁବ୍: ପ୍ଲେଲିଷ୍ଟଗୁଡିକ",
"music_artists": "ୱାଇଟି ମ୍ୟୁଜିକ: ଆର୍ଟିଷ୍ଟ"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "ସଦସ୍ୟତା: {0}" "subscribed_channels_count": "ସଦସ୍ୟତା: {0}"
@ -156,7 +187,13 @@
"copied": "କପି ହୋଇଛି!", "copied": "କପି ହୋଇଛି!",
"cannot_copy": "କପି କରିପାରିବ ନାହିଁ!", "cannot_copy": "କପି କରିପାରିବ ନାହିଁ!",
"page_not_found": "ପୃଷ୍ଠାଟି ମିଳିଲା ନାହିଁ", "page_not_found": "ପୃଷ୍ଠାଟି ମିଳିଲା ନାହିଁ",
"local_storage": "ଏହି କ୍ରିୟା ଲୋକାଲ୍ ଷ୍ଟୋରେଜ୍ ଆବଶ୍ୟକ କରେ, କୁକିଜ୍ ସକ୍ଷମ ଅଛି କି?" "local_storage": "ଏହି କ୍ରିୟା ଲୋକାଲ୍ ଷ୍ଟୋରେଜ୍ ଆବଶ୍ୟକ କରେ, କୁକିଜ୍ ସକ୍ଷମ ଅଛି କି?",
"register_no_email_note": "ଉପଯୋଗକର୍ତ୍ତା ନାମ ଭାବରେ ଏକ ଇ-ମେଲ୍ ବ୍ୟବହାର କରିବା ସୁପାରିଶ କରାଯାଏ ନାହିଁ । ଯେକୌଣସି ପ୍ରକାରେ ଅଗ୍ରଗତି କରନ୍ତୁ?",
"next_video_countdown": "{0} ସେକେଣ୍ଡ ରେ ପରବର୍ତ୍ତୀ ଭିଡିଓ ଖେଳିବାକୁ ଆରମ୍ଭ ହେବ",
"hours": "{amount} ଘଣ୍ଟା",
"days": "{amount} ଦିନ",
"weeks": "{amount} ସପ୍ତାହ",
"months": "{amount} ମାସ"
}, },
"preferences": { "preferences": {
"instance_name": "ଇନଷ୍ଟାନ୍ସ ନାମ", "instance_name": "ଇନଷ୍ଟାନ୍ସ ନାମ",
@ -169,6 +206,8 @@
}, },
"login": { "login": {
"password": "ପାସୱାର୍ଡ", "password": "ପାସୱାର୍ଡ",
"username": "ଉପଯୋଗକର୍ତ୍ତା ନାମ" "username": "ଉପଯୋଗକର୍ତ୍ତା ନାମ",
"passwords_incorrect": "ପ୍ରବେଶ ସଂକେତ ମେଳ ଖାଉନାହିଁ!",
"password_confirm": "ପ୍ରବେଶ ସଂକେତ ନିଶ୍ଚିତ କରନ୍ତୁ"
} }
} }

View File

@ -12,10 +12,14 @@
"account": "Konto", "account": "Konto",
"instance": "Instancja", "instance": "Instancja",
"livestreams": "Na żywo", "livestreams": "Na żywo",
"channels": "Kanały" "channels": "Kanały",
"bookmarks": "Zakładki",
"channel_groups": "Grupy kanałów",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "Obejrzyj na {0}" "watch_on": "Obejrzyj na {0}",
"failed": "Niepowodzenie z powodu kodu błędu {0}, przejrzyj logi, aby uzyskać więcej informacji"
}, },
"actions": { "actions": {
"subscribe": "Subskrybuj - {count}", "subscribe": "Subskrybuj - {count}",
@ -46,20 +50,20 @@
"audio_only": "Tylko audio", "audio_only": "Tylko audio",
"default_quality": "Domyślna jakość", "default_quality": "Domyślna jakość",
"buffering_goal": "Cel buforowania (w sekundach)", "buffering_goal": "Cel buforowania (w sekundach)",
"country_selection": "Wybór kraju", "country_selection": "Kraj",
"default_homepage": "Domyślna strona główna", "default_homepage": "Domyślna strona główna",
"show_comments": "Pokaż komentarze", "show_comments": "Pokaż komentarze",
"minimize_description_default": "Ukryj opis", "minimize_description_default": "Ukryj opis",
"store_watch_history": "Zapamiętaj historię oglądania", "store_watch_history": "Zapamiętaj historię oglądania",
"language_selection": "Wybór języka", "language_selection": "Język",
"instances_list": "Lista instancji", "instances_list": "Lista instancji",
"enabled_codecs": "Włączone kodeki (lista wielokrotnego wyboru)", "enabled_codecs": "Włączone kodeki (lista wielokrotnego wyboru)",
"instance_selection": "Wybór instancji", "instance_selection": "Instancja",
"show_more": "Pokaż więcej", "show_more": "Pokaż więcej",
"yes": "Tak", "yes": "Tak",
"no": "Nie", "no": "Nie",
"export_to_json": "Eksport do pliku JSON", "export_to_json": "Eksport do pliku JSON",
"import_from_json": "Import z pliku JSON/CSV", "import_from_json": "Import z pliku JSON",
"loop_this_video": "Zapętlaj ten film", "loop_this_video": "Zapętlaj ten film",
"auto_play_next_video": "Autoodtwarzanie następnego filmu", "auto_play_next_video": "Autoodtwarzanie następnego filmu",
"donations": "Wsparcie", "donations": "Wsparcie",
@ -67,10 +71,10 @@
"show_description": "Pokaż opis", "show_description": "Pokaż opis",
"minimize_recommendations": "Ukryj proponowane", "minimize_recommendations": "Ukryj proponowane",
"show_recommendations": "Pokaż proponowane", "show_recommendations": "Pokaż proponowane",
"disable_lbry": "Wyłącz LBRY dla streaming-u", "disable_lbry": "Wyłącz LBRY dla przesyłania strumieniowego",
"enable_lbry_proxy": "Włącz proxy dla LBRY", "enable_lbry_proxy": "Włącz proxy dla LBRY",
"view_ssl_score": "Pokaż ocenę SSL", "view_ssl_score": "Pokaż ocenę SSL",
"search": "Szukaj", "search": "Szukaj (Ctrl+K)",
"filter": "Filtruj", "filter": "Filtruj",
"loading": "Ładowanie...", "loading": "Ładowanie...",
"clear_history": "Wyczyść historię", "clear_history": "Wyczyść historię",
@ -92,7 +96,7 @@
"documentation": "Dokumentacja", "documentation": "Dokumentacja",
"instance_donations": "Darowizny na rzecz instancji", "instance_donations": "Darowizny na rzecz instancji",
"back_to_home": "Idź do strony głównej", "back_to_home": "Idź do strony głównej",
"instance_auth_selection": "Wybrana instancja autoryzacyjna", "instance_auth_selection": "Instancja uwierzytelniania",
"time_code": "Kod czasowy (w sekundach)", "time_code": "Kod czasowy (w sekundach)",
"show_markers": "Pokaż segmenty na odtwarzaczu", "show_markers": "Pokaż segmenty na odtwarzaczu",
"store_search_history": "Zapamiętaj historię wyszukiwania", "store_search_history": "Zapamiętaj historię wyszukiwania",
@ -100,7 +104,6 @@
"source_code": "Kod źródłowy", "source_code": "Kod źródłowy",
"show_chapters": "Rozdziały", "show_chapters": "Rozdziały",
"minimize_chapters_default": "Ukryj rozdziały", "minimize_chapters_default": "Ukryj rozdziały",
"rename_playlist": "Zmień nazwę playlisty",
"follow_link": "Otwórz link", "follow_link": "Otwórz link",
"minimize_comments_default": "Ukryj sekcję komentarzy", "minimize_comments_default": "Ukryj sekcję komentarzy",
"minimize_comments": "Ukryj komentarze", "minimize_comments": "Ukryj komentarze",
@ -113,14 +116,37 @@
"backup_preferences": "Pobierz kopię zapasową ustawień", "backup_preferences": "Pobierz kopię zapasową ustawień",
"download_as_txt": "Pobierz jako .txt", "download_as_txt": "Pobierz jako .txt",
"reset_preferences": "Zresetuj ustawienia", "reset_preferences": "Zresetuj ustawienia",
"new_playlist_name": "Nowa nazwa playlisty",
"share": "Udostępnij", "share": "Udostępnij",
"with_timecode": "Udostępnij z kodem czasowym", "with_timecode": "Udostępnij z kodem czasowym",
"piped_link": "Link Piped", "piped_link": "Link Piped",
"status_page": "Status", "status_page": "Status",
"reply_count": "{count} odpowiedzi", "reply_count": "{count} odpowiedzi",
"no_valid_playlists": "Ten plik nie zawiera poprawnych playlist!", "no_valid_playlists": "Ten plik nie zawiera poprawnych playlist!",
"with_playlist": "Udostępnij z 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ć",
"group_name": "Nazwa grupy",
"create_group": "Utwórz grupę",
"auto_display_captions": "Automatyczne wyświetlanie napisów",
"edit_playlist": "Edytuj playlistę",
"playlist_name": "Nazwa playlisty",
"playlist_description": "Opis playlisty",
"okay": "OK",
"cancel": "Anuluj",
"show_search_suggestions": "Pokaż sugestie wyszukiwania",
"chapters_layout_mobile": "Układ rozdziałów na urządzeniach mobilnych",
"delete_automatically": "Usuń automatycznie po",
"enable_dearrow": "Włącz DeArrow",
"generate_qrcode": "Wygeneruj kod QR",
"import_from_json_csv": "Import z pliku JSON/CSV",
"download_frame": "Pobierz klatkę"
}, },
"comment": { "comment": {
"pinned_by": "Przypięty przez {author}", "pinned_by": "Przypięty przez {author}",
@ -139,7 +165,9 @@
}, },
"login": { "login": {
"username": "Nazwa użytkownika", "username": "Nazwa użytkownika",
"password": "Hasło" "password": "Hasło",
"password_confirm": "Potwierdź hasło",
"passwords_incorrect": "Hasła się nie zgadzają!"
}, },
"video": { "video": {
"videos": "Filmy", "videos": "Filmy",
@ -148,8 +176,14 @@
"sponsor_segments": "Segmenty sponsorowane", "sponsor_segments": "Segmenty sponsorowane",
"ratings_disabled": "Ocenianie wyłączone", "ratings_disabled": "Ocenianie wyłączone",
"chapters": "Rozdziały", "chapters": "Rozdziały",
"live": "{0} Na żywo", "live": "{0} na żywo",
"shorts": "Krótkie wideo" "shorts": "Krótkie wideo",
"all": "Wszystkie",
"category": "Kategoria",
"chapters_horizontal": "Poziomy",
"chapters_vertical": "Pionowy",
"license": "Licencja",
"visibility": "Widoczność"
}, },
"search": { "search": {
"did_you_mean": "Czy chodziło ci o: {0}?", "did_you_mean": "Czy chodziło ci o: {0}?",
@ -160,14 +194,21 @@
"music_songs": "YT Music: Utwory", "music_songs": "YT Music: Utwory",
"music_videos": "YT Music: Teledyski", "music_videos": "YT Music: Teledyski",
"music_albums": "YT Music: Albumy", "music_albums": "YT Music: Albumy",
"music_playlists": "YT Music: Playlisty" "music_playlists": "YT Music: Playlisty",
"music_artists": "YT Music: Wykonawcy"
}, },
"info": { "info": {
"cannot_copy": "Nie można skopiować!", "cannot_copy": "Nie można skopiować!",
"copied": "Skopiowano!", "copied": "Skopiowano!",
"page_not_found": "Strona nie znaleziona", "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.", "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?" "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",
"days": "{amount} dni",
"weeks": "{amount} tygodnie",
"hours": "{amount} godziny",
"months": "{amount} miesiące"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Licznik subskrybcji: {0}" "subscribed_channels_count": "Licznik subskrybcji: {0}"

View File

@ -1,174 +1,216 @@
{ {
"titles": { "titles": {
"trending": "Tendências", "trending": "Tendências",
"preferences": "Configurações", "preferences": "Preferências",
"subscriptions": "Subscrições", "subscriptions": "Subscrições",
"login": "Iniciar Sessão", "login": "Iniciar sessão",
"register": "Registar", "register": "Registar",
"history": "Histórico", "history": "Histórico",
"feed": "Conteúdo", "feed": "Conteúdo",
"playlists": "Listas de Reprodução", "playlists": "Listas de reprodução",
"account": "Conta", "account": "Conta",
"instance": "Instância", "instance": "Instância",
"player": "Reprodutor", "player": "Reprodutor",
"livestreams": "Transmissões ao vivo", "livestreams": "Emissões em direto",
"channels": "Canais" "channels": "Canais",
"bookmarks": "Marcadores",
"channel_groups": "Grupos de canais",
"dearrow": "DeArrow"
}, },
"actions": { "actions": {
"sort_by": "Ordenar por:", "sort_by": "Ordenar por:",
"most_recent": "Mais Recente", "most_recent": "Mais recentes",
"least_recent": "Menos Recente", "least_recent": "Mais antigos",
"channel_name_asc": "Nome do Canal (A-Z)", "channel_name_asc": "Nome do canal (A-Z)",
"back": "Voltar", "back": "Recuar",
"uses_api_from": "Utiliza a \"API\" de ", "uses_api_from": "Utiliza a \"API\" de ",
"enable_sponsorblock": "Ativar \"SponsorBlock\"", "enable_sponsorblock": "Ativar \"SponsorBlock\"",
"skip_intro": "Saltar Intermissão/Animação de Introdução", "skip_intro": "Ignorar intermissão/animação de introdução",
"skip_outro": "Saltar \"Endcards\"/Créditos", "skip_outro": "Ignorar \"Endcards\"/Créditos",
"skip_preview": "Saltar Pré-Visualização/Recapitulação", "skip_preview": "Ignorar pré-visualização/recapitulando",
"auto": "Automático", "auto": "Automático",
"dark": "Escuro", "dark": "Escuro",
"autoplay_video": "Reproduzir Vídeo Automaticamente", "autoplay_video": "Reproduzir vídeos automaticamente",
"audio_only": "Apenas Áudio", "audio_only": "Apenas áudio",
"default_quality": "Qualidade Padrão", "default_quality": "Qualidade padrão",
"country_selection": "Seleção de País", "country_selection": "País",
"default_homepage": "Página Inicial Padrão", "default_homepage": "Página inicial padrão",
"show_comments": "Mostrar Comentários", "show_comments": "Mostrar comentários",
"minimize_description_default": "Minimizar Descrição por defeito", "minimize_description_default": "Minimizar descrição por omissão",
"store_watch_history": "Guardar Histórico de Visualizações", "store_watch_history": "Guardar histórico de visualizações",
"instances_list": "Lista de Instâncias", "instances_list": "Lista de instâncias",
"enabled_codecs": "\"Codecs\" Activados (Vários)", "enabled_codecs": "Codificadores ativados (vários)",
"instance_selection": "Seleção de Instância", "instance_selection": "Instância",
"show_more": "Mostrar Mais", "show_more": "Mostrar mais",
"import_from_json": "Importar de JSON/CSV", "import_from_json": "Importar de JSON",
"export_to_json": "Exportar para JSON", "export_to_json": "Exportar para JSON",
"loop_this_video": "Repetir este Vídeo", "loop_this_video": "Repetir este vídeo",
"auto_play_next_video": "Reproduzir Automaticamente o próximo Vídeo", "auto_play_next_video": "Reproduzir vídeo seguinte automaticamente",
"donations": "Doações de desenvolvimento", "donations": "Doações para o desenvolvimento",
"minimize_description": "Minimizar Descrição", "minimize_description": "Minimizar descrição",
"show_description": "Mostrar Descrição", "show_description": "Mostrar descrição",
"show_recommendations": "Mostrar Recomendações", "show_recommendations": "Mostrar recomendações",
"disable_lbry": "Desactivar \"LBRY\" para Transmissão", "disable_lbry": "Desativar \"LBRY\" para emissões",
"enable_lbry_proxy": "Activar \"Proxy\" para \"LBRY\"", "enable_lbry_proxy": "Ativar proxy para \"LBRY\"",
"view_ssl_score": "Ver Pontuação \"SSL\"", "view_ssl_score": "Ver avaliação \"SSL\"",
"search": "Procurar", "search": "Pesquisa (Ctrl+K)",
"filter": "Filtrar", "filter": "Filtrar",
"loading": "A Carregar...", "loading": "A carregar...",
"clear_history": "Limpar Histórico", "clear_history": "Limpar histórico",
"subscribe": "Subscrever - {count}", "subscribe": "Subscrever - {count}",
"unsubscribe": "Anular subscrição - {count}", "unsubscribe": "Anular subscrição - {count}",
"view_subscriptions": "Ver Subscrições", "view_subscriptions": "Ver subscrições",
"channel_name_desc": "Nome do Canal (Z-A)", "channel_name_desc": "Nome do canal (Z-A)",
"skip_sponsors": "Saltar Patrocínios", "skip_sponsors": "Ignorar patrocínios",
"yes": "Sim", "yes": "Sim",
"skip_non_music": "Saltar Música: Secção Não-Musical", "skip_non_music": "Ignorar música: secção não musical",
"no": "Não", "no": "Não",
"theme": "Tema", "theme": "Tema",
"language_selection": "Seleção de Idioma", "language_selection": "Idioma",
"minimize_recommendations": "Minimizar Recomendações", "minimize_recommendations": "Minimizar recomendações",
"light": "Claro", "light": "Claro",
"hide_replies": "Ocultar Respostas", "hide_replies": "Ocultar respostas",
"load_more_replies": "Carregar mais Respostas", "load_more_replies": "Carregar mais respostas",
"skip_highlight": "Saltar Destaque", "skip_highlight": "Ignorar destaques",
"skip_interaction": "Saltar Lembrete de Interação (Subscreve)", "skip_interaction": "Ignorar lembrete de interação (subscrição)",
"skip_self_promo": "Saltar Promoção Não Paga/Auto-Promoção", "skip_self_promo": "Ignorar promoção gratuita/auto-promoção",
"buffering_goal": "Objetivo de \"Buffering\" (em segundos)", "buffering_goal": "Objetivo de 'buffer' (em segundos)",
"skip_filler_tangent": "Saltar Tangente \"Filler\"", "skip_filler_tangent": "Ignorar segmentos de preenchimento",
"add_to_playlist": "Adicionar à lista de reprodução", "add_to_playlist": "Adicionar à lista de reprodução",
"delete_playlist": "Apagar Lista de Reprodução", "delete_playlist": "Apagar lista de reprodução",
"select_playlist": "Selecionar uma Lista de Reprodução", "select_playlist": "Selecionar uma lista de reprodução",
"delete_playlist_confirm": "Apagar esta 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", "please_select_playlist": "Selecione uma lista de reprodução",
"delete_playlist_video_confirm": "Remover o vídeo da lista de reprodução?", "delete_playlist_video_confirm": "Remover vídeo da lista de reprodução?",
"remove_from_playlist": "Remover 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_success": "Clonada com sucesso!",
"clone_playlist": "Clonar Lista de Reprodução", "clone_playlist": "Clonar lista de reprodução",
"show_markers": "Mostrar Marcadores no Leitor", "show_markers": "Mostrar marcas no reprodutor",
"delete_account": "Apagar Conta", "delete_account": "Apagar conta",
"logout": "Terminar sessão neste aparelho", "logout": "Terminar sessão neste dispositivo",
"minimize_recommendations_default": "Minimizar Recomendações por defeito", "minimize_recommendations_default": "Minimizar recomendações por omissão",
"invalidate_session": "Terminar sessão em todos os aparelhos", "invalidate_session": "Terminar sessão em todos os dispositivos",
"different_auth_instance": "Usar uma instância diferente para autenticação", "different_auth_instance": "Usar uma instância diferente para autenticação",
"instance_auth_selection": "Selecção da Instância para Autenticação", "instance_auth_selection": "Instância para autenticação",
"confirm_reset_preferences": "Tem a certeza que quer redefinir as suas configurações?", "confirm_reset_preferences": "Tem a certeza de que deseja repor as preferências?",
"download_as_txt": "Descarregar como .txt", "download_as_txt": "Descarregar como .txt",
"reset_preferences": "Redefinir preferências", "reset_preferences": "Repor preferências",
"restore_preferences": "Restaurar configurações", "restore_preferences": "Restaurar preferências",
"follow_link": "Seguir ligação", "follow_link": "Seguir ligação",
"piped_link": "Ligação do Piped", "piped_link": "Ligação do Piped",
"backup_preferences": "Exportar configurações", "backup_preferences": "Exportar preferências",
"store_search_history": "Armazenar Histórico de Pesquisa", "store_search_history": "Guardar histórico de pesquisas",
"hide_watched": "Ocultar vídeos assistidos no feed", "hide_watched": "Ocultar do feed os vídeos visualizados",
"documentation": "Documentação", "documentation": "Documentação",
"status_page": "Estado", "status_page": "Estado",
"source_code": "Código-fonte", "source_code": "Código-fonte",
"instance_donations": "Doações de instâncias", "instance_donations": "Doações de instâncias",
"minimize_chapters_default": "Minimizar Capítulos por padrão", "minimize_chapters_default": "Minimizar capítulos por omissão",
"show_watch_on_youtube": "Mostrar Botão Assistir no YouTube", "show_watch_on_youtube": "Mostrar botão Ver no YouTube",
"new_playlist_name": "Novo nome da lista de reprodução", "minimize_comments": "Minimizar comentários",
"minimize_comments": "Minimizar Comentários",
"back_to_home": "Voltar ao início", "back_to_home": "Voltar ao início",
"rename_playlist": "Renomear",
"copy_link": "Copiar ligação", "copy_link": "Copiar ligação",
"time_code": "Código de tempo (em segundos)", "time_code": "Código de tempo (em segundos)",
"minimize_comments_default": "Minimizar Comentários por defeito", "minimize_comments_default": "Minimizar comentários por omissão",
"share": "Partilhar", "share": "Partilhar",
"with_timecode": "Partilhar com código de tempo", "with_timecode": "Partilhar com código de tempo",
"show_chapters": "Capítulos", "show_chapters": "Capítulos",
"reply_count": "{count} respostas", "reply_count": "{count} respostas",
"no_valid_playlists": "O ficheiro não contém listas de reprodução válidas!" "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 Ignorar",
"skip_automatically": "Automaticamente",
"min_segment_length": "Tamanho mínimo do segmento (segundos)",
"skip_segment": "Ignorar segmento",
"show_less": "Mostrar menos",
"dismiss": "Ignorar",
"autoplay_next_countdown": "Contagem decrescente até ao próximo vídeo (em segundos)",
"create_group": "Criar grupo",
"group_name": "Nome do grupo",
"auto_display_captions": "Mostrar legendas",
"playlist_description": "Descrição da lista de reprodução",
"okay": "Ok",
"cancel": "Cancelar",
"edit_playlist": "Editar lista de reprodução",
"playlist_name": "Nome da lista de reprodução",
"show_search_suggestions": "Mostrar sugestões de pesquisa",
"chapters_layout_mobile": "Aplicações recentemente adicionadas",
"enable_dearrow": "Ativar o DeArrow",
"delete_automatically": "Eliminar automaticamente após",
"generate_qrcode": "Gerar código QR",
"import_from_json_csv": "Importar de JSON/CSV",
"download_frame": "Quadro de transferência"
}, },
"preferences": { "preferences": {
"instance_name": "Nome da Instância", "instance_name": "Nome da instância",
"instance_locations": "Localizações da Instância", "instance_locations": "Localizações da instância",
"ssl_score": "Pontuação \"SSL\"", "ssl_score": "Avaliação SSL",
"has_cdn": "Tem \"CDN\"?", "has_cdn": "Tem CDN?",
"version": "Versão", "version": "Versão",
"registered_users": "Utilizadores Registados", "registered_users": "Utilizadores registados",
"up_to_date": "Atualizada?" "up_to_date": "Atualizada?"
}, },
"login": { "login": {
"password": "Palavra-passe", "password": "Palavra-passe",
"username": "Nome de utilizador" "username": "Nome de utilizador",
"passwords_incorrect": "As palavras-passe não coincidem!",
"password_confirm": "Confirmar a palavra-passe"
}, },
"video": { "video": {
"videos": "Vídeos", "videos": "Vídeos",
"views": "{views} visualizações", "views": "{views} visualizações",
"watched": "Visto", "watched": "Visto",
"sponsor_segments": "Segmentos Patrocinados", "sponsor_segments": "Segmentos patrocinados",
"ratings_disabled": "Classificações Desactivadas", "ratings_disabled": "Avaliações desativadas",
"chapters": "Capítulos", "chapters": "Capítulos",
"live": "{0} em Direto", "live": "{0} em direto",
"shorts": "\"Shorts\"" "shorts": "Curtos",
"all": "Todos",
"category": "Categoria",
"chapters_horizontal": "Horizontal",
"chapters_vertical": "Vertical",
"license": "Licença",
"visibility": "Visibilidade"
}, },
"search": { "search": {
"did_you_mean": "Será que querias dizer: {0}?", "did_you_mean": "Será que queria dizer: {0}?",
"all": "YouTube: Tudo", "all": "YouTube: Tudo",
"videos": "YouTube: Vídeos", "videos": "YouTube: Vídeos",
"channels": "YouTube: Canais", "channels": "YouTube: Canais",
"music_songs": "YT Music: Músicas", "music_songs": "YT Music: Músicas",
"music_videos": "YT Music: Vídeos", "music_videos": "YT Music: Vídeos",
"music_albums": "YT Music: Álbuns", "music_albums": "YT Music: Álbuns",
"music_playlists": "YT Music: Listas de Reprodução", "music_playlists": "YT Music: Listas de reprodução",
"playlists": "YouTube: Listas de Reprodução" "playlists": "YouTube: Listas de reprodução",
"music_artists": "YT Music: Artistas"
}, },
"player": { "player": {
"watch_on": "Ver em {0}" "watch_on": "Ver em {0}",
"failed": "Falha com o código de erro {0}, ver registos para mais informações"
}, },
"comment": { "comment": {
"pinned_by": "Afixado por {author}", "pinned_by": "Afixado por {author}",
"disabled": "Os comentários estão desactivados pelo dono do canal.", "disabled": "O dono do canal desativou os comentários.",
"loading": "A carregar comentários...", "loading": "A carregar comentários...",
"user_disabled": "Os comentários estão desactivados nas definições." "user_disabled": "Os comentários estão desativados nas definições."
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Subscrito a: {0}" "subscribed_channels_count": "Subscreveu: {0}"
}, },
"info": { "info": {
"copied": "Copiada!", "copied": "Copiada!",
"cannot_copy": "Não foi possível copiar!", "cannot_copy": "Não foi possível copiar!",
"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?", "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." "preferences_note": "Nota: as preferências são guardadas no armazenamento local do seu navegador. Se limpar os dados de navegação, também limpa as preferências.",
"register_no_email_note": "Não recomendamos utilizar um endereço de e-mail como nome de utilizador. Continuar?",
"next_video_countdown": "O próximo vídeo será reproduzido dentro de {0} segundos",
"hours": "{quantidade} hora(s)",
"days": "{quantidade} dia(s)",
"weeks": "{quantidade} semana(s)",
"months": "{quantidade} mês(es)"
} }
} }

View File

@ -10,7 +10,7 @@
"dark": "Escuro", "dark": "Escuro",
"light": "Claro", "light": "Claro",
"show_comments": "Exibir Comentários", "show_comments": "Exibir Comentários",
"country_selection": "Seleção de País", "country_selection": "País",
"default_homepage": "Página Inicial Padrão", "default_homepage": "Página Inicial Padrão",
"default_quality": "Qualidade Padrão", "default_quality": "Qualidade Padrão",
"autoplay_video": "Reprodução Automática", "autoplay_video": "Reprodução Automática",
@ -34,7 +34,7 @@
"skip_non_music": "Pular Música: Seção não Musical", "skip_non_music": "Pular Música: Seção não Musical",
"skip_filler_tangent": "Pular Enchimento Tangencial", "skip_filler_tangent": "Pular Enchimento Tangencial",
"enabled_codecs": "Codecs Ativados (Múltiplos)", "enabled_codecs": "Codecs Ativados (Múltiplos)",
"language_selection": "Seleção de Idioma", "language_selection": "Idioma",
"yes": "Sim", "yes": "Sim",
"show_more": "Mostrar Mais", "show_more": "Mostrar Mais",
"export_to_json": "Exportar para JSON", "export_to_json": "Exportar para JSON",
@ -55,14 +55,14 @@
"view_ssl_score": "Ver Pontuação SSL", "view_ssl_score": "Ver Pontuação SSL",
"disable_lbry": "Desativar LBRY para Streaming", "disable_lbry": "Desativar LBRY para Streaming",
"enable_lbry_proxy": "Ativar Proxy para LBRY", "enable_lbry_proxy": "Ativar Proxy para LBRY",
"import_from_json": "Importar de JSON/CSV", "import_from_json": "Importar de JSON",
"loop_this_video": "Repetir este Vídeo", "loop_this_video": "Repetir este Vídeo",
"instances_list": "Lista de Instâncias", "instances_list": "Lista de Instâncias",
"clear_history": "Limpar Histórico", "clear_history": "Limpar Histórico",
"search": "Pesquisar", "search": "Pesquisar (Ctrl+K)",
"no": "Não", "no": "Não",
"show_description": "Exibir Descrição", "show_description": "Exibir Descrição",
"instance_selection": "Seleção de Instância", "instance_selection": "Instância",
"auto_play_next_video": "Autorreproduzir Próximo Vídeo", "auto_play_next_video": "Autorreproduzir Próximo Vídeo",
"filter": "Filtro", "filter": "Filtro",
"store_watch_history": "Salvar Histórico de Exibição", "store_watch_history": "Salvar Histórico de Exibição",
@ -81,14 +81,12 @@
"status_page": "Estado", "status_page": "Estado",
"source_code": "Código fonte", "source_code": "Código fonte",
"instance_donations": "Doações de instâncias", "instance_donations": "Doações de instâncias",
"instance_auth_selection": "Seleção de Instância de Autenticação", "instance_auth_selection": "Instância de Autenticação",
"clone_playlist_success": "Clonada com sucesso!", "clone_playlist_success": "Clonada com sucesso!",
"download_as_txt": "Baixar como .txt", "download_as_txt": "Baixar como .txt",
"restore_preferences": "Restaurar preferências", "restore_preferences": "Restaurar preferências",
"back_to_home": "Voltar ao início", "back_to_home": "Voltar ao início",
"share": "Compartilhar", "share": "Compartilhar",
"rename_playlist": "Renomear playlist",
"new_playlist_name": "Novo nome da playlist",
"with_timecode": "Compartilhar com código de tempo", "with_timecode": "Compartilhar com código de tempo",
"piped_link": "Link do Piped", "piped_link": "Link do Piped",
"follow_link": "Seguir link", "follow_link": "Seguir link",
@ -102,7 +100,31 @@
"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", "minimize_chapters_default": "Minimizar Capítulos por padrão",
"no_valid_playlists": "O arquivo não contém playlists válidas!", "no_valid_playlists": "O arquivo não contém playlists válidas!",
"with_playlist": "Compartilhar com playlist" "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",
"cancel": "Cancelar",
"edit_playlist": "Editar playlist",
"playlist_description": "Descrição da playlist",
"okay": "Ok",
"playlist_name": "Nome da playlist",
"auto_display_captions": "Exibição Automática de Legendas",
"create_group": "Criar grupo",
"group_name": "Nome do grupo",
"show_search_suggestions": "Mostrar sugestões de pesquisa",
"chapters_layout_mobile": "Layout dos Capítulos no Celular",
"delete_automatically": "Deletar automaticamente após",
"generate_qrcode": "Gerar código QR",
"enable_dearrow": "Ativar DeArrow",
"import_from_json_csv": "Importar de JSON/CSV",
"download_frame": "Baixar quadro"
}, },
"titles": { "titles": {
"history": "Histórico", "history": "Histórico",
@ -117,10 +139,14 @@
"player": "Player", "player": "Player",
"account": "Conta", "account": "Conta",
"channels": "Canais", "channels": "Canais",
"livestreams": "Transmissões ao vivo" "livestreams": "Transmissões ao vivo",
"bookmarks": "Favoritos",
"channel_groups": "Grupos de Canais",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "Assistir no {0}" "watch_on": "Assistir no {0}",
"failed": "Falhou com código de erro {0}, veja os logs para mais informações"
}, },
"comment": { "comment": {
"pinned_by": "Fixado por {author}", "pinned_by": "Fixado por {author}",
@ -139,7 +165,9 @@
}, },
"login": { "login": {
"username": "Nome de usuário", "username": "Nome de usuário",
"password": "Senha" "password": "Senha",
"password_confirm": "Confirme senha",
"passwords_incorrect": "As senhas não correspondem!"
}, },
"video": { "video": {
"videos": "Vídeos", "videos": "Vídeos",
@ -149,7 +177,13 @@
"watched": "Assistido", "watched": "Assistido",
"ratings_disabled": "Avaliações Desativadas", "ratings_disabled": "Avaliações Desativadas",
"sponsor_segments": "Segmentos de Patrocinadores", "sponsor_segments": "Segmentos de Patrocinadores",
"shorts": "Shorts" "shorts": "Shorts",
"all": "Todos",
"category": "Categoria",
"chapters_horizontal": "Horizontal",
"chapters_vertical": "Vertical",
"license": "Licença",
"visibility": "Visibilidade"
}, },
"search": { "search": {
"did_you_mean": "Você quis dizer: {0}?", "did_you_mean": "Você quis dizer: {0}?",
@ -160,14 +194,21 @@
"music_videos": "YT Music: Vídeos", "music_videos": "YT Music: Vídeos",
"music_albums": "YT Music: Álbuns", "music_albums": "YT Music: Álbuns",
"music_playlists": "YT Music: Playlists", "music_playlists": "YT Music: Playlists",
"all": "YouTube: Tudo" "all": "YouTube: Tudo",
"music_artists": "YT Music: Artistas"
}, },
"info": { "info": {
"copied": "Copiado!", "copied": "Copiado!",
"cannot_copy": "Não foi possível copiar!", "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.", "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?" "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",
"hours": "{amount} hora(s)",
"days": "{amount} dia(s)",
"weeks": "{amount} semana(s)",
"months": "{amount} mês/meses"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Inscrito em: {0}" "subscribed_channels_count": "Inscrito em: {0}"

View File

@ -1,135 +1,158 @@
{ {
"titles": { "titles": {
"preferences": "Configurações", "preferences": "Preferências",
"history": "Histórico", "history": "Histórico",
"feed": "Conteúdo", "feed": "Conteúdo",
"subscriptions": "Subscrições", "subscriptions": "Subscrições",
"trending": "Tendências", "trending": "Tendências",
"login": "Iniciar Sessão", "login": "Iniciar sessão",
"register": "Registar", "register": "Registar",
"playlists": "Listas de Reprodução", "playlists": "Listas de reprodução",
"account": "Conta", "account": "Conta",
"instance": "Instância", "instance": "Instância",
"player": "Reprodutor", "player": "Reprodutor",
"livestreams": "Transmissões ao vivo", "livestreams": "Emissões em direto",
"channels": "Canais" "channels": "Canais",
"bookmarks": "Marcadores",
"channel_groups": "Grupos de canais",
"dearrow": "DeArrow"
}, },
"actions": { "actions": {
"view_subscriptions": "Ver Subscrições", "view_subscriptions": "Ver subscrições",
"sort_by": "Ordenar por:", "sort_by": "Ordenar por:",
"most_recent": "Mais Recente", "most_recent": "Mais recentes",
"least_recent": "Menos Recente", "least_recent": "Mais antigos",
"channel_name_asc": "Nome do Canal (A-Z)", "channel_name_asc": "Nome do canal (A-Z)",
"channel_name_desc": "Nome do Canal (Z-A)", "channel_name_desc": "Nome do canal (Z-A)",
"uses_api_from": "Utiliza a \"API\" de ", "uses_api_from": "Utiliza a \"API\" de ",
"enable_sponsorblock": "Ativar \"SponsorBlock\"", "enable_sponsorblock": "Ativar \"SponsorBlock\"",
"skip_sponsors": "Saltar Patrocínios", "skip_sponsors": "Ignorar patrocínios",
"skip_intro": "Saltar Intermissão/Animação de Introdução", "skip_intro": "Ignorar intermissão/animação de introdução",
"skip_outro": "Saltar \"Endcards\"/Créditos", "skip_outro": "Ignorar \"Endcards\"/Créditos",
"skip_preview": "Saltar Pré-Visualização/Recapitulação", "skip_preview": "Ignorar pré-visualização/recapitulando",
"skip_interaction": "Saltar Lembrete de Interação (Subscreve)", "skip_interaction": "Ignorar lembrete de interação (subscrição)",
"skip_self_promo": "Saltar Promoção Não Paga/Auto-Promoção", "skip_self_promo": "Ignorar promoção gratuita/auto-promoção",
"skip_non_music": "Saltar Música: Secção Não-Musical", "skip_non_music": "Ignorar música: secção não musical",
"skip_highlight": "Saltar Destaque", "skip_highlight": "Ignorar destaques",
"skip_filler_tangent": "Saltar Tangente \"Filler\"", "skip_filler_tangent": "Ignorar segmentos de preenchimento",
"theme": "Tema", "theme": "Tema",
"auto": "Automático", "auto": "Automático",
"dark": "Escuro", "dark": "Escuro",
"light": "Claro", "light": "Claro",
"buffering_goal": "Objetivo de \"Buffering\" (em segundos)", "buffering_goal": "Objetivo de 'buffer' (em segundos)",
"country_selection": "Seleção de País", "country_selection": "País",
"default_homepage": "Página Inicial Padrão", "default_homepage": "Página inicial padrão",
"show_comments": "Mostrar Comentários", "show_comments": "Mostrar comentários",
"minimize_description_default": "Minimizar Descrição por defeito", "minimize_description_default": "Minimizar descrição por omissão",
"store_watch_history": "Guardar Histórico de Visualizações", "store_watch_history": "Guardar histórico de visualizações",
"language_selection": "Seleção de Idioma", "language_selection": "Idioma",
"enabled_codecs": "\"Codecs\" Activados (Vários)", "enabled_codecs": "Codificadores ativados (vários)",
"instance_selection": "Seleção de Instância", "instance_selection": "Instância",
"show_more": "Mostrar Mais", "show_more": "Mostrar mais",
"import_from_json": "Importar de JSON/CSV", "import_from_json": "Importar de JSON/CSV",
"loop_this_video": "Repetir este Vídeo", "loop_this_video": "Repetir este vídeo",
"auto_play_next_video": "Reproduzir Automaticamente o próximo Vídeo", "auto_play_next_video": "Reproduzir vídeo seguinte automaticamente",
"donations": "Doações de desenvolvimento", "donations": "Doações para o desenvolvimento",
"minimize_description": "Minimizar Descrição", "minimize_description": "Minimizar descrição",
"show_description": "Mostrar Descrição", "show_description": "Mostrar descrição",
"minimize_recommendations": "Minimizar Recomendações", "minimize_recommendations": "Minimizar recomendações",
"show_recommendations": "Mostrar Recomendações", "show_recommendations": "Mostrar recomendações",
"view_ssl_score": "Ver Pontuação \"SSL\"", "view_ssl_score": "Ver avaliação \"SSL\"",
"search": "Procurar", "search": "Pesquisa (Ctrl+K)",
"hide_replies": "Ocultar Respostas", "hide_replies": "Ocultar respostas",
"load_more_replies": "Carregar mais Respostas", "load_more_replies": "Carregar mais respostas",
"unsubscribe": "Anular subscrição - {count}", "unsubscribe": "Anular subscrição - {count}",
"subscribe": "Subscrever - {count}", "subscribe": "Subscrever - {count}",
"back": "Voltar", "back": "Recuar",
"audio_only": "Apenas Áudio", "audio_only": "Apenas áudio",
"default_quality": "Qualidade Padrão", "default_quality": "Qualidade padrão",
"instances_list": "Lista de Instâncias", "instances_list": "Lista de instâncias",
"export_to_json": "Exportar para JSON", "export_to_json": "Exportar para JSON",
"autoplay_video": "Reproduzir Vídeo Automaticamente", "autoplay_video": "Reproduzir vídeos automaticamente",
"yes": "Sim", "yes": "Sim",
"enable_lbry_proxy": "Activar \"Proxy\" para \"LBRY\"", "enable_lbry_proxy": "Ativar proxy para \"LBRY\"",
"no": "Não", "no": "Não",
"filter": "Filtrar", "filter": "Filtrar",
"clear_history": "Limpar Histórico", "clear_history": "Limpar histórico",
"disable_lbry": "Desactivar \"LBRY\" para Transmissão", "disable_lbry": "Desativar \"LBRY\" para emissões",
"loading": "A Carregar...", "loading": "A carregar...",
"please_select_playlist": "Selecionar uma lista de reprodução se faz favor", "please_select_playlist": "Selecione uma lista de reprodução",
"select_playlist": "Selecionar uma Lista de Reprodução", "select_playlist": "Selecionar uma lista de reprodução",
"add_to_playlist": "Adicionar à lista de reprodução", "add_to_playlist": "Adicionar à lista de reprodução",
"delete_playlist": "Apagar Lista de Reprodução", "delete_playlist": "Apagar lista de reprodução",
"download_as_txt": "Descarregar como .txt", "download_as_txt": "Descarregar como .txt",
"delete_playlist_confirm": "Apagar esta lista de reprodução?", "delete_playlist_confirm": "Apagar esta lista de reprodução?",
"show_markers": "Mostrar Marcadores no Leitor", "show_markers": "Mostrar marcas no reprodutor",
"remove_from_playlist": "Remover da lista de reprodução", "remove_from_playlist": "Remover da lista de reprodução",
"delete_playlist_video_confirm": "Remover o vídeo da lista de reprodução?", "delete_playlist_video_confirm": "Remover vídeo da lista de reprodução?",
"create_playlist": "Criar Lista de Reprodução", "create_playlist": "Criar lista de reprodução",
"delete_account": "Apagar Conta", "delete_account": "Apagar conta",
"logout": "Terminar sessão neste aparelho", "logout": "Terminar sessão neste dispositivo",
"minimize_recommendations_default": "Minimizar Recomendações por defeito", "minimize_recommendations_default": "Minimizar recomendações por omissão",
"different_auth_instance": "Usar uma instância diferente para autenticação", "different_auth_instance": "Usar uma instância diferente para autenticação",
"instance_auth_selection": "Selecção da Instância para Autenticação", "instance_auth_selection": "Instância de autenticação",
"invalidate_session": "Terminar sessão em todos os aparelhos", "invalidate_session": "Terminar sessão em todos os dispositivos",
"clone_playlist": "Clonar Lista de Reprodução", "clone_playlist": "Clonar lista de reprodução",
"clone_playlist_success": "Clonada com sucesso!", "clone_playlist_success": "Clonada com sucesso!",
"rename_playlist": "Renomear", "restore_preferences": "Restaurar preferências",
"restore_preferences": "Restaurar configurações", "confirm_reset_preferences": "Tem a certeza de que deseja repor as preferências?",
"confirm_reset_preferences": "Tem a certeza que quer redefinir as suas configurações?",
"new_playlist_name": "Novo nome da lista de reprodução",
"share": "Partilhar", "share": "Partilhar",
"with_timecode": "Partilhar com código de tempo", "with_timecode": "Partilhar com código de tempo",
"piped_link": "Ligação do Piped", "piped_link": "Ligação do Piped",
"follow_link": "Seguir ligação", "follow_link": "Seguir ligação",
"copy_link": "Copiar ligação", "copy_link": "Copiar ligação",
"time_code": "Código de tempo (em segundos)", "time_code": "Código de tempo (em segundos)",
"reset_preferences": "Redefinir preferências", "reset_preferences": "Repor preferências",
"backup_preferences": "Exportar configurações", "backup_preferences": "Exportar preferências",
"back_to_home": "Voltar ao início", "back_to_home": "Voltar ao início",
"minimize_comments_default": "Minimizar Comentários por defeito", "minimize_comments_default": "Minimizar comentários por omissão",
"store_search_history": "Armazenar Histórico de Pesquisa", "store_search_history": "Histórico de pesquisa da loja",
"minimize_chapters_default": "Minimizar Capítulos por padrão", "minimize_chapters_default": "Minimizar capítulos por omissão",
"show_watch_on_youtube": "Mostrar Botão Assistir no YouTube", "show_watch_on_youtube": "Mostrar botão Ver no YouTube",
"show_chapters": "Capítulos", "show_chapters": "Capítulos",
"hide_watched": "Ocultar vídeos assistidos no feed", "hide_watched": "Ocultar do feed os vídeos visualizados",
"documentation": "Documentação", "documentation": "Documentação",
"status_page": "Estado", "status_page": "Estado",
"minimize_comments": "Minimizar Comentários", "minimize_comments": "Minimizar comentários",
"reply_count": "{count} respostas", "reply_count": "{count} respostas",
"source_code": "Código-fonte", "source_code": "Código-fonte",
"instance_donations": "Doações de instâncias", "instance_donations": "Doações de instâncias",
"no_valid_playlists": "O ficheiro não contém listas de reprodução válidas!" "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 Ignorar",
"skip_automatically": "Automaticamente",
"min_segment_length": "Tamanho mínimo do segmento (segundos)",
"skip_segment": "Ignorar segmento",
"show_less": "Mostrar menos",
"autoplay_next_countdown": "Contagem decrescente até ao próximo vídeo (em segundos)",
"dismiss": "Ignorar",
"create_group": "Criar grupo",
"group_name": "Nome do grupo",
"auto_display_captions": "Mostrar legendas",
"playlist_description": "Descrição da lista de reprodução",
"edit_playlist": "Editar lista de reprodução",
"playlist_name": "Nome da lista de reprodução",
"cancel": "Cancelar",
"okay": "Ok",
"show_search_suggestions": "Mostrar sugestões de pesquisa",
"chapters_layout_mobile": "Aplicações recentemente adicionadas",
"enable_dearrow": "Ativar o DeArrow",
"delete_automatically": "Eliminar automaticamente após"
}, },
"comment": { "comment": {
"pinned_by": "Afixado por {author}", "pinned_by": "Afixado por {author}",
"disabled": "Os comentários estão desactivados pelo dono do canal.", "disabled": "O dono do canal desativou os comentários.",
"user_disabled": "Os comentários estão desactivados nas definições.", "user_disabled": "Os comentários estão desativados nas definições.",
"loading": "A carregar comentários..." "loading": "A carregar comentários..."
}, },
"preferences": { "preferences": {
"instance_name": "Nome da Instância", "instance_name": "Nome da instância",
"instance_locations": "Localizações da Instância", "instance_locations": "Localizações da instância",
"has_cdn": "Tem \"CDN\"?", "has_cdn": "Tem CDN?",
"registered_users": "Utilizadores Registados", "registered_users": "Utilizadores registados",
"ssl_score": "Pontuação \"SSL\"", "ssl_score": "Avaliação SSL",
"up_to_date": "Atualizada?", "up_to_date": "Atualizada?",
"version": "Versão" "version": "Versão"
}, },
@ -141,34 +164,47 @@
"videos": "Vídeos", "videos": "Vídeos",
"views": "{views} visualizações", "views": "{views} visualizações",
"watched": "Visto", "watched": "Visto",
"sponsor_segments": "Segmentos Patrocinados", "sponsor_segments": "Segmentos patrocinados",
"ratings_disabled": "Classificações Desactivadas", "ratings_disabled": "Avaliações desativadas",
"chapters": "Capítulos", "chapters": "Capítulos",
"live": "{0} em Direto", "live": "{0} em direto",
"shorts": "\"Shorts\"" "shorts": "Curtos",
"all": "Todos",
"category": "Categoria",
"chapters_horizontal": "Horizontal",
"chapters_vertical": "Vertical",
"license": "Licença",
"visibility": "Visibilidade"
}, },
"search": { "search": {
"did_you_mean": "Será que querias dizer: {0}?", "did_you_mean": "Será que queria dizer: {0}?",
"all": "YouTube: Tudo", "all": "YouTube: Tudo",
"videos": "YouTube: Vídeos", "videos": "YouTube: Vídeos",
"channels": "YouTube: Canais", "channels": "YouTube: Canais",
"playlists": "YouTube: Listas de Reprodução", "playlists": "YouTube: Listas de reprodução",
"music_songs": "YT Music: Músicas", "music_songs": "YT Music: Músicas",
"music_videos": "YT Music: Vídeos", "music_videos": "YT Music: Vídeos",
"music_albums": "YT Music: Álbuns", "music_albums": "YT Music: Álbuns",
"music_playlists": "YT Music: Listas de Reprodução" "music_playlists": "YT Music: Listas de reprodução",
"music_artists": "YT Music: Artistas"
}, },
"player": { "player": {
"watch_on": "Ver em {0}" "watch_on": "Ver em {0}"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Subscrito a: {0}" "subscribed_channels_count": "Subscreveu: {0}"
}, },
"info": { "info": {
"preferences_note": "Nota: as configurações são guardadas no armazenamento local to seu navegador. Eliminar os dados de navegação irá redefini-las.", "preferences_note": "Nota: as preferências são guardadas no armazenamento local do seu navegador. Se limpar os dados de navegação, também limpa as preferências.",
"page_not_found": "Página não encontrada", "page_not_found": "Página não encontrada",
"copied": "Copiada!", "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?" "local_storage": "Esta ação requer localStorage, os cookies estão ativados?",
"register_no_email_note": "Não recomendamos utilizar um endereço de e-mail como nome de utilizador. Continuar?",
"next_video_countdown": "A reproduzir o vídeo seguinte em {0}s",
"hours": "{quantidade} hora(s)",
"days": "{quantidade} dia(s)",
"weeks": "{quantidade} semana(s)",
"months": "{quantidade} mês(es)"
} }
} }

View File

@ -1,152 +1,182 @@
{ {
"actions": { "actions": {
"back_to_home": "Înapoi acasă", "back_to_home": "Înapoi acasă",
"store_search_history": "Salveaza Istoric de Cautări", "store_search_history": "Rețineți istoricul de căutări",
"with_timecode": "Distribuie cu cod de timp", "with_timecode": "Distribuiți cu timpul de cod",
"piped_link": "Link Piped", "piped_link": "Link Piped",
"time_code": "Cod de timp (secunde)", "time_code": "Cod de timp (secunde)",
"show_chapters": "Capitole", "show_chapters": "Capitole",
"search": "Caută", "search": "Căutare (Ctrl+K)",
"logout": "Scoate contul de pe acest dispozitiv", "logout": "Deconectați-vă de pe acest dispozitiv",
"add_to_playlist": "Adaugă în Playlist", "add_to_playlist": "Adăugare în playlist",
"remove_from_playlist": "Șterge din Playlist", "remove_from_playlist": "Ștergere din playlist",
"create_playlist": "Creează Playlist", "create_playlist": "Creați playlist",
"delete_playlist": "Șterge Playlist", "delete_playlist": "Ștergeți playlist",
"delete_playlist_confirm": "Ștergi acest playlist?", "delete_playlist_confirm": "Ștergi acest playlist?",
"please_select_playlist": "Te rog să alegi un playlist", "please_select_playlist": "Vă rugăm să alegeți un playlist",
"minimize_recommendations_default": "Ascunde Recomandări ca default", "minimize_recommendations_default": "Minimizați recomandările în mod implicit",
"subscribe": "Abonează-te - {count}", "subscribe": "Abonare - {count}",
"least_recent": "Mai puțin recente", "least_recent": "Cele mai vechi",
"channel_name_asc": "Nume Canal (A-Z)", "channel_name_asc": "Numele canalului (A-Z)",
"channel_name_desc": "Nume Canal (Z-A)", "channel_name_desc": "Nume canalului (Z-A)",
"back": "Înapoi", "back": "Înapoi",
"uses_api_from": "Folosește API de la ", "uses_api_from": "Se folosește API-ul de la ",
"enable_sponsorblock": "Activează Sponsorblock", "enable_sponsorblock": "Activați Sponsorblock",
"skip_intro": "Sari animația de Intermisie / Intro", "skip_intro": "Omitere pauze/animații de intro",
"skip_preview": "Sari Preview / Recapitulare", "skip_preview": "Omitere previzualizare/recapitulare",
"skip_self_promo": "Sari Promoția Neplătita / Proprie", "skip_self_promo": "Omitere promoție neplătită/autopromovare",
"skip_non_music": "Sari Muzica: Secțiunea de Non-Muzică", "skip_non_music": "Omitere muzică: Secțiune non-muzicală",
"skip_highlight": "Sari Highlight", "skip_highlight": "Omitere evidențiere",
"show_markers": "Arată Marcaje in Player", "show_markers": "Se afișează marcatori în player",
"dark": "Întunecat", "dark": "Întunecat",
"auto": "Auto", "auto": "Auto",
"audio_only": "Doar Audio", "audio_only": "Doar audio",
"default_quality": "Calitate Default", "default_quality": "Calitate implicită",
"country_selection": "Selecție Țară", "country_selection": "Țară",
"default_homepage": "Pagina de Acasă", "default_homepage": "Pagina principală implicită",
"minimize_comments_default": "Ascunde Comentariile", "minimize_comments_default": "Minimizați comentariile în mod implicit",
"minimize_description_default": "Ascunde Descrierea", "minimize_description_default": "Minimizați descrierea în mod implicit",
"language_selection": "Selecție Limbă", "language_selection": "Limbă",
"instances_list": "Listă de Instanțe", "instances_list": "Listă de Instanțe",
"enabled_codecs": "Activează Codecuri (Multiple)", "enabled_codecs": "Codecuri activate (multiple)",
"loop_this_video": "Repornește Video-ul", "loop_this_video": "Repetare video",
"donations": "Donații", "donations": "Donații pentru dezvoltare",
"show_recommendations": "Arată Recomandări", "show_recommendations": "Afișați recomandările",
"disable_lbry": "Oprește LBRY pentru Streaming", "disable_lbry": "Dezactivați LBRY pentru streaming",
"enable_lbry_proxy": "Activează Proxy pentru LBRY", "enable_lbry_proxy": "Activați proxy pentru LBRY",
"view_ssl_score": "Vezi Scor SSL", "view_ssl_score": "Vedeți scorul SSL",
"filter": "Filtru", "filter": "Filtru",
"loading": "Se încarcă...", "loading": "Se încarcă...",
"clear_history": "Șterge Istoric", "clear_history": "Ștergeți istoricul",
"hide_replies": "Ascunde răspunsuri", "hide_replies": "Ascundeți răspunsurile",
"load_more_replies": "Mai multe Răspunsuri", "load_more_replies": "Mai multe răspunsuri",
"delete_playlist_video_confirm": "Ștergi video-ul din playlist?", "delete_playlist_video_confirm": "Ștergeți videoclipul din playlist?",
"select_playlist": "Alege un Playlist", "select_playlist": "Selectați un playlist",
"delete_account": "Șterge Contul", "delete_account": "Ștergeți-vă contul",
"show_watch_on_youtube": "Arata buton de Vezi pe YouTube", "show_watch_on_youtube": "Afișați butonul „Vizionați pe YouTube”",
"invalidate_session": "Deconectează-te peste tot", "invalidate_session": "Deconectați toate dispozitivele",
"instance_auth_selection": "Selecție Instanță de Autentificare", "instance_auth_selection": "Instanța de autentificare",
"clone_playlist_success": "Clonat cu succes!", "clone_playlist_success": "Clonată cu succes!",
"reset_preferences": "Resetează preferințele", "reset_preferences": "Resetați preferințele",
"confirm_reset_preferences": "Sigur vrei să îți resetezi preferințele?", "confirm_reset_preferences": "Sunteți sigur că doriți să vă resetați preferințele?",
"rename_playlist": "Redenumește playlist", "share": "Distribuiți",
"new_playlist_name": "Nume playlist nou", "follow_link": "Urmați link-ul",
"share": "Distribuie", "copy_link": "Copiați link-ul",
"follow_link": "Deschide link", "hide_watched": "Ascundeți videoclipurile vizionate din flux",
"copy_link": "Copiază link",
"hide_watched": "Ascunde video-urile vizionate",
"documentation": "Documentație", "documentation": "Documentație",
"status_page": "Status", "status_page": "Status",
"source_code": "Cod sursă", "source_code": "Cod sursă",
"instance_donations": "Donații instanță", "instance_donations": "Donații instanță",
"reply_count": "{count} răspunsuri", "reply_count": "{count} răspunsuri",
"minimize_chapters_default": "Ascunde Capitole ca default", "minimize_chapters_default": "Minimizați capitolele în mod implicit",
"skip_sponsors": "Sari Sponsori", "skip_sponsors": "Omitere sponsori",
"different_auth_instance": "Folosește altă instanță pentru autentificare", "different_auth_instance": "Folosiți o instanță diferită pentru autentificare",
"clone_playlist": "Clonează Playlist", "clone_playlist": "Clonați lista de redare",
"backup_preferences": "Backup preferințe", "backup_preferences": "Faceți backup la preferințe",
"unsubscribe": "Dezabonează-te - {count}", "unsubscribe": "Dezabonare - {count}",
"view_subscriptions": "Vezi Subscripții", "view_subscriptions": "Vedeți abonamentele",
"sort_by": "Sortează după:", "sort_by": "Sortare după:",
"download_as_txt": "Descarcă ca .txt", "download_as_txt": "Descărcați ca .txt",
"most_recent": "Cele mai Recente", "most_recent": "Cele mai recente",
"skip_outro": "Sari Endcards / Credite", "skip_outro": "Omitere carduri de sfârșit/mulțumiri",
"skip_interaction": "Sari Reminder de interacțiune (Subscribe)", "skip_interaction": "Omitere reamintiri de interacțiune (abonare)",
"light": "Luminat", "light": "Luminat",
"restore_preferences": "Restore preferințe", "restore_preferences": "Restaurați preferințele",
"skip_filler_tangent": "Sari Tangenta Filler", "skip_filler_tangent": "Omitere tangentă de umplere",
"theme": "Temă", "theme": "Temă",
"autoplay_video": "Autopornește Video", "autoplay_video": "Redare automată video",
"buffering_goal": "Buffering Goal (secunde)", "buffering_goal": "Obiectiv de tamponare (în secunde)",
"instance_selection": "Selecție Instanță", "instance_selection": "Instanță",
"store_watch_history": "Salvează Istoricul de Vizionare", "store_watch_history": "Rețineți istoricul de vizionări",
"minimize_comments": "Ascunde Comentarii", "minimize_comments": "Minimizați comentariile",
"minimize_description": "Ascunde Descriere", "minimize_description": "Minimizați descrierea",
"show_more": "Mai Mult", "show_more": "Mai mult",
"no": "Nu", "no": "Nu",
"export_to_json": "Exportă ca JSON", "export_to_json": "Exportați ca JSON",
"import_from_json": "Importă din JSON/CSV", "import_from_json": "Importați din JSON/CSV",
"auto_play_next_video": "Autopornește următorul Video", "auto_play_next_video": "Redați automat următorul video",
"minimize_recommendations": "Ascunde Recomandări", "minimize_recommendations": "Minimizați recomandările",
"yes": "Da", "yes": "Da",
"show_comments": "Arată Comentarii", "show_comments": "Afișați comentariile",
"show_description": "Arată Descriere" "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ă",
"group_name": "Numele grupului",
"create_group": "Creați un grup",
"auto_display_captions": "Afișare automată subtitrări",
"playlist_name": "Numele playlist-ului",
"okay": "OK",
"playlist_description": "Descrierea playlist-ului",
"edit_playlist": "Editează playlist-ul",
"cancel": "Anulare",
"chapters_layout_mobile": "Mod afișare capitole pe mobil",
"show_search_suggestions": "Afișare sugestii căutare",
"enable_dearrow": "Activați DeArrow",
"delete_automatically": "Șterge automat după"
}, },
"preferences": { "preferences": {
"ssl_score": "Scor SSL", "ssl_score": "Scor SSL",
"version": "Versiune", "version": "Versiune",
"up_to_date": "Actualizat?", "up_to_date": "Actualizat?",
"instance_name": "Nume Instanță", "instance_name": "Nume instanță",
"instance_locations": "Locațiile Instanței", "instance_locations": "Locațiile instanței",
"has_cdn": "Are CDN?", "has_cdn": "Are CDN?",
"registered_users": "Useri Înregistrați" "registered_users": "Utilizatori înregistrați"
}, },
"comment": { "comment": {
"user_disabled": "Comentariile sunt dezactivate în setări.", "user_disabled": "Comentariile sunt dezactivate în setări.",
"pinned_by": "Pomovat de {author}", "pinned_by": "Fixat de {author}",
"disabled": "Comentariile sunt dezactivate de creator.", "disabled": "Comentariile sunt dezactivate de către autor.",
"loading": "Se incarcă comentariile..." "loading": "Se încarcă comentariile..."
}, },
"video": { "video": {
"views": "{views} vizionări", "views": "{views} vizionări",
"chapters": "Capitole", "chapters": "Capitole",
"shorts": "Shorts", "shorts": "Shorts",
"watched": "Văzut", "watched": "Vizionat",
"sponsor_segments": "Segmente Sponsori", "sponsor_segments": "Segmente sponsori",
"ratings_disabled": "Like-uri dezactivate", "ratings_disabled": "Evaluări dezactivate",
"live": "{0} Live", "live": "{0} în direct",
"videos": "Video-uri" "videos": "Videoclipuri",
"category": "Categorie",
"all": "Tot",
"chapters_horizontal": "Orizontal",
"chapters_vertical": "Vertical"
}, },
"login": { "login": {
"username": "Nume User", "username": "Nume de utilizator",
"password": "Parolă" "password": "Parolă"
}, },
"search": { "search": {
"videos": "YouTube: Video-uri", "videos": "YouTube: Videoclipuri",
"music_playlists": "YT Music: Playlisturi", "music_playlists": "YT Music: Liste de redare",
"did_you_mean": "Voiai să scrii: {0}?", "did_you_mean": "Vă refereați la: {0}?",
"all": "YouTube: Toate", "all": "YouTube: Toate",
"channels": "YouTube: Canale", "channels": "YouTube: Canale",
"playlists": "YouTube: Playlisturi", "playlists": "YouTube: Liste de redare",
"music_songs": "YT Music: Cântece", "music_songs": "YT Music: Muzică",
"music_videos": "YT Music: Video-uri", "music_videos": "YT Music: Videoclipuri",
"music_albums": "YT Music: Albume" "music_albums": "YT Music: Albume",
"music_artists": "YT Music: Artiști"
}, },
"info": { "info": {
"cannot_copy": "Nu se poate copia!", "cannot_copy": "Nu s-a putut copia!",
"preferences_note": "Sfat: preferințele sunt salvate in memoria locala a browserului tău. Ștergând datele browserului le ștergi si pe ele.", "preferences_note": "Notă: preferințele sunt salvate în memoria locală a browserului dvs. Ștergerea datelor din browserul dvs. le va reseta.",
"page_not_found": "Pagină negăsită", "page_not_found": "Pagina nu a fost găsită",
"copied": "S-a copiat!" "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",
"days": "{amount} zi(le)"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Abonat la: {0}" "subscribed_channels_count": "Abonat la: {0}"
@ -154,19 +184,22 @@
"titles": { "titles": {
"register": "Înregistrare", "register": "Înregistrare",
"history": "Istoric", "history": "Istoric",
"subscriptions": "Abonări", "subscriptions": "Abonamente",
"playlists": "Playlisturi", "playlists": "Liste de redare",
"account": "Cont", "account": "Cont",
"instance": "Instanță", "instance": "Instanță",
"login": "Logare", "login": "Autentificare",
"feed": "Abonări", "feed": "Flux",
"trending": "Trending", "trending": "Tendințe",
"livestreams": "Live-uri", "livestreams": "Fluxuri live",
"channels": "Canale", "channels": "Canale",
"preferences": "Preferințe", "preferences": "Preferințe",
"player": "Player" "player": "Player-ul",
"bookmarks": "Marcaje",
"channel_groups": "Grupuri de canale",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "Vezi pe {0}" "watch_on": "Vizionați pe {0}"
} }
} }

View File

@ -5,14 +5,17 @@
"register": "Регистрация", "register": "Регистрация",
"feed": "Подписки", "feed": "Подписки",
"preferences": "Настройки", "preferences": "Настройки",
"history": "История просмотров", "history": "История",
"subscriptions": "Ваши подписки", "subscriptions": "Подписки",
"playlists": "Плейлисты", "playlists": "Плейлисты",
"account": "Аккаунт", "account": "Аккаунт",
"player": "Плеер", "player": "Плеер",
"instance": "Сервер", "instance": "Сервер",
"livestreams": "Прямые трансляции", "livestreams": "Прямые трансляции",
"channels": "Каналы" "channels": "Каналы",
"bookmarks": "Закладки",
"channel_groups": "Группы каналов",
"dearrow": "DeArrow"
}, },
"player": { "player": {
"watch_on": "Смотреть на {0}" "watch_on": "Смотреть на {0}"
@ -27,7 +30,7 @@
"channel_name_asc": "Имя канала (А-Я)", "channel_name_asc": "Имя канала (А-Я)",
"channel_name_desc": "Имя канала (Я-А)", "channel_name_desc": "Имя канала (Я-А)",
"back": "Назад", "back": "Назад",
"uses_api_from": "Использовать API, предоставляемое ", "uses_api_from": "Использовать API ",
"enable_sponsorblock": "Включить Sponsorblock", "enable_sponsorblock": "Включить Sponsorblock",
"skip_sponsors": "Пропускать спонсорскую рекламу", "skip_sponsors": "Пропускать спонсорскую рекламу",
"skip_intro": "Пропускать заставку/интро", "skip_intro": "Пропускать заставку/интро",
@ -53,11 +56,11 @@
"instances_list": "Список зеркал сервиса Piped", "instances_list": "Список зеркал сервиса Piped",
"enabled_codecs": "Включённые кодеки (Можно выбрать несколько)", "enabled_codecs": "Включённые кодеки (Можно выбрать несколько)",
"instance_selection": "Выбор зеркала сервиса Piped", "instance_selection": "Выбор зеркала сервиса Piped",
"show_more": "Показать больше", "show_more": "Показать еще",
"yes": "Да", "yes": "Да",
"no": "Нет", "no": "Нет",
"export_to_json": "Экспорт в JSON", "export_to_json": "Экспорт в JSON",
"import_from_json": "Импорт из JSON/CSV", "import_from_json": "Импорт из JSON",
"loop_this_video": "Повтор текущего видео", "loop_this_video": "Повтор текущего видео",
"auto_play_next_video": "Сразу проигрывать следующее рекомендованное видео", "auto_play_next_video": "Сразу проигрывать следующее рекомендованное видео",
"donations": "Пожертвования на разработку", "donations": "Пожертвования на разработку",
@ -66,9 +69,9 @@
"minimize_recommendations": "Свернуть рекомендации", "minimize_recommendations": "Свернуть рекомендации",
"show_recommendations": "Показать рекомендации", "show_recommendations": "Показать рекомендации",
"disable_lbry": "Отключить LBRY для стриминга", "disable_lbry": "Отключить LBRY для стриминга",
"enable_lbry_proxy": "Проксировать видео с LBRY", "enable_lbry_proxy": "Проксировать видео для LBRY",
"view_ssl_score": "Посмотреть настройки SSL", "view_ssl_score": "Посмотреть настройки SSL",
"search": "Поиск", "search": "Поиск (Ctrl+K)",
"filter": "Фильтр", "filter": "Фильтр",
"loading": "Загрузка...", "loading": "Загрузка...",
"clear_history": "Очистить историю", "clear_history": "Очистить историю",
@ -84,52 +87,74 @@
"select_playlist": "Выбрать плейлист", "select_playlist": "Выбрать плейлист",
"delete_playlist_confirm": "Удалить этот плейлист?", "delete_playlist_confirm": "Удалить этот плейлист?",
"delete_playlist_video_confirm": "Удалить видео из плейлиста?", "delete_playlist_video_confirm": "Удалить видео из плейлиста?",
"show_markers": "Показать Mаркеры Hа Проигрывателе", "show_markers": "Показать маркеры на проигрывателе",
"delete_account": "Удалить аккаунт", "delete_account": "Удалить аккаунт",
"logout": "Выйти из этого устройства", "logout": "Выйти из этого устройства",
"download_as_txt": "Скачать как .txt", "download_as_txt": "Скачать как .txt",
"minimize_recommendations_default": "Скрыть Рекомендации по умолчанию", "minimize_recommendations_default": "Скрыть Рекомендации по умолчанию",
"invalidate_session": "Выйти из всех устройств", "invalidate_session": "Выйти из всех устройств",
"different_auth_instance": "Использовать другие средства аутентификации", "different_auth_instance": "Использовать другое зеркало для аутентификации",
"instance_auth_selection": "Выбор средств аутентификации", "instance_auth_selection": "Выбор зеркала аутентификации",
"clone_playlist": "Клонировать плейлист", "clone_playlist": "Клонировать плейлист",
"clone_playlist_success": "Клонирование прошло успешно!", "clone_playlist_success": "Успешно клонировано!",
"show_chapters": "Части", "show_chapters": "Главы",
"rename_playlist": "Переименовать плейлист",
"new_playlist_name": "Новое название плейлиста",
"share": "Поделиться", "share": "Поделиться",
"with_timecode": "Поделиться с отметкой времени", "with_timecode": "Поделиться с таймкодом",
"piped_link": "Ссылка Piped", "piped_link": "Ссылка Piped",
"follow_link": "Ссылка подписки", "follow_link": "Перейти по ссылке",
"copy_link": "Скопировать ссылку", "copy_link": "Скопировать ссылку",
"time_code": "Тайм-код (в секундах)", "time_code": "Таймкод (в секундах)",
"reset_preferences": "Сбросить настройки", "reset_preferences": "Сбросить настройки",
"confirm_reset_preferences": "Вы уверены, что хотите сбросить настройки?", "confirm_reset_preferences": "Вы уверены, что хотите сбросить настройки?",
"backup_preferences": "Бэкап настроек", "backup_preferences": "Бэкап настроек",
"restore_preferences": "Восстановить настройки", "restore_preferences": "Восстановить настройки",
"back_to_home": "Вернутся на главную", "back_to_home": "Назад на главную",
"store_search_history": "Хранить историю поиска", "store_search_history": "Хранить историю поиска",
"hide_watched": "Скрыть просмотренные видео в ленте", "hide_watched": "Скрыть просмотренные видео в ленте",
"status_page": "Статус", "status_page": "Статус",
"source_code": "Исходный код", "source_code": "Исходный код",
"documentation": "Пожертвования сервера", "documentation": "Документация",
"instance_donations": "Пожертвования сервера", "instance_donations": "Пожертвования зеркала",
"reply_count": "{count} ответов", "reply_count": "{count} ответов",
"minimize_comments_default": "Сворачивать комментарии по умолчанию", "minimize_comments_default": "Сворачивать комментарии по умолчанию",
"minimize_comments": "Свернуть комментарии", "minimize_comments": "Свернуть комментарии",
"show_watch_on_youtube": "Показать кнопку Смотреть на YouTube", "show_watch_on_youtube": "Показать кнопку Смотреть на YouTube",
"minimize_chapters_default": "Скрывать главы по умолчанию", "minimize_chapters_default": "Скрывать главы по умолчанию",
"no_valid_playlists": "Файл не содержит действительных списков воспроизведения!" "no_valid_playlists": "Файл не содержит действующих плейлистов!",
"with_playlist": "Поделиться с плейлистом",
"bookmark_playlist": "Закладка",
"playlist_bookmarked": "В закладках",
"skip_automatically": "Автоматически",
"min_segment_length": "Минимальная длина сегмента (в секундах)",
"skip_button_only": "Показать кнопку \"Пропустить\"",
"skip_segment": "Пропустить сегмент",
"show_less": "Показать меньше",
"autoplay_next_countdown": "Отсчет по умолчанию до следующего видео (в секундах)",
"dismiss": "Отклонить",
"group_name": "Имя группы",
"create_group": "Создать группу",
"cancel": "Отмена",
"edit_playlist": "Редактировать плейлист",
"playlist_description": "Описание плейлиста",
"okay": "Хорошо",
"auto_display_captions": "Авто-отображение субтитров",
"playlist_name": "Название плейлиста",
"show_search_suggestions": "Показать поисковые предложения",
"chapters_layout_mobile": "Расположение глав в мобильном виде",
"delete_automatically": "Автоматическое удаление после",
"enable_dearrow": "Включить DeArrow",
"generate_qrcode": "Сгенерировать QR Код",
"import_from_json_csv": "Импорт из JSON/CSV"
}, },
"comment": { "comment": {
"pinned_by": "Прикреплено пользователем {author}", "pinned_by": "Закреплено пользователем {author}",
"loading": "Загрузка комментариев...", "loading": "Загрузка комментариев...",
"user_disabled": "Комментарии отключены в настройках.", "user_disabled": "Комментарии отключены в настройках.",
"disabled": "Коментарии отключены автором." "disabled": "Комментарии отключены автором."
}, },
"preferences": { "preferences": {
"instance_name": "Название", "instance_name": "Имя зеркала",
"instance_locations": "Местоположение", "instance_locations": "Местоположения зеркала",
"has_cdn": "Имеется CDN?", "has_cdn": "Имеется CDN?",
"ssl_score": "Оценка настроек SSL", "ssl_score": "Оценка настроек SSL",
"registered_users": "Зарегистрировано пользователей", "registered_users": "Зарегистрировано пользователей",
@ -137,8 +162,10 @@
"up_to_date": "Версия актуальна?" "up_to_date": "Версия актуальна?"
}, },
"login": { "login": {
"username": "Аккаунт на Piped", "username": "Имя пользователя",
"password": "Пароль" "password": "Пароль",
"password_confirm": "Повторите пароль",
"passwords_incorrect": "Пароль не совпадает!"
}, },
"video": { "video": {
"videos": "Видео", "videos": "Видео",
@ -148,7 +175,13 @@
"ratings_disabled": "Оценки отключены", "ratings_disabled": "Оценки отключены",
"live": "{0} В эфире", "live": "{0} В эфире",
"chapters": "Содержание", "chapters": "Содержание",
"shorts": "Shorts" "shorts": "Shorts",
"all": "Все",
"category": "Категория",
"chapters_horizontal": "Горизонтально",
"chapters_vertical": "Вертикально",
"visibility": "Видимость",
"license": "Лицензия"
}, },
"search": { "search": {
"did_you_mean": "Может быть вы имели в виду: {0}?", "did_you_mean": "Может быть вы имели в виду: {0}?",
@ -159,16 +192,23 @@
"music_songs": "YT Music: Композиции", "music_songs": "YT Music: Композиции",
"music_videos": "YT Music: Видео", "music_videos": "YT Music: Видео",
"music_albums": "YT Music: Альбомы", "music_albums": "YT Music: Альбомы",
"music_playlists": "YT Music: Плейлисты" "music_playlists": "YT Music: Плейлисты",
"music_artists": "YT Music: Исполнители"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Подписан на: {0}" "subscribed_channels_count": "Подписан на: {0}"
}, },
"info": { "info": {
"preferences_note": "Примечание: настройки сохранены в локальном хранилище браузера. При удалении данных браузера они будут удалены.", "preferences_note": "Примечание: настройки сохранены в локальном хранилище браузера. Удаление данных вашего браузера сбросит их.",
"copied": "Скопировано!", "copied": "Скопировано!",
"cannot_copy": "Не получилось скопировать!", "cannot_copy": "Не удалось скопировать!",
"page_not_found": "Страница не найдена", "page_not_found": "Страница не найдена",
"local_storage": "Это действие требует локального хранилища (localStorage), разрешены ли файлы cookie?" "local_storage": "Это действие требует разрешения localStorage, включены ли cookie-файлы?",
"register_no_email_note": "Использование электронной почты в качестве имени пользователя не рекомендуется. Продолжить?",
"next_video_countdown": "Следующие видео через {0} с",
"days": "{amount} дней",
"hours": "{amount} час(ов)",
"weeks": "{amount} недель",
"months": "{amount} месяцев"
} }
} }

207
src/locales/si.json Normal file
View File

@ -0,0 +1,207 @@
{
"titles": {
"trending": "නැගී එන",
"login": "පිවිසෙන්න",
"register": "ලියාපදිංචිය",
"preferences": "අභිප්‍රේත",
"history": "ඉතිහාසය",
"subscriptions": "දායකත්‍ව",
"account": "ගිණුම",
"player": "වාදකය",
"livestreams": "සජීව ප්‍රචාර",
"channels": "නාලිකා",
"playlists": "වාදන ලැයිස්තු",
"instance": "සේවාදායකය",
"bookmarks": "පොත්යොමු",
"feed": "සංග්‍රහය",
"channel_groups": "නාලිකා සමූහ"
},
"actions": {
"subscribe": "දායකවන්න - {count}",
"unsubscribe": "දායක නොවන්න - {count}",
"most_recent": "වඩාත් මෑත",
"least_recent": "ආසන්න මෑත",
"channel_name_asc": "නාලිකාවේ නම (අ-ෆ)",
"channel_name_desc": "නාලිකාවේ නම (ෆ-අ)",
"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": "ආපසු මුල් පිටුවට",
"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": "යූටියුබ්හි නරඹන්න බොත්තම පෙන්වන්න",
"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": "ඔබගේ අභිප්‍රේත නැවත සැකසීමට වුවමනා ද?",
"follow_link": "සබැඳියට යන්න",
"store_search_history": "සෙවුම් ඉතිහාසය ගබඩා කරන්න",
"no_valid_playlists": "ගොනුවේ වලංගු වාදන ලැයිස්තු අඩංගු නොවේ!",
"playlist_bookmarked": "පොත්යොමුවක් යෙදිණි",
"enable_lbry_proxy": "LBRY සඳහා ප්‍රතියුක්තය සබල කරන්න",
"different_auth_instance": "සත්‍යතාව තහවුරු කිරීම සඳහා වෙනත් සේවාදායකයක් භාවිතා කරන්න",
"hide_watched": "සංග්‍රහයෙන් නැරඹූ දෘශ්‍යක සඟවන්න",
"skip_button_only": "මඟහරින බොත්තම පෙන්වන්න",
"skip_automatically": "ස්වයංක්‍රීයව",
"skip_segment": "කොටස මඟ හරින්න",
"min_segment_length": "අවම කොටස් දිග (තත්පර වලින්)",
"show_less": "අඩුවෙන් පෙන්වන්න",
"dismiss": "අයින් කරන්න",
"autoplay_next_countdown": "ඊළඟ දෘශ්‍යකය තෙක් ගණන් කිරීම (තත්. වලින්)",
"group_name": "සමූහයේ නම",
"create_group": "සමූහය සාදන්න",
"cancel": "අවලංගු",
"okay": "හරි",
"edit_playlist": "වාදන ලැයිස්තුව සංස්කරණය",
"playlist_name": "වාදන ලැයිස්තුවේ නම",
"playlist_description": "වාදන ලැයිස්තුවේ සවිස්තරය",
"auto_display_captions": "උපසිරැසි ස්වයංක්‍රීයව පෙන්වන්න",
"show_search_suggestions": "සෙවුම් යෝජනා පෙන්වන්න",
"delete_automatically": "මෙයින් පසුව මකන්න",
"generate_qrcode": "QR කේතයක් උත්පාදනය"
},
"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": "ප්‍රවර්ගය",
"chapters_vertical": "සිරස්",
"license": "බලපත්‍රය",
"chapters_horizontal": "තිරස්"
},
"search": {
"did_you_mean": "ඔබ අදහස් කළේ: {0}?",
"videos": "යූටියුබ්: දෘශ්‍යක",
"playlists": "යූටියුබි: වාදන ලැයිස්තු",
"music_songs": "යූටියුබි ගීත: ගීත",
"music_videos": "යූටියුබි ගීත: දෘශ්‍යක",
"music_albums": "YT Music: ඇල්බම",
"music_playlists": "යූටියුබි ගීත: වාදන ලැයිස්තු",
"channels": "යූටියුබ්: නාලිකා",
"all": "යූටියුබි: සියල්ල",
"music_artists": "යූටියුබ් ගීත: කලාකරුවන්"
},
"info": {
"page_not_found": "පිටුව හමු නොවිණි",
"copied": "පිටපත් විය!",
"cannot_copy": "පිටපත් නොවේ!",
"local_storage": "මෙම ක්‍රියාමාර්ගයට ස්ථානීය-ආචයනය වුවමනාය, දත්තකඩ සබල කර තිබේද?",
"register_no_email_note": "පරිශ්‍රීලක නාමය ලෙස වි-තැපෑලක් භාවිතය නිර්දේශ නොකෙරේ. ඉදිරියට යන්නද?",
"preferences_note": "සටහන: ඔබගේ අතිරික්සුවේ ස්ථානීය ආචයනයේ අභිප්‍රේත සුරැකෙයි. අතිරික්සුවේ දත්ත මැකීමෙන් ඒවා අහිමි වනු ඇත.",
"next_video_countdown": "ඊළඟ දෘශ්‍යකය තත්. {0} කින් වාදනය වේ",
"days": "දවස් {amount}",
"weeks": "සති {amount}",
"hours": "පැය {amount}",
"months": "මාස {amount}"
},
"subscriptions": {
"subscribed_channels_count": "දායක වූයේ: {0}"
}
}

View File

@ -72,8 +72,6 @@
"view_ssl_score": "Zobraziť SSL skóre", "view_ssl_score": "Zobraziť SSL skóre",
"filter": "Filter", "filter": "Filter",
"delete_playlist_video_confirm": "Odstrániť video zo zoznamu?", "delete_playlist_video_confirm": "Odstrániť video zo zoznamu?",
"rename_playlist": "Premenovať zoznam skladieb",
"new_playlist_name": "Nový názov zoznamu skladieb",
"share": "Zdieľať", "share": "Zdieľať",
"follow_link": "Nasledujte odkaz", "follow_link": "Nasledujte odkaz",
"loading": "Načítavanie...", "loading": "Načítavanie...",

View File

@ -7,7 +7,9 @@
"ratings_disabled": "Оцене су онемогућене", "ratings_disabled": "Оцене су онемогућене",
"chapters": "Поглавља", "chapters": "Поглавља",
"live": "{0} Уживо", "live": "{0} Уживо",
"shorts": "Кратки видео снимци" "shorts": "Кратки видео снимци",
"all": "Све",
"category": "Категорија"
}, },
"actions": { "actions": {
"view_ssl_score": "Погледај SSL скор/оцену", "view_ssl_score": "Погледај SSL скор/оцену",
@ -23,7 +25,7 @@
"load_more_replies": "Учитај још одговора", "load_more_replies": "Учитај још одговора",
"unsubscribe": "Прекини са праћењем - {count}", "unsubscribe": "Прекини са праћењем - {count}",
"auto": "Аутоматски", "auto": "Аутоматски",
"search": "Претрага", "search": "Претрага (Ctrl+K)",
"skip_non_music": "Прескочи делове где нема музике у музичким видео клиповима", "skip_non_music": "Прескочи делове где нема музике у музичким видео клиповима",
"theme": "Теме", "theme": "Теме",
"audio_only": "Само звук", "audio_only": "Само звук",
@ -98,21 +100,28 @@
"status_page": "Статус", "status_page": "Статус",
"instance_donations": "Донације инстанци", "instance_donations": "Донације инстанци",
"show_chapters": "Поглавља", "show_chapters": "Поглавља",
"rename_playlist": "Преименуј плејлисту",
"with_timecode": "Подели са временским кодом", "with_timecode": "Подели са временским кодом",
"piped_link": "Piped веза", "piped_link": "Piped веза",
"back_to_home": "Врати се на почетну", "back_to_home": "Врати се на почетну",
"follow_link": "Прати везу", "follow_link": "Прати везу",
"copy_link": "Копирај везу", "copy_link": "Копирај везу",
"time_code": "Временски код (у секундама)", "time_code": "Временски код (у секундама)",
"new_playlist_name": "Ново име плејлисте",
"minimize_comments_default": "Подразумевано умањи коментаре", "minimize_comments_default": "Подразумевано умањи коментаре",
"minimize_comments": "Умањи коментаре", "minimize_comments": "Умањи коментаре",
"reply_count": "{count} одговора", "reply_count": "{count} одговора",
"minimize_chapters_default": "Умањи поглавља подразумевано", "minimize_chapters_default": "Умањи поглавља подразумевано",
"show_watch_on_youtube": "Прикажите \"Гредај на YouTube-у\" дугме", "show_watch_on_youtube": "Прикажите \"Гредај на YouTube-у\" дугме",
"no_valid_playlists": "Датотека не садржи важеће пописе снимака!", "no_valid_playlists": "Датотека не садржи важеће пописе снимака!",
"with_playlist": "Делите са пописом снимака" "with_playlist": "Делите са пописом снимака",
"playlist_bookmarked": "Обиљежено",
"bookmark_playlist": "Биљежак",
"show_less": "Прикажи мање",
"skip_button_only": "Прикажи дугме за прескакање",
"skip_automatically": "Аутоматски",
"min_segment_length": "Најмања дужина сегмента (у секундама)",
"skip_segment": "Прескочи сегмент",
"dismiss": "Одбаци",
"autoplay_next_countdown": "Подразумевано одбројавање до следећег видеа (у секундама)"
}, },
"preferences": { "preferences": {
"instance_locations": "Локација инстанце", "instance_locations": "Локација инстанце",
@ -151,7 +160,8 @@
"instance": "Инстанца", "instance": "Инстанца",
"player": "Покретник", "player": "Покретник",
"livestreams": "Уживо преноси", "livestreams": "Уживо преноси",
"channels": "Канали" "channels": "Канали",
"bookmarks": "Биљешци"
}, },
"comment": { "comment": {
"pinned_by": "Закачено од {author}", "pinned_by": "Закачено од {author}",
@ -170,6 +180,8 @@
"copied": "Копирано!", "copied": "Копирано!",
"cannot_copy": "Није могуће копирати!", "cannot_copy": "Није могуће копирати!",
"preferences_note": "Напомена: подешавања се чувају у локалној меморији вашег претраживача. Брисање података прегледача ће их ресетовати.", "preferences_note": "Напомена: подешавања се чувају у локалној меморији вашег претраживача. Брисање података прегледача ће их ресетовати.",
"local_storage": "Ова радња захтева локално складиште, да ли су колачићи омогућени ?" "local_storage": "Ова радња захтева локално складиште, да ли су колачићи омогућени ?",
"register_no_email_note": "Коришћење е-поруке као корисничког имена се не препоручује. Желите ли ипак наставити?",
"next_video_countdown": "Репродукује се следећи видео за {0}с"
} }
} }

View File

@ -10,7 +10,12 @@
"playlists": "Spellistor", "playlists": "Spellistor",
"account": "Konto", "account": "Konto",
"instance": "Instans", "instance": "Instans",
"player": "Spelare" "player": "Spelare",
"bookmarks": "Bokmärken",
"dearrow": "DeArrow",
"livestreams": "Livesändningar",
"channels": "Kanaler",
"channel_groups": "Kanal Grupper"
}, },
"actions": { "actions": {
"subscribe": "Prenumerera - {count}", "subscribe": "Prenumerera - {count}",
@ -46,18 +51,18 @@
"store_watch_history": "Spara titthistorik", "store_watch_history": "Spara titthistorik",
"instances_list": "Lista över instanser", "instances_list": "Lista över instanser",
"enabled_codecs": "Aktivera codecs (flera)", "enabled_codecs": "Aktivera codecs (flera)",
"import_from_json": "Importera från JSON/CSV", "import_from_json": "Importera från JSON",
"donations": "Donationer", "donations": "Donationer till utveckling",
"filter": "Filter", "filter": "Filter",
"hide_replies": "Dölj svar", "hide_replies": "Dölj svar",
"load_more_replies": "Ladda fler svar", "load_more_replies": "Ladda fler svar",
"enable_sponsorblock": "Aktivera sponsorblockering", "enable_sponsorblock": "Aktivera sponsorblockering",
"skip_preview": "Hoppa över förhandsgranskning/sammanfattning", "skip_preview": "Hoppa över förhandsgranskning/sammanfattning",
"autoplay_video": "Spela upp video automatiskt", "autoplay_video": "Spela upp video automatiskt",
"country_selection": "Val av land", "country_selection": "Land",
"language_selection": "Val av språk", "language_selection": "Språk",
"skip_non_music": "Hoppa över musik: Icke-musikaliskt avsnitt", "skip_non_music": "Hoppa över musik: Icke-musikaliskt avsnitt",
"instance_selection": "Val av instans", "instance_selection": "Instans",
"show_more": "Visa mer", "show_more": "Visa mer",
"yes": "Ja", "yes": "Ja",
"no": "Nej", "no": "Nej",
@ -67,7 +72,7 @@
"show_recommendations": "Visa rekommendationer", "show_recommendations": "Visa rekommendationer",
"disable_lbry": "Inaktivera LBRY för strömning", "disable_lbry": "Inaktivera LBRY för strömning",
"enable_lbry_proxy": "Aktivera proxy för LBRY", "enable_lbry_proxy": "Aktivera proxy för LBRY",
"search": "Sök", "search": "Sök (Ctrl+K)",
"clear_history": "Rensa historik", "clear_history": "Rensa historik",
"skip_filler_tangent": "Hoppa över påfyllningstangent", "skip_filler_tangent": "Hoppa över påfyllningstangent",
"skip_highlight": "Hoppa över höjdpunkt", "skip_highlight": "Hoppa över höjdpunkt",
@ -87,15 +92,61 @@
"minimize_recommendations_default": "Minimera rekommendationer som standard", "minimize_recommendations_default": "Minimera rekommendationer som standard",
"invalidate_session": "Logga ut alla enheter", "invalidate_session": "Logga ut alla enheter",
"different_auth_instance": "Använd en annan instans för autentisering", "different_auth_instance": "Använd en annan instans för autentisering",
"instance_auth_selection": "Val av autentiseringsinstans", "instance_auth_selection": "Autentiseringsinstans",
"download_as_txt": "Ladda ner som .txt", "download_as_txt": "Ladda ner som .txt",
"reset_preferences": "Återställ inställningar", "reset_preferences": "Återställ inställningar",
"confirm_reset_preferences": "Är du säker på att du vill återställa dina inställningar?", "confirm_reset_preferences": "Är du säker på att du vill återställa dina inställningar?",
"backup_preferences": "Inställningar för säkerhetskopiering", "backup_preferences": "Inställningar för säkerhetskopiering",
"restore_preferences": "Återställa inställningar" "restore_preferences": "Återställa inställningar",
"enable_dearrow": "Aktivera DeArrow",
"autoplay_next_countdown": "Antal sekunder tills nästa video startar automatiskt",
"minimize_comments_default": "Minimera kommentarer som standard",
"show_watch_on_youtube": "Visa knappen \"Titta på YouTube\"",
"back_to_home": "Tillbaka till startsidan",
"delete_automatically": "Ta bort automatiskt efter",
"with_timecode": "Dela med tidsstämpel",
"reply_count": "{count} svar",
"with_playlist": "Dela med spellista",
"dismiss": "Avböj",
"min_segment_length": "Minsta segmentlängd (i sekunder)",
"skip_segment": "Hoppa över segment",
"minimize_comments": "Minimera kommentarer",
"show_less": "Visa mindre",
"cancel": "Avbryt",
"store_search_history": "Spara sökhistorik",
"documentation": "Dokumentation",
"okay": "Okej",
"status_page": "Status",
"minimize_chapters_default": "Minimera kapitel som standard",
"time_code": "Tidsstämpel (i sekunder)",
"hide_watched": "Dölj tittade videor i flödet",
"share": "Dela",
"show_chapters": "Kapitel",
"source_code": "Källkod",
"edit_playlist": "Redigera spellista",
"playlist_name": "Spellistans namn",
"playlist_description": "Beskrivning av spellista",
"generate_qrcode": "Generera QR-kod",
"chapters_layout_mobile": "Layout för kapitel på mobil",
"piped_link": "Piped-länk",
"follow_link": "Följ-länk",
"copy_link": "Kopiera länk",
"group_name": "Gruppnamn",
"show_search_suggestions": "Visa sökförslag",
"auto_display_captions": "Automatisk visning av textning",
"bookmark_playlist": "Bokmärke",
"instance_donations": "Instans donationer",
"no_valid_playlists": "Filen innehåller inga giltiga spellistor!",
"playlist_bookmarked": "Bokmärkt",
"create_group": "Skapa grupp",
"skip_button_only": "Visa hoppa över-knapp",
"skip_automatically": "Automatiskt",
"download_frame": "Ladda ner bildruta",
"import_from_json_csv": "Importera från JSON/CSV"
}, },
"player": { "player": {
"watch_on": "Titta på {0}" "watch_on": "Titta på {0}",
"failed": "Misslyckades med felkod {0}, se loggar för mer information"
}, },
"preferences": { "preferences": {
"instance_name": "Instansnamn", "instance_name": "Instansnamn",
@ -108,7 +159,9 @@
}, },
"login": { "login": {
"username": "Användarnamn", "username": "Användarnamn",
"password": "Lösenord" "password": "Lösenord",
"password_confirm": "Bekräfta lösenord",
"passwords_incorrect": "Lösenorden stämmer inte överens!"
}, },
"video": { "video": {
"videos": "Videor", "videos": "Videor",
@ -118,7 +171,13 @@
"ratings_disabled": "Betyg inaktiverade", "ratings_disabled": "Betyg inaktiverade",
"chapters": "Kapitel", "chapters": "Kapitel",
"live": "{0} Live", "live": "{0} Live",
"shorts": "Shorts" "shorts": "Shorts",
"license": "Licens",
"all": "Alla",
"category": "Kategori",
"chapters_horizontal": "Horisontell",
"visibility": "Synlighet",
"chapters_vertical": "Vertikal"
}, },
"comment": { "comment": {
"pinned_by": "Fäst av {author}", "pinned_by": "Fäst av {author}",
@ -135,12 +194,26 @@
"all": "YouTube: Alla", "all": "YouTube: Alla",
"videos": "YouTube: Videor", "videos": "YouTube: Videor",
"playlists": "YouTube: Spellistor", "playlists": "YouTube: Spellistor",
"music_songs": "YT Music: Låtar" "music_songs": "YT Music: Låtar",
"music_artists": "YT Music: Artister"
}, },
"subscriptions": { "subscriptions": {
"subscribed_channels_count": "Prenumererar på: {0}" "subscribed_channels_count": "Prenumererar på: {0}"
}, },
"information": { "information": {
"preferences_note": "Observera: inställningar sparas i webbläsarens lokala lagring. Om du raderar dina webbläsardata återställs de." "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å?",
"hours": "{amount} timma(r)",
"preferences_note": "Obs: Inställningarna sparas i det lokala lagringsutrymmet i din webbläsare. Om du raderar dina webbläsardata återställs de.",
"days": "{amount} dag(ar)",
"weeks": "{amount} vecka/veckor",
"months": "{amount} månad(er)",
"next_video_countdown": "Spelar nästa video om {0}s",
"cannot_copy": "Kan inte kopiera!",
"page_not_found": "Sida hittas ej",
"copied": "Kopierad!",
"local_storage": "Det här kräver localStorage, är cookies aktiverat?"
} }
} }

Some files were not shown because too many files have changed in this diff Show More