Compare commits
221 Commits
6e7f76a4de
...
7a871a59f0
Author | SHA1 | Date |
---|---|---|
RadoslavL | 7a871a59f0 | |
Émilien (perso) | b673695aa2 | |
ChunkyProgrammer | 170eef58fd | |
Samantaz Fox | 08390acd0c | |
Brahim Hadriche | 1a2d408d38 | |
Émilien (perso) | 99a5e9cbc4 | |
Samantaz Fox | 619aa3ff05 | |
Samantaz Fox | e8a36985af | |
Samantaz Fox | 962ce23cc2 | |
Samantaz Fox | e0ce59d3e8 | |
Samantaz Fox | c5a3112e49 | |
Samantaz Fox | d3703baba9 | |
Samantaz Fox | 20203f4ec0 | |
Samantaz Fox | 732553519e | |
ChunkyProgrammer | a957b0fb7c | |
ChunkyProgrammer | 26429bee3f | |
ChunkyProgrammer | ef6b766b29 | |
Émilien (perso) | 1e6ec605e8 | |
Samantaz Fox | 60f6a345d9 | |
Samantaz Fox | d1dddc1adc | |
Hosted Weblate | 00ef004029 | |
Hosted Weblate | 7ff11e4c44 | |
Hosted Weblate | 8db2e060d9 | |
Hosted Weblate | 8b0cbd2a29 | |
Hosted Weblate | d2ce519559 | |
Hosted Weblate | 219b587945 | |
Hosted Weblate | e8810509c1 | |
Hosted Weblate | 9688200caf | |
Hosted Weblate | 26a50eb4e8 | |
Hosted Weblate | 0ce945bfa8 | |
Hosted Weblate | aadf848ee6 | |
Hosted Weblate | 53ce2a1a9a | |
Hosted Weblate | 1d5100462b | |
Hosted Weblate | 986515dc5b | |
Hosted Weblate | 1d906aeecc | |
Hosted Weblate | 426b472a15 | |
Hosted Weblate | 1493e6a086 | |
Hosted Weblate | 3767ab2eeb | |
Hosted Weblate | fea36fc639 | |
Hosted Weblate | a16235d3b9 | |
Hosted Weblate | 99a3bd4fff | |
Hosted Weblate | 4aed0e1102 | |
Hosted Weblate | 833c711cba | |
Hosted Weblate | 7e1deea15e | |
Hosted Weblate | b9ae1a61da | |
Hosted Weblate | f062c18b82 | |
Hosted Weblate | f21a532c0d | |
Hosted Weblate | 8cec7ba004 | |
Hosted Weblate | 8169cd8977 | |
Hosted Weblate | 8ffc569ebd | |
Hosted Weblate | 736f35332a | |
Hosted Weblate | c52c6d3c9a | |
Samantaz Fox | 7b84bdb29b | |
Samantaz Fox | 5c0b6d8afa | |
Samantaz Fox | c85b908613 | |
Samantaz Fox | f32764c840 | |
Samantaz Fox | d30dae43fe | |
Samantaz Fox | 338d3d9f86 | |
Samantaz Fox | 1f51255f2f | |
Samantaz Fox | dcbe52c9fb | |
Samantaz Fox | bd5df3af5f | |
Samantaz Fox | 9bd2072e1d | |
Samantaz Fox | 3b4358dbd4 | |
Émilien (perso) | cf686202e0 | |
shironeko | 6b33820f1f | |
shironeko | 98c421e9f5 | |
shironeko | c864a63b6d | |
syeopite | 0ad2eff2a4 | |
ThetaDev | c005ada487 | |
toabr | 4a339df5c4 | |
syeopite | 1c0b4205d4 | |
syeopite | b16f66ef00 | |
vojkovic | 7cca1285aa | |
pitkajuh | c059829035 | |
ChunkyProgrammer | 7da4a7f72b | |
nixos script | 0917efd9cb | |
ChunkyProgrammer | 090b470bfc | |
Luigi | 97c4165f55 | |
guidiasz | 87a8207f37 | |
ChunkyProgrammer | fe8b1b4cc4 | |
ChunkyProgrammer | f1edb1d6bf | |
Chunky programmer | b5f8b4542a | |
ChunkyProgrammer | b344d98c25 | |
ChunkyProgrammer | 8c22e6a640 | |
ChunkyProgrammer | 6488794218 | |
Samantaz Fox | 9e8baa3539 | |
Samantaz Fox | 07fe648a9c | |
Samantaz Fox | 6da3287e9d | |
Samantaz Fox | 37c2f5caed | |
Samantaz Fox | 07b366f06b | |
Samantaz Fox | e8a14446af | |
Samantaz Fox | 813dc6de1c | |
unbelauscht | 72478ba704 | |
unbelauscht | 9e970fe4bd | |
Abdul Rauf | d76fed5850 | |
TheFrenchGhosty | 6868cade05 | |
Samantaz Fox | 7b6930c16b | |
Samantaz Fox | 9d5fa2bcc4 | |
Samantaz Fox | 9310d09f93 | |
syeopite | 67571b2492 | |
maboroshin | d5df81f0f8 | |
maboroshin | eb27e097ed | |
Timothy Redaelli | 9ce9c54399 | |
Corné Dorrestijn | 16c79f1ef5 | |
Samantaz Fox | 3a5d408602 | |
Samantaz Fox | 7e363fa3c8 | |
Samantaz Fox | d9416a0be5 | |
Brahim Hadriche | b40cf6544a | |
ChunkyProgrammer | 8338a73e7b | |
ChunkyProgrammer | 86ee761788 | |
Samantaz Fox | c5b87e3b5e | |
syeopite | ed8b84ed15 | |
syeopite | 8ce91166d6 | |
syeopite | 8525758583 | |
syeopite | ab4c0a1d3c | |
syeopite | c31908a011 | |
syeopite | 2562f80695 | |
syeopite | fead0e14ac | |
syeopite | 438467f69a | |
Émilien (perso) | db3c57d49f | |
Brahim Hadriche | 3881038a32 | |
Brahim Hadriche | 7e267da5be | |
Brahim Hadriche | d7901c1e0d | |
Brahim Hadriche | 85a5bbd696 | |
syeopite | 8087e64dfe | |
Samantaz Fox | 07de1e236f | |
Samantaz Fox | 2414e7db41 | |
Samantaz Fox | 1a33012cad | |
Samantaz Fox | cf7c49deb0 | |
Samantaz Fox | d543a68a84 | |
Samantaz Fox | 2a65b5f52e | |
Samantaz Fox | 9072fa4355 | |
Samantaz Fox | 88cc62d45e | |
Samantaz Fox | 40919c6a83 | |
Ulysses Zhan | 3b219a4c7f | |
zlElo | b809e877a1 | |
Émilien (perso) | 0e4d3d89fc | |
RadoslavL | cc703b0274 | |
Ulysses Zhan | 81a4f29c73 | |
jt404 | d7ea5609b2 | |
ChunkyProgrammer | 0bd415158f | |
Ulysses Zhan | 50977fb5d9 | |
Ulysses Zhan | a1a0e4c59f | |
Ulysses Zhan | b9cbdce976 | |
jt404 | 8125ddca06 | |
xbdm | 069e91d2a6 | |
RadoslavL | 8e45e05fba | |
Samantaz Fox | 0aebac5f3e | |
Samantaz Fox | 60fae015d8 | |
Samantaz Fox | ce0e21400e | |
Samantaz Fox | 7b40775427 | |
Samantaz Fox | 1caaf63c8a | |
Samantaz Fox | eddb54adb1 | |
Samantaz Fox | 01491bf315 | |
Samantaz Fox | 8ca884a5a3 | |
Hosted Weblate | 3dc0574bb5 | |
Hosted Weblate | 42b6c8032f | |
Hosted Weblate | add6b3a602 | |
Hosted Weblate | 06e01f52cf | |
Hosted Weblate | b73ea63e55 | |
Hosted Weblate | 4723c1b3ee | |
Hosted Weblate | 53905ac55f | |
Hosted Weblate | f6fbabc15d | |
Hosted Weblate | 72aa4f6a6c | |
Hosted Weblate | 265bf2427c | |
Hosted Weblate | 0d055d4baa | |
Hosted Weblate | 9910939f43 | |
Hosted Weblate | 4917c5be4b | |
Hosted Weblate | 2326330988 | |
Hosted Weblate | dd21628792 | |
Hosted Weblate | 2d6ab80622 | |
Hosted Weblate | 0fe0524597 | |
Hosted Weblate | fdf05eaa2b | |
Hosted Weblate | 6799c0b9b8 | |
Hosted Weblate | 4824a1f59a | |
Hosted Weblate | 71cbe97f1a | |
Hosted Weblate | 18549e8d27 | |
Hosted Weblate | 9f695faf5d | |
Hosted Weblate | 3cc2b34795 | |
Hosted Weblate | c0d6217cad | |
Hosted Weblate | 34b206899d | |
Hosted Weblate | b56dd5a010 | |
Hosted Weblate | 68184e9d40 | |
RadoslavL | 7dc9b3f088 | |
Samantaz Fox | f26c995344 | |
RadoslavL | ed2a44149e | |
Jake Anto | 572d9cf4a7 | |
Samantaz Fox | 32310b7c9f | |
Jake Anto | 6d177b5fa4 | |
Jake Anto | 877037e114 | |
Jake Anto | 2e6101e623 | |
ChunkyProgrammer | f77e4378fe | |
ChunkyProgrammer | 4f25069f55 | |
Samantaz Fox | 47cc9dc169 | |
RadoslavL | ea781ceeee | |
syeopite | be2feba17c | |
syeopite | a999438ae4 | |
syeopite | e9d59a6dfd | |
syeopite | 4e97d8ad09 | |
syeopite | d371eb50f2 | |
syeopite | 0cb7d0b441 | |
syeopite | 54fa59cbb0 | |
RadoslavL | 8542c974c8 | |
RadoslavL | e8c9b85ef5 | |
ChunkyProgrammer | 8781520b8a | |
ChunkyProgrammer | bb04bcc42c | |
ChunkyProgrammer | f55b96a53b | |
ChunkyProgrammer | 734f1b7764 | |
ChunkyProgrammer | e3c365f3d6 | |
RadoslavL | 270d606ad8 | |
Samantaz Fox | 700c57559b | |
Samantaz Fox | ebee973b24 | |
Samantaz Fox | 06b2a4ba9d | |
Samantaz Fox | 58f4a012b7 | |
Samantaz Fox | 2456b62936 | |
Samantaz Fox | 792a999386 | |
RadoslavL | beec62cf0e | |
Samantaz Fox | 2425c47882 | |
Samantaz Fox | 49b9316b9f | |
Ming Kin Choi | 27d8fa112d | |
Ming Kin Choi | 2a092577c6 |
|
@ -38,16 +38,16 @@ jobs:
|
|||
matrix:
|
||||
stable: [true]
|
||||
crystal:
|
||||
- 1.6.2
|
||||
- 1.7.3
|
||||
- 1.8.2
|
||||
- 1.9.2
|
||||
- 1.10.1
|
||||
include:
|
||||
- crystal: nightly
|
||||
stable: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
|
@ -87,7 +87,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build Docker
|
||||
run: docker-compose build --build-arg release=0
|
||||
|
@ -103,18 +103,18 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build Docker ARM64 image
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile.arm64
|
||||
|
|
|
@ -11,7 +11,6 @@ on:
|
|||
- invidious.service
|
||||
- .git*
|
||||
- .editorconfig
|
||||
|
||||
- screenshots/*
|
||||
- .github/ISSUE_TEMPLATE/*
|
||||
- kubernetes/**
|
||||
|
@ -22,7 +21,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Crystal
|
||||
uses: crystal-lang/install-crystal@v1.8.0
|
||||
|
@ -38,42 +37,64 @@ jobs:
|
|||
fi
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.QUAY_USERNAME }}
|
||||
password: ${{ secrets.QUAY_PASSWORD }}
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: quay.io/invidious/invidious
|
||||
tags: |
|
||||
type=sha,format=short,prefix={{date 'YYYY.MM.DD'}}-,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
|
||||
labels: |
|
||||
quay.expires-after=12w
|
||||
|
||||
- name: Build and push Docker AMD64 image for Push Event
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
platforms: linux/amd64
|
||||
labels: quay.expires-after=12w
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
push: true
|
||||
tags: quay.io/invidious/invidious:${{ github.sha }},quay.io/invidious/invidious:latest
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
build-args: |
|
||||
"release=1"
|
||||
|
||||
- name: Docker meta
|
||||
id: meta-arm64
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: quay.io/invidious/invidious
|
||||
flavor: |
|
||||
suffix=-arm64
|
||||
tags: |
|
||||
type=sha,format=short,prefix={{date 'YYYY.MM.DD'}}-,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
|
||||
labels: |
|
||||
quay.expires-after=12w
|
||||
|
||||
- name: Build and push Docker ARM64 image for Push Event
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile.arm64
|
||||
platforms: linux/arm64/v8
|
||||
labels: quay.expires-after=12w
|
||||
labels: ${{ steps.meta-arm64.outputs.labels }}
|
||||
push: true
|
||||
tags: quay.io/invidious/invidious:${{ github.sha }}-arm64,quay.io/invidious/invidious:latest-arm64
|
||||
tags: ${{ steps.meta-arm64.outputs.tags }}
|
||||
build-args: |
|
||||
"release=1"
|
||||
|
|
|
@ -10,13 +10,13 @@ jobs:
|
|||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 365
|
||||
days-before-pr-stale: 90
|
||||
days-before-close: 30
|
||||
exempt-pr-labels: blocked
|
||||
exempt-pr-labels: blocked,exempt-stale
|
||||
stale-issue-message: 'This issue has been automatically marked as stale and will be closed in 30 days because it has not had recent activity and is much likely outdated. If you think this issue is still relevant and applicable, you just have to post a comment and it will be unmarked.'
|
||||
stale-pr-message: 'This pull request has been automatically marked as stale and will be closed in 30 days because it has not had recent activity and is much likely abandoned or outdated. If you think this pull request is still relevant and applicable, you just have to post a comment and it will be unmarked.'
|
||||
stale-issue-label: "stale"
|
||||
|
|
15
README.md
15
README.md
|
@ -82,7 +82,7 @@
|
|||
|
||||
**Data import/export**
|
||||
- Import subscriptions from YouTube, NewPipe and Freetube
|
||||
- Import watch history from NewPipe
|
||||
- Import watch history from YouTube and NewPipe
|
||||
- Export subscriptions to NewPipe and Freetube
|
||||
- Import/Export Invidious user data
|
||||
|
||||
|
@ -145,18 +145,7 @@ Weblate also allows you to log-in with major SSO providers like Github, Gitlab,
|
|||
|
||||
## Projects using Invidious
|
||||
|
||||
- [FreeTube](https://github.com/FreeTubeApp/FreeTube): A libre software YouTube app for privacy.
|
||||
- [CloudTube](https://sr.ht/~cadence/tube/): A JavaScript-rich alternate YouTube player.
|
||||
- [PeerTubeify](https://gitlab.com/Cha_de_L/peertubeify): On YouTube, displays a link to the same video on PeerTube, if it exists.
|
||||
- [MusicPiped](https://github.com/deep-gaurav/MusicPiped): A material design music player that streams music from YouTube.
|
||||
- [HoloPlay](https://github.com/stephane-r/holoplay-wa): Progressive Web App connecting on Invidious API's with search, playlists and favorites.
|
||||
- [WatchTube](https://github.com/WatchTubeTeam/WatchTube): Powerful YouTube client for Apple Watch.
|
||||
- [Yattee](https://github.com/yattee/yattee): Alternative YouTube frontend for iPhone, iPad, Mac and Apple TV.
|
||||
- [TubiTui](https://codeberg.org/777/TubiTui): A lightweight, libre, TUI-based YouTube client.
|
||||
- [Ytfzf](https://github.com/pystardust/ytfzf): A posix script to find and watch youtube videos from the terminal. (Without API).
|
||||
- [Playlet](https://github.com/iBicha/playlet): Unofficial Youtube client for Roku TV.
|
||||
- [Clipious](https://github.com/lamarios/clipious): Unofficial Invidious client for Android.
|
||||
|
||||
A list of projects and extensions for or utilizing Invidious can be found in the documentation: https://docs.invidious.io/applications/
|
||||
|
||||
## Liability
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Copyright (c) 2024 by Jennifer (https://codepen.io/jwjertzoch/pen/JjyGeRy)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall
|
||||
be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
.carousel {
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.slides {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
overflow-x: scroll;
|
||||
scrollbar-width: none;
|
||||
scroll-snap-type: x mandatory;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.slides::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slides-item {
|
||||
align-items: center;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
font-size: 100px;
|
||||
height: 600px;
|
||||
justify-content: center;
|
||||
margin: 0 1rem;
|
||||
position: relative;
|
||||
scroll-snap-align: start;
|
||||
transform: scale(1);
|
||||
transform-origin: center center;
|
||||
transition: transform .5s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.carousel__nav {
|
||||
padding: 1.25rem .5rem;
|
||||
}
|
||||
|
||||
.slider-nav {
|
||||
align-items: center;
|
||||
background-color: #ddd;
|
||||
border-radius: 50%;
|
||||
color: #000;
|
||||
display: inline-flex;
|
||||
height: 1.5rem;
|
||||
justify-content: center;
|
||||
padding: .5rem;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
.skip-link {
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: auto;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
align-items: center;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 30px;
|
||||
height: 30px;
|
||||
justify-content: center;
|
||||
opacity: .8;
|
||||
text-decoration: none;
|
||||
width: 50%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.light-theme .slider-nav {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.dark-theme .slider-nav {
|
||||
background-color: #0005;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.no-theme .slider-nav {
|
||||
background-color: #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.no-theme .slider-nav {
|
||||
background-color: #0005;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ body {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.h-box {
|
||||
|
@ -197,6 +198,7 @@ img.thumbnail {
|
|||
display: block; /* See: https://stackoverflow.com/a/11635197 */
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.thumbnail-placeholder {
|
||||
|
@ -392,11 +394,19 @@ p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
|
|||
* Comments & community posts
|
||||
*/
|
||||
|
||||
#comments {
|
||||
.comments {
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't want the top and bottom margin on the post page.
|
||||
*/
|
||||
.comments.post-comments {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.video-iframe-wrapper {
|
||||
position: relative;
|
||||
height: 0;
|
||||
|
@ -433,16 +443,26 @@ p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
|
|||
*/
|
||||
|
||||
footer {
|
||||
color: #919191;
|
||||
margin-top: auto;
|
||||
padding: 1.5em 0;
|
||||
text-align: center;
|
||||
max-height: 30vh;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #919191 !important;
|
||||
text-decoration: underline;
|
||||
.light-theme footer {
|
||||
color: #7c7c7c;
|
||||
}
|
||||
|
||||
.dark-theme footer {
|
||||
color: #adadad;
|
||||
}
|
||||
|
||||
.light-theme footer a {
|
||||
color: #7c7c7c !important;
|
||||
}
|
||||
|
||||
.dark-theme footer a {
|
||||
color: #adadad !important;
|
||||
}
|
||||
|
||||
footer span {
|
||||
|
@ -548,6 +568,14 @@ span > select {
|
|||
color: #303030;
|
||||
}
|
||||
|
||||
.no-theme footer {
|
||||
color: #7c7c7c;
|
||||
}
|
||||
|
||||
.no-theme footer a {
|
||||
color: #7c7c7c !important;
|
||||
}
|
||||
|
||||
.light-theme .pure-menu-heading {
|
||||
color: #565d64;
|
||||
}
|
||||
|
@ -581,7 +609,7 @@ span > select {
|
|||
}
|
||||
|
||||
.dark-theme a {
|
||||
color: #a0a0a0;
|
||||
color: #adadad;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@ -635,7 +663,7 @@ body.dark-theme {
|
|||
}
|
||||
|
||||
.no-theme a {
|
||||
color: #a0a0a0;
|
||||
color: #adadad;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@ -666,6 +694,14 @@ body.dark-theme {
|
|||
background-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.no-theme footer {
|
||||
color: #adadad;
|
||||
}
|
||||
|
||||
.no-theme footer a {
|
||||
color: #adadad !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -759,3 +795,7 @@ h1, h2, h3, h4, h5, p,
|
|||
.channel-emoji {
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
#download_widget {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
var video_data = JSON.parse(document.getElementById('video_data').textContent);
|
||||
|
||||
var spinnerHTML = '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
|
||||
var spinnerHTMLwithHR = spinnerHTML + '<hr>';
|
||||
|
||||
String.prototype.supplant = function (o) {
|
||||
return this.replace(/{([^{}]*)}/g, function (a, b) {
|
||||
var r = o[b];
|
||||
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||
});
|
||||
};
|
||||
|
||||
function toggle_comments(event) {
|
||||
var target = event.target;
|
||||
var body = target.parentNode.parentNode.parentNode.children[1];
|
||||
if (body.style.display === 'none') {
|
||||
target.textContent = '[ − ]';
|
||||
body.style.display = '';
|
||||
} else {
|
||||
target.textContent = '[ + ]';
|
||||
body.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function hide_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = 'none';
|
||||
|
||||
target.textContent = sub_text;
|
||||
target.onclick = show_youtube_replies;
|
||||
target.setAttribute('data-inner-text', inner_text);
|
||||
target.setAttribute('data-sub-text', sub_text);
|
||||
}
|
||||
|
||||
function show_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = '';
|
||||
|
||||
target.textContent = sub_text;
|
||||
target.onclick = hide_youtube_replies;
|
||||
target.setAttribute('data-inner-text', inner_text);
|
||||
target.setAttribute('data-sub-text', sub_text);
|
||||
}
|
||||
|
||||
function get_youtube_comments() {
|
||||
var comments = document.getElementById('comments');
|
||||
|
||||
var fallback = comments.innerHTML;
|
||||
comments.innerHTML = spinnerHTML;
|
||||
|
||||
var baseUrl = video_data.base_url || '/api/v1/comments/'+ video_data.id
|
||||
var url = baseUrl +
|
||||
'?format=html' +
|
||||
'&hl=' + video_data.preferences.locale +
|
||||
'&thin_mode=' + video_data.preferences.thin_mode;
|
||||
|
||||
if (video_data.ucid) {
|
||||
url += '&ucid=' + video_data.ucid
|
||||
}
|
||||
|
||||
var onNon200 = function (xhr) { comments.innerHTML = fallback; };
|
||||
if (video_data.params.comments[1] === 'youtube')
|
||||
onNon200 = function (xhr) {};
|
||||
|
||||
helpers.xhr('GET', url, {retries: 5, entity_name: 'comments'}, {
|
||||
on200: function (response) {
|
||||
var commentInnerHtml = ' \
|
||||
<div> \
|
||||
<h3> \
|
||||
<a href="javascript:void(0)">[ − ]</a> \
|
||||
{commentsText} \
|
||||
</h3> \
|
||||
<b> \
|
||||
'
|
||||
if (video_data.support_reddit) {
|
||||
commentInnerHtml += ' <a href="javascript:void(0)" data-comments="reddit"> \
|
||||
{redditComments} \
|
||||
</a> \
|
||||
'
|
||||
}
|
||||
commentInnerHtml += ' </b> \
|
||||
</div> \
|
||||
<div>{contentHtml}</div> \
|
||||
<hr>'
|
||||
commentInnerHtml = commentInnerHtml.supplant({
|
||||
contentHtml: response.contentHtml,
|
||||
redditComments: video_data.reddit_comments_text,
|
||||
commentsText: video_data.comments_text.supplant({
|
||||
// toLocaleString correctly splits number with local thousands separator. e.g.:
|
||||
// '1,234,567.89' for user with English locale
|
||||
// '1 234 567,89' for user with Russian locale
|
||||
// '1.234.567,89' for user with Portuguese locale
|
||||
commentCount: response.commentCount.toLocaleString()
|
||||
})
|
||||
});
|
||||
comments.innerHTML = commentInnerHtml;
|
||||
comments.children[0].children[0].children[0].onclick = toggle_comments;
|
||||
if (video_data.support_reddit) {
|
||||
comments.children[0].children[1].children[0].onclick = swap_comments;
|
||||
}
|
||||
},
|
||||
onNon200: onNon200, // declared above
|
||||
onError: function (xhr) {
|
||||
comments.innerHTML = spinnerHTML;
|
||||
},
|
||||
onTimeout: function (xhr) {
|
||||
comments.innerHTML = spinnerHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_youtube_replies(target, load_more, load_replies) {
|
||||
var continuation = target.getAttribute('data-continuation');
|
||||
|
||||
var body = target.parentNode.parentNode;
|
||||
var fallback = body.innerHTML;
|
||||
body.innerHTML = spinnerHTML;
|
||||
var baseUrl = video_data.base_url || '/api/v1/comments/'+ video_data.id
|
||||
var url = baseUrl +
|
||||
'?format=html' +
|
||||
'&hl=' + video_data.preferences.locale +
|
||||
'&thin_mode=' + video_data.preferences.thin_mode +
|
||||
'&continuation=' + continuation;
|
||||
|
||||
if (video_data.ucid) {
|
||||
url += '&ucid=' + video_data.ucid
|
||||
}
|
||||
if (load_replies) url += '&action=action_get_comment_replies';
|
||||
|
||||
helpers.xhr('GET', url, {}, {
|
||||
on200: function (response) {
|
||||
if (load_more) {
|
||||
body = body.parentNode.parentNode;
|
||||
body.removeChild(body.lastElementChild);
|
||||
body.insertAdjacentHTML('beforeend', response.contentHtml);
|
||||
} else {
|
||||
body.removeChild(body.lastElementChild);
|
||||
|
||||
var p = document.createElement('p');
|
||||
var a = document.createElement('a');
|
||||
p.appendChild(a);
|
||||
|
||||
a.href = 'javascript:void(0)';
|
||||
a.onclick = hide_youtube_replies;
|
||||
a.setAttribute('data-sub-text', video_data.hide_replies_text);
|
||||
a.setAttribute('data-inner-text', video_data.show_replies_text);
|
||||
a.textContent = video_data.hide_replies_text;
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = response.contentHtml;
|
||||
|
||||
body.appendChild(p);
|
||||
body.appendChild(div);
|
||||
}
|
||||
},
|
||||
onNon200: function (xhr) {
|
||||
body.innerHTML = fallback;
|
||||
},
|
||||
onTimeout: function (xhr) {
|
||||
console.warn('Pulling comments failed');
|
||||
body.innerHTML = fallback;
|
||||
}
|
||||
});
|
||||
}
|
|
@ -10,7 +10,7 @@ var notifications, delivered;
|
|||
var notifications_mock = { close: function () { } };
|
||||
|
||||
function get_subscriptions() {
|
||||
helpers.xhr('GET', '/api/v1/auth/subscriptions?fields=authorId', {
|
||||
helpers.xhr('GET', '/api/v1/auth/subscriptions', {
|
||||
retries: 5,
|
||||
entity_name: 'subscriptions'
|
||||
}, {
|
||||
|
@ -22,7 +22,7 @@ function create_notification_stream(subscriptions) {
|
|||
// sse.js can't be replaced to EventSource in place as it lack support of payload and headers
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
|
||||
notifications = new SSE(
|
||||
'/api/v1/auth/notifications?fields=videoId,title,author,authorId,publishedText,published,authorThumbnails,liveNow', {
|
||||
'/api/v1/auth/notifications', {
|
||||
withCredentials: true,
|
||||
payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId; }).join(','),
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
|
|
|
@ -98,11 +98,13 @@ if (video_data.params.quality === 'dash') {
|
|||
|
||||
/**
|
||||
* Function for add time argument to url
|
||||
*
|
||||
* @param {String} url
|
||||
* @param {String} [base]
|
||||
* @returns {URL} urlWithTimeArg
|
||||
*/
|
||||
function addCurrentTimeToURL(url) {
|
||||
var urlUsed = new URL(url);
|
||||
function addCurrentTimeToURL(url, base) {
|
||||
var urlUsed = new URL(url, base);
|
||||
urlUsed.searchParams.delete('start');
|
||||
var currentTime = Math.ceil(player.currentTime());
|
||||
if (currentTime > 0)
|
||||
|
@ -112,6 +114,50 @@ function addCurrentTimeToURL(url) {
|
|||
return urlUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Global variable to save the last timestamp (in full seconds) at which the external
|
||||
* links were updated by the 'timeupdate' callback below.
|
||||
*
|
||||
* It is initialized to 5s so that the video will always restart from the beginning
|
||||
* if the user hasn't really started watching before switching to the other website.
|
||||
*/
|
||||
var timeupdate_last_ts = 5;
|
||||
|
||||
/**
|
||||
* Callback that updates the timestamp on all external links
|
||||
*/
|
||||
player.on('timeupdate', function () {
|
||||
// Only update once every second
|
||||
let current_ts = Math.floor(player.currentTime());
|
||||
if (current_ts > timeupdate_last_ts) timeupdate_last_ts = current_ts;
|
||||
else return;
|
||||
|
||||
// YouTube links
|
||||
|
||||
let elem_yt_watch = document.getElementById('link-yt-watch');
|
||||
let elem_yt_embed = document.getElementById('link-yt-embed');
|
||||
|
||||
let base_url_yt_watch = elem_yt_watch.getAttribute('data-base-url');
|
||||
let base_url_yt_embed = elem_yt_embed.getAttribute('data-base-url');
|
||||
|
||||
elem_yt_watch.href = addCurrentTimeToURL(base_url_yt_watch);
|
||||
elem_yt_embed.href = addCurrentTimeToURL(base_url_yt_embed);
|
||||
|
||||
// Invidious links
|
||||
|
||||
let domain = window.location.origin;
|
||||
|
||||
let elem_iv_embed = document.getElementById('link-iv-embed');
|
||||
let elem_iv_other = document.getElementById('link-iv-other');
|
||||
|
||||
let base_url_iv_embed = elem_iv_embed.getAttribute('data-base-url');
|
||||
let base_url_iv_other = elem_iv_other.getAttribute('data-base-url');
|
||||
|
||||
elem_iv_embed.href = addCurrentTimeToURL(base_url_iv_embed, domain);
|
||||
elem_iv_other.href = addCurrentTimeToURL(base_url_iv_other, domain);
|
||||
});
|
||||
|
||||
|
||||
var shareOptions = {
|
||||
socials: ['fbFeed', 'tw', 'reddit', 'email'],
|
||||
|
||||
|
@ -701,6 +747,17 @@ if (navigator.vendor === 'Apple Computer, Inc.' && video_data.params.listen) {
|
|||
});
|
||||
}
|
||||
|
||||
// Safari screen timeout on looped video playback fix
|
||||
if (navigator.vendor === 'Apple Computer, Inc.' && !video_data.params.listen && video_data.params.video_loop) {
|
||||
player.loop(false);
|
||||
player.ready(function () {
|
||||
player.on('ended', function () {
|
||||
player.currentTime(0);
|
||||
player.play();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Watch on Invidious link
|
||||
if (location.pathname.startsWith('/embed/')) {
|
||||
const Button = videojs.getComponent('Button');
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
addEventListener('load', function (e) {
|
||||
get_youtube_comments();
|
||||
});
|
|
@ -1,14 +1,4 @@
|
|||
'use strict';
|
||||
var video_data = JSON.parse(document.getElementById('video_data').textContent);
|
||||
var spinnerHTML = '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
|
||||
var spinnerHTMLwithHR = spinnerHTML + '<hr>';
|
||||
|
||||
String.prototype.supplant = function (o) {
|
||||
return this.replace(/{([^{}]*)}/g, function (a, b) {
|
||||
var r = o[b];
|
||||
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||
});
|
||||
};
|
||||
|
||||
function toggle_parent(target) {
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
|
@ -21,18 +11,6 @@ function toggle_parent(target) {
|
|||
}
|
||||
}
|
||||
|
||||
function toggle_comments(event) {
|
||||
var target = event.target;
|
||||
var body = target.parentNode.parentNode.parentNode.children[1];
|
||||
if (body.style.display === 'none') {
|
||||
target.textContent = '[ − ]';
|
||||
body.style.display = '';
|
||||
} else {
|
||||
target.textContent = '[ + ]';
|
||||
body.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function swap_comments(event) {
|
||||
var source = event.target.getAttribute('data-comments');
|
||||
|
||||
|
@ -43,36 +21,6 @@ function swap_comments(event) {
|
|||
}
|
||||
}
|
||||
|
||||
function hide_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = 'none';
|
||||
|
||||
target.textContent = sub_text;
|
||||
target.onclick = show_youtube_replies;
|
||||
target.setAttribute('data-inner-text', inner_text);
|
||||
target.setAttribute('data-sub-text', sub_text);
|
||||
}
|
||||
|
||||
function show_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
|
||||
var body = target.parentNode.parentNode.children[1];
|
||||
body.style.display = '';
|
||||
|
||||
target.textContent = sub_text;
|
||||
target.onclick = hide_youtube_replies;
|
||||
target.setAttribute('data-inner-text', inner_text);
|
||||
target.setAttribute('data-sub-text', sub_text);
|
||||
}
|
||||
|
||||
var continue_button = document.getElementById('continue');
|
||||
if (continue_button) {
|
||||
continue_button.onclick = continue_autoplay;
|
||||
|
@ -208,111 +156,6 @@ function get_reddit_comments() {
|
|||
});
|
||||
}
|
||||
|
||||
function get_youtube_comments() {
|
||||
var comments = document.getElementById('comments');
|
||||
|
||||
var fallback = comments.innerHTML;
|
||||
comments.innerHTML = spinnerHTML;
|
||||
|
||||
var url = '/api/v1/comments/' + video_data.id +
|
||||
'?format=html' +
|
||||
'&hl=' + video_data.preferences.locale +
|
||||
'&thin_mode=' + video_data.preferences.thin_mode;
|
||||
|
||||
var onNon200 = function (xhr) { comments.innerHTML = fallback; };
|
||||
if (video_data.params.comments[1] === 'youtube')
|
||||
onNon200 = function (xhr) {};
|
||||
|
||||
helpers.xhr('GET', url, {retries: 5, entity_name: 'comments'}, {
|
||||
on200: function (response) {
|
||||
comments.innerHTML = ' \
|
||||
<div> \
|
||||
<h3> \
|
||||
<a href="javascript:void(0)">[ − ]</a> \
|
||||
{commentsText} \
|
||||
</h3> \
|
||||
<b> \
|
||||
<a href="javascript:void(0)" data-comments="reddit"> \
|
||||
{redditComments} \
|
||||
</a> \
|
||||
</b> \
|
||||
</div> \
|
||||
<div>{contentHtml}</div> \
|
||||
<hr>'.supplant({
|
||||
contentHtml: response.contentHtml,
|
||||
redditComments: video_data.reddit_comments_text,
|
||||
commentsText: video_data.comments_text.supplant({
|
||||
// toLocaleString correctly splits number with local thousands separator. e.g.:
|
||||
// '1,234,567.89' for user with English locale
|
||||
// '1 234 567,89' for user with Russian locale
|
||||
// '1.234.567,89' for user with Portuguese locale
|
||||
commentCount: response.commentCount.toLocaleString()
|
||||
})
|
||||
});
|
||||
|
||||
comments.children[0].children[0].children[0].onclick = toggle_comments;
|
||||
comments.children[0].children[1].children[0].onclick = swap_comments;
|
||||
},
|
||||
onNon200: onNon200, // declared above
|
||||
onError: function (xhr) {
|
||||
comments.innerHTML = spinnerHTML;
|
||||
},
|
||||
onTimeout: function (xhr) {
|
||||
comments.innerHTML = spinnerHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_youtube_replies(target, load_more, load_replies) {
|
||||
var continuation = target.getAttribute('data-continuation');
|
||||
|
||||
var body = target.parentNode.parentNode;
|
||||
var fallback = body.innerHTML;
|
||||
body.innerHTML = spinnerHTML;
|
||||
|
||||
var url = '/api/v1/comments/' + video_data.id +
|
||||
'?format=html' +
|
||||
'&hl=' + video_data.preferences.locale +
|
||||
'&thin_mode=' + video_data.preferences.thin_mode +
|
||||
'&continuation=' + continuation;
|
||||
if (load_replies) url += '&action=action_get_comment_replies';
|
||||
|
||||
helpers.xhr('GET', url, {}, {
|
||||
on200: function (response) {
|
||||
if (load_more) {
|
||||
body = body.parentNode.parentNode;
|
||||
body.removeChild(body.lastElementChild);
|
||||
body.insertAdjacentHTML('beforeend', response.contentHtml);
|
||||
} else {
|
||||
body.removeChild(body.lastElementChild);
|
||||
|
||||
var p = document.createElement('p');
|
||||
var a = document.createElement('a');
|
||||
p.appendChild(a);
|
||||
|
||||
a.href = 'javascript:void(0)';
|
||||
a.onclick = hide_youtube_replies;
|
||||
a.setAttribute('data-sub-text', video_data.hide_replies_text);
|
||||
a.setAttribute('data-inner-text', video_data.show_replies_text);
|
||||
a.textContent = video_data.hide_replies_text;
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = response.contentHtml;
|
||||
|
||||
body.appendChild(p);
|
||||
body.appendChild(div);
|
||||
}
|
||||
},
|
||||
onNon200: function (xhr) {
|
||||
body.innerHTML = fallback;
|
||||
},
|
||||
onTimeout: function (xhr) {
|
||||
console.warn('Pulling comments failed');
|
||||
body.innerHTML = fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (video_data.play_next) {
|
||||
player.on('ended', function () {
|
||||
var url = new URL('https://example.com/watch?v=' + video_data.next_video);
|
||||
|
|
|
@ -15,5 +15,7 @@
|
|||
],
|
||||
"theme_color": "#575757",
|
||||
"background_color": "#575757",
|
||||
"display": "standalone"
|
||||
"display": "standalone",
|
||||
"description": "An alternative front-end to YouTube",
|
||||
"start_url": "/"
|
||||
}
|
||||
|
|
|
@ -392,27 +392,6 @@ jobs:
|
|||
enable: true
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Captcha API
|
||||
# -----------------------------
|
||||
|
||||
##
|
||||
## URL of the captcha solving service.
|
||||
##
|
||||
## Accepted values: any URL
|
||||
## Default: https://api.anti-captcha.com
|
||||
##
|
||||
#captcha_api_url: https://api.anti-captcha.com
|
||||
|
||||
##
|
||||
## API key for the captcha solving service.
|
||||
##
|
||||
## Accepted values: a string
|
||||
## Default: <none>
|
||||
##
|
||||
#captcha_key:
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Miscellaneous
|
||||
# -----------------------------
|
||||
|
|
|
@ -32,15 +32,13 @@ services:
|
|||
# statistics_enabled: false
|
||||
hmac_key: "CHANGE_ME!!"
|
||||
healthcheck:
|
||||
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
|
||||
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/trending || exit 1
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 2
|
||||
depends_on:
|
||||
- invidious-db
|
||||
|
||||
invidious-db:
|
||||
image: docker.io/library/postgres:13
|
||||
image: docker.io/library/postgres:14
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgresdata:/var/lib/postgresql/data
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
FROM crystallang/crystal:1.4.1-alpine AS builder
|
||||
FROM crystallang/crystal:1.8.2-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache sqlite-static yaml-static
|
||||
|
||||
ARG release
|
||||
|
@ -19,8 +20,7 @@ COPY ./assets/ ./assets/
|
|||
COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
|
||||
|
||||
RUN crystal spec --warnings all \
|
||||
--link-flags "-lxml2 -llzma"
|
||||
|
||||
--link-flags "-lxml2 -llzma"
|
||||
RUN if [[ "${release}" == 1 ]] ; then \
|
||||
crystal build ./src/invidious.cr \
|
||||
--release \
|
||||
|
@ -32,9 +32,8 @@ RUN if [[ "${release}" == 1 ]] ; then \
|
|||
--link-flags "-lxml2 -llzma"; \
|
||||
fi
|
||||
|
||||
|
||||
FROM alpine:3.16
|
||||
RUN apk add --no-cache librsvg ttf-opensans tini
|
||||
FROM alpine:3.18
|
||||
RUN apk add --no-cache rsvg-convert ttf-opensans tini
|
||||
WORKDIR /invidious
|
||||
RUN addgroup -g 1000 -S invidious && \
|
||||
adduser -u 1000 -S invidious -G invidious
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
FROM alpine:3.16 AS builder
|
||||
RUN apk add --no-cache 'crystal=1.4.1-r0' shards sqlite-static yaml-static yaml-dev libxml2-dev zlib-static openssl-libs-static openssl-dev musl-dev
|
||||
FROM alpine:3.18 AS builder
|
||||
RUN apk add --no-cache 'crystal=1.8.2-r0' shards sqlite-static yaml-static yaml-dev libxml2-static zlib-static openssl-libs-static openssl-dev musl-dev xz-static
|
||||
|
||||
ARG release
|
||||
|
||||
|
@ -32,8 +32,8 @@ RUN if [[ "${release}" == 1 ]] ; then \
|
|||
--link-flags "-lxml2 -llzma"; \
|
||||
fi
|
||||
|
||||
FROM alpine:3.16
|
||||
RUN apk add --no-cache librsvg ttf-opensans tini
|
||||
FROM alpine:3.18
|
||||
RUN apk add --no-cache rsvg-convert ttf-opensans tini
|
||||
WORKDIR /invidious
|
||||
RUN addgroup -g 1000 -S invidious && \
|
||||
adduser -u 1000 -S invidious -G invidious
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
dependencies:
|
||||
- name: postgresql
|
||||
repository: https://charts.bitnami.com/bitnami/
|
||||
version: 12.1.9
|
||||
digest: sha256:71ff342a6c0a98bece3d7fe199983afb2113f8db65a3e3819de875af2c45add7
|
||||
generated: "2023-01-20T20:42:32.757707004Z"
|
||||
version: 12.11.1
|
||||
digest: sha256:3c10008175c4f5c1cec38782f5a7316154b89074c77ebbd9bcc4be4f5ff21122
|
||||
generated: "2023-09-14T22:40:43.171275362Z"
|
||||
|
|
|
@ -17,6 +17,6 @@ maintainers:
|
|||
email: mail@leonklingele.de
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: ~12.1.6
|
||||
version: ~12.11.0
|
||||
repository: "https://charts.bitnami.com/bitnami/"
|
||||
engine: gotpl
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"Time (h:mm:ss):": "الوقت (h:mm:ss):",
|
||||
"Text CAPTCHA": "نص الكابتشا",
|
||||
"Image CAPTCHA": "صورة الكابتشا",
|
||||
"Sign In": "تسجيل الدخول",
|
||||
"Sign In": "إنشاء حساب",
|
||||
"Register": "التسجيل",
|
||||
"E-mail": "البريد الإلكتروني",
|
||||
"Preferences": "الإعدادات",
|
||||
|
@ -548,5 +548,13 @@
|
|||
"generic_button_rss": "RSS",
|
||||
"channel_tab_releases_label": "الإصدارات",
|
||||
"playlist_button_add_items": "إضافة مقاطع فيديو",
|
||||
"channel_tab_podcasts_label": "البودكاست"
|
||||
"channel_tab_podcasts_label": "البودكاست",
|
||||
"generic_channels_count_0": "{{count}} قناة",
|
||||
"generic_channels_count_1": "{{count}} قناة",
|
||||
"generic_channels_count_2": "{{count}} قناتان",
|
||||
"generic_channels_count_3": "{{count}} قنوات",
|
||||
"generic_channels_count_4": "{{count}} قنوات",
|
||||
"generic_channels_count_5": "{{count}} قناة",
|
||||
"Import YouTube watch history (.json)": "استيراد سجل مشاهدة YouTube بصيغة (.json)",
|
||||
"toggle_theme": "تبديل الموضوع"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,491 @@
|
|||
{
|
||||
"Korean (auto-generated)": "Корейски (автоматично генерирано)",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"published - reverse": "публикувани - в обратен ред",
|
||||
"preferences_quality_dash_option_worst": "Най-ниско качество",
|
||||
"Password is a required field": "Парола е задължитело поле",
|
||||
"channel_tab_podcasts_label": "Подкасти",
|
||||
"Token is expired, please try again": "Токенът е изтекъл, моля опитайте отново",
|
||||
"Turkish": "Турски",
|
||||
"preferences_save_player_pos_label": "Запази позицията на плейъра: ",
|
||||
"View Reddit comments": "Виж Reddit коментари",
|
||||
"Export data as JSON": "Експортиране на Invidious информацията като JSON",
|
||||
"About": "За сайта",
|
||||
"Save preferences": "Запази промените",
|
||||
"Load more": "Зареди още",
|
||||
"Import/export": "Импортиране/експортиране",
|
||||
"Albanian": "Албански",
|
||||
"New password": "Нова парола",
|
||||
"Southern Sotho": "Южен Сото",
|
||||
"channel_tab_videos_label": "Видеа",
|
||||
"Spanish (Mexico)": "Испански (Мексико)",
|
||||
"preferences_player_style_label": "Стил на плейъра: ",
|
||||
"preferences_region_label": "Държавата на съдържанието: ",
|
||||
"Premieres in `x`": "Премиера в `x`",
|
||||
"Watch history": "История на гледане",
|
||||
"generic_subscriptions_count": "{{count}} абонамент",
|
||||
"generic_subscriptions_count_plural": "{{count}} абонамента",
|
||||
"preferences_continue_label": "Пускай следващото видео автоматично: ",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Здравей! Изглежда си изключил JavaScript. Натисни тук за да видиш коментарите, но обърни внимание, че може да отнеме повече време да заредят.",
|
||||
"Polish": "Полски",
|
||||
"Icelandic": "Исландски",
|
||||
"preferences_local_label": "Пускане на видеа през прокси: ",
|
||||
"Hebrew": "Иврит",
|
||||
"Fallback captions: ": "Резервни надписи: ",
|
||||
"search_filters_title": "Филтри",
|
||||
"search_filters_apply_button": "Приложете избрани филтри",
|
||||
"Download is disabled": "Изтеглянето е деактивирано",
|
||||
"User ID is a required field": "Потребителско име е задължително поле",
|
||||
"comments_points_count": "{{count}} точка",
|
||||
"comments_points_count_plural": "{{count}} точки",
|
||||
"next_steps_error_message_go_to_youtube": "Отидеш в YouTube",
|
||||
"preferences_quality_dash_option_2160p": "2160p",
|
||||
"search_filters_type_option_video": "Видео",
|
||||
"Spanish (Latin America)": "Испански (Латинска Америка)",
|
||||
"Download as: ": "Изтегли като: ",
|
||||
"Default": "По подразбиране",
|
||||
"search_filters_sort_option_views": "Гледания",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"Igbo": "Игбо",
|
||||
"Subscriptions": "Абонаменти",
|
||||
"German (auto-generated)": "Немски (автоматично генерирано)",
|
||||
"`x` is live": "`x` е на живо",
|
||||
"Azerbaijani": "Азербайджански",
|
||||
"Premieres `x`": "Премиера `x`",
|
||||
"Japanese (auto-generated)": "Японски (автоматично генерирано)",
|
||||
"preferences_quality_option_medium": "Средно",
|
||||
"footer_donate_page": "Даряване",
|
||||
"Show replies": "Покажи отговорите",
|
||||
"Esperanto": "Есперанто",
|
||||
"search_message_change_filters_or_query": "Опитай да разшириш търсенето си и/или да смениш филтрите.",
|
||||
"CAPTCHA enabled: ": "Активиране на CAPTCHA: ",
|
||||
"View playlist on YouTube": "Виж плейлиста в YouTube",
|
||||
"crash_page_before_reporting": "Преди докладването на бъг, бъди сигурен, че си:",
|
||||
"Top enabled: ": "Активиране на страница с топ видеа: ",
|
||||
"preferences_quality_dash_option_best": "Най-високо",
|
||||
"search_filters_duration_label": "Продължителност",
|
||||
"Slovak": "Словашки",
|
||||
"Channel Sponsor": "Канален спонсор",
|
||||
"generic_videos_count": "{{count}} видео",
|
||||
"generic_videos_count_plural": "{{count}} видеа",
|
||||
"videoinfo_started_streaming_x_ago": "Започна да излъчва преди `x`",
|
||||
"videoinfo_youTube_embed_link": "Вграждане",
|
||||
"channel_tab_streams_label": "Стриймове",
|
||||
"oldest": "най-стари",
|
||||
"playlist_button_add_items": "Добавяне на видеа",
|
||||
"Import NewPipe data (.zip)": "Импортиране на NewPipe информация (.zip)",
|
||||
"Clear watch history": "Изчистване на историята на гледане",
|
||||
"generic_count_minutes": "{{count}} минута",
|
||||
"generic_count_minutes_plural": "{{count}} минути",
|
||||
"published": "публикувани",
|
||||
"Show annotations": "Покажи анотации",
|
||||
"Login enabled: ": "Активиране на впизване: ",
|
||||
"Somali": "Сомалийски",
|
||||
"YouTube comment permalink": "Постоянна връзка на коментарите на YouTube",
|
||||
"Kurdish": "Кюрдски",
|
||||
"search_filters_date_option_hour": "Последния час",
|
||||
"Lao": "Лаоски",
|
||||
"Maltese": "Малтийски",
|
||||
"Register": "Регистрация",
|
||||
"View channel on YouTube": "Виж канала в YouTube",
|
||||
"Playlist privacy": "Поверителен плейлист",
|
||||
"preferences_unseen_only_label": "Показвай само негледаните: ",
|
||||
"Gujarati": "Гуджарати",
|
||||
"Please log in": "Моля влезте",
|
||||
"search_filters_sort_option_rating": "Рейтинг",
|
||||
"Manage subscriptions": "Управление на абонаментите",
|
||||
"preferences_quality_dash_option_720p": "720p",
|
||||
"preferences_watch_history_label": "Активирай историята на гледане: ",
|
||||
"user_saved_playlists": "`x` запази плейлисти",
|
||||
"preferences_extend_desc_label": "Автоматично разшири описанието на видеото ",
|
||||
"preferences_max_results_label": "Брой видеа показани на началната страница: ",
|
||||
"Spanish (Spain)": "Испански (Испания)",
|
||||
"invidious": "Invidious",
|
||||
"crash_page_refresh": "пробвал да <a href=\"`x`\">опресниш страницата</a>",
|
||||
"Image CAPTCHA": "CAPTCHA с Изображение",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"Chinese (Hong Kong)": "Китайски (Хонг Конг)",
|
||||
"Import Invidious data": "Импортиране на Invidious JSON информацията",
|
||||
"Blacklisted regions: ": "Неразрешени региони: ",
|
||||
"Only show latest video from channel: ": "Показвай само най-новите видеа в канала: ",
|
||||
"Hmong": "Хмонг",
|
||||
"French": "Френски",
|
||||
"search_filters_type_option_channel": "Канал",
|
||||
"Artist: ": "Артист: ",
|
||||
"generic_count_months": "{{count}} месец",
|
||||
"generic_count_months_plural": "{{count}} месеца",
|
||||
"preferences_annotations_subscribed_label": "Показвай анотаций по подразбиране за абонирани канали? ",
|
||||
"search_message_use_another_instance": " Можеш също да <a href=\"`x`\">търсиш на друга инстанция</a>.",
|
||||
"Danish": "Датски",
|
||||
"generic_subscribers_count": "{{count}} абонат",
|
||||
"generic_subscribers_count_plural": "{{count}} абоната",
|
||||
"Galician": "Галисий",
|
||||
"newest": "най-нови",
|
||||
"Empty playlist": "Плейлиста е празен",
|
||||
"download_subtitles": "Субритри - `x` (.vtt)",
|
||||
"preferences_category_misc": "Различни предпочитания",
|
||||
"Uzbek": "Узбекски",
|
||||
"View JavaScript license information.": "Виж Javascript лиценза.",
|
||||
"Filipino": "Филипински",
|
||||
"Malagasy": "Мадагаскарски",
|
||||
"generic_button_save": "Запиши",
|
||||
"Dark mode: ": "Тъмен режим: ",
|
||||
"Public": "Публичен",
|
||||
"Basque": "Баскски",
|
||||
"channel:`x`": "Канал:`x`",
|
||||
"Armenian": "Арменски",
|
||||
"This channel does not exist.": "Този канал не съществува.",
|
||||
"Luxembourgish": "Люксембургски",
|
||||
"preferences_related_videos_label": "Покажи подобни видеа: ",
|
||||
"English": "Английски",
|
||||
"Delete account": "Изтриване на акаунт",
|
||||
"Gaming": "Игри",
|
||||
"Video mode": "Видео режим",
|
||||
"preferences_dark_mode_label": "Тема: ",
|
||||
"crash_page_search_issue": "потърсил за <a href=\"`x`\">съществуващи проблеми в GitHub</a>",
|
||||
"preferences_category_subscription": "Предпочитания за абонаменти",
|
||||
"last": "най-скорощни",
|
||||
"Chinese (Simplified)": "Китайски (Опростен)",
|
||||
"Could not create mix.": "Създаването на микс е неуспешно.",
|
||||
"generic_button_cancel": "Отказ",
|
||||
"search_filters_type_option_movie": "Филм",
|
||||
"search_filters_date_option_year": "Тази година",
|
||||
"Swedish": "Шведски",
|
||||
"Previous page": "Предишна страница",
|
||||
"none": "нищо",
|
||||
"popular": "най-популярни",
|
||||
"Unsubscribe": "Отписване",
|
||||
"Slovenian": "Словенски",
|
||||
"Nepali": "Непалски",
|
||||
"Time (h:mm:ss):": "Време (h:mm:ss):",
|
||||
"English (auto-generated)": "Английски (автоматично генерирано)",
|
||||
"search_filters_sort_label": "Сортирай по",
|
||||
"View more comments on Reddit": "Виж повече коментари в Reddit",
|
||||
"Sinhala": "Синхалски",
|
||||
"preferences_feed_menu_label": "Меню с препоръки: ",
|
||||
"preferences_autoplay_label": "Автоматично пускане: ",
|
||||
"Pashto": "Пущунски",
|
||||
"English (United States)": "Английски (САЩ)",
|
||||
"Sign In": "Вход",
|
||||
"subscriptions_unseen_notifs_count": "{{count}} невидяно известие",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}} невидяни известия",
|
||||
"Log in": "Вход",
|
||||
"Engagement: ": "Участие: ",
|
||||
"Album: ": "Албум: ",
|
||||
"preferences_speed_label": "Скорост по подразбиране: ",
|
||||
"Import FreeTube subscriptions (.db)": "Импортиране на FreeTube абонаменти (.db)",
|
||||
"preferences_quality_option_dash": "DASH (адаптивно качество)",
|
||||
"preferences_show_nick_label": "Показвай потребителското име отгоре: ",
|
||||
"Private": "Частен",
|
||||
"Samoan": "Самоански",
|
||||
"preferences_notifications_only_label": "Показвай само известията (ако има такива): ",
|
||||
"Create playlist": "Създаване на плейлист",
|
||||
"next_steps_error_message_refresh": "Опресниш",
|
||||
"Top": "Топ",
|
||||
"preferences_quality_dash_option_1080p": "1080p",
|
||||
"Malayalam": "Малаялам",
|
||||
"Token": "Токен",
|
||||
"preferences_comments_label": "Коментари по подразбиране: ",
|
||||
"Movies": "Филми",
|
||||
"light": "светла",
|
||||
"Unlisted": "Скрит",
|
||||
"preferences_category_admin": "Администраторни предпочитания",
|
||||
"Erroneous token": "Невалиден токен",
|
||||
"No": "Не",
|
||||
"CAPTCHA is a required field": "CAPTCHA е задължително поле",
|
||||
"Video unavailable": "Неналично видео",
|
||||
"footer_source_code": "Изходен код",
|
||||
"New passwords must match": "Новите пароли трябва да съвпадат",
|
||||
"Playlist does not exist.": "Плейлиста не съществува.",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Експортиране на абонаментите като OPML (за NewPipe и FreeTube)",
|
||||
"search_filters_duration_option_short": "Кратко (< 4 минути)",
|
||||
"search_filters_duration_option_long": "Дълго (> 20 минути)",
|
||||
"tokens_count": "{{count}} токен",
|
||||
"tokens_count_plural": "{{count}} токена",
|
||||
"Yes": "Да",
|
||||
"Dutch": "Холандски",
|
||||
"Arabic": "Арабски",
|
||||
"An alternative front-end to YouTube": "Алтернативен преден план на YouTube",
|
||||
"View `x` comments": {
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Виж `x` коментар",
|
||||
"": "Виж `x` коментари"
|
||||
},
|
||||
"Chinese (China)": "Китайски (Китай)",
|
||||
"Italian (auto-generated)": "Италиански (автоматично генерирано)",
|
||||
"alphabetically - reverse": "обратно на азбучния ред",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"`x` marked it with a ❤": "`x` го маркира със ❤",
|
||||
"Current version: ": "Текуща версия: ",
|
||||
"channel_tab_community_label": "Общност",
|
||||
"preferences_quality_dash_option_1440p": "1440p",
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
"`x` uploaded a video": "`x` качи видео",
|
||||
"Welsh": "Уелски",
|
||||
"search_message_no_results": "Няма намерени резултати.",
|
||||
"channel_tab_releases_label": "Версии",
|
||||
"Bangla": "Бенгалски",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"Indonesian": "Индонезийски",
|
||||
"`x` ago": "преди `x`",
|
||||
"Invidious Private Feed for `x`": "Invidious персонални видеа за `x`",
|
||||
"Finnish": "Финландски",
|
||||
"Amharic": "Амхарски",
|
||||
"Malay": "Малайски",
|
||||
"Interlingue": "Интерлинг",
|
||||
"search_filters_date_option_month": "Този месец",
|
||||
"Georgian": "Грузински",
|
||||
"Xhosa": "Кхоса",
|
||||
"Marathi": "Маратхи",
|
||||
"Yoruba": "Йоруба",
|
||||
"Song: ": "Музика: ",
|
||||
"Scottish Gaelic": "Шотландски гелски",
|
||||
"search_filters_features_label": "Функции",
|
||||
"preferences_quality_label": "Предпочитано качество на видеото: ",
|
||||
"generic_channels_count": "{{count}} канал",
|
||||
"generic_channels_count_plural": "{{count}} канала",
|
||||
"Croatian": "Хърватски",
|
||||
"Thai": "Тайски",
|
||||
"Chinese (Taiwan)": "Китайски (Тайван)",
|
||||
"youtube": "YouTube",
|
||||
"Source available here.": "Източник наличен тук.",
|
||||
"LIVE": "На живо",
|
||||
"Ukrainian": "Украински",
|
||||
"Russian": "Руски",
|
||||
"Tajik": "Таджикски",
|
||||
"Token manager": "Управляване на токени",
|
||||
"preferences_quality_dash_label": "Предпочитано DASH качество на видеото: ",
|
||||
"adminprefs_modified_source_code_url_label": "URL до хранилището на променения изходен код",
|
||||
"Japanese": "Японски",
|
||||
"Title": "Заглавие",
|
||||
"Authorize token for `x`?": "Разреши токена за `x`?",
|
||||
"reddit": "Reddit",
|
||||
"permalink": "постоянна връзка",
|
||||
"Trending": "На върха",
|
||||
"Turkish (auto-generated)": "Турски (автоматично генерирано)",
|
||||
"Bulgarian": "Български",
|
||||
"Indonesian (auto-generated)": "Индонезийски (автоматично генерирано)",
|
||||
"Enable web notifications": "Активирай уеб известия",
|
||||
"Western Frisian": "Западен фризски",
|
||||
"search_filters_date_option_week": "Тази седмица",
|
||||
"Yiddish": "Идиш",
|
||||
"preferences_category_player": "Предпочитания за плейъра",
|
||||
"Shared `x` ago": "Споделено преди `x`",
|
||||
"Swahili": "Суахили",
|
||||
"Portuguese (auto-generated)": "Португалски (автоматично генерирано)",
|
||||
"generic_count_years": "{{count}} година",
|
||||
"generic_count_years_plural": "{{count}} години",
|
||||
"Wilson score: ": "Wilson оценка: ",
|
||||
"Genre: ": "Жанр: ",
|
||||
"videoinfo_invidious_embed_link": "Вграждане на линк",
|
||||
"Popular enabled: ": "Активиране на популярната страница: ",
|
||||
"Wrong username or password": "Грешно потребителско име или парола",
|
||||
"Vietnamese": "Виетнамски",
|
||||
"alphabetically": "по азбучен ред",
|
||||
"Afrikaans": "Африкаанс",
|
||||
"Zulu": "Зулуски",
|
||||
"(edited)": "(редактирано)",
|
||||
"Whitelisted regions: ": "Разрешени региони: ",
|
||||
"Spanish (auto-generated)": "Испански (автоматично генерирано)",
|
||||
"Could not fetch comments": "Получаването на коментарите е неуспешно",
|
||||
"Sindhi": "Синдхи",
|
||||
"News": "Новини",
|
||||
"preferences_video_loop_label": "Винаги повтаряй: ",
|
||||
"%A %B %-d, %Y": "%-d %B %Y, %A",
|
||||
"preferences_quality_option_small": "Ниско",
|
||||
"English (United Kingdom)": "Английски (Великобритания)",
|
||||
"Rating: ": "Рейтинг: ",
|
||||
"channel_tab_playlists_label": "Плейлисти",
|
||||
"generic_button_edit": "Редактирай",
|
||||
"Report statistics: ": "Активиране на статистики за репортиране: ",
|
||||
"Cebuano": "Себуано",
|
||||
"Chinese (Traditional)": "Китайски (Традиционен)",
|
||||
"generic_playlists_count": "{{count}} плейлист",
|
||||
"generic_playlists_count_plural": "{{count}} плейлиста",
|
||||
"Import NewPipe subscriptions (.json)": "Импортиране на NewPipe абонаменти (.json)",
|
||||
"Preferences": "Предпочитания",
|
||||
"Subscribe": "Абониране",
|
||||
"Import and Export Data": "Импортиране и експортиране на информация",
|
||||
"preferences_quality_option_hd720": "HD720",
|
||||
"search_filters_type_option_playlist": "Плейлист",
|
||||
"Serbian": "Сръбски",
|
||||
"Kazakh": "Казахски",
|
||||
"Telugu": "Телугу",
|
||||
"search_filters_features_option_purchased": "Купено",
|
||||
"revoke": "отмяна",
|
||||
"search_filters_sort_option_date": "Дата на качване",
|
||||
"preferences_category_data": "Предпочитания за информацията",
|
||||
"search_filters_date_option_none": "Всякаква дата",
|
||||
"Log out": "Излизане",
|
||||
"Search": "Търсене",
|
||||
"preferences_quality_dash_option_auto": "Автоматично",
|
||||
"dark": "тъмна",
|
||||
"Cantonese (Hong Kong)": "Кантонски (Хонг Конг)",
|
||||
"crash_page_report_issue": "Ако никои от горепосочените не помогнаха, моля <a href=\"`x`\">отворете нов проблем в GitHub</a> (предпочитано на Английски) и добавете следния текст в съобщението (НЕ превеждайте този текст):",
|
||||
"Czech": "Чешки",
|
||||
"crash_page_switch_instance": "пробвал да <a href=\"`x`\">ползваш друга инстанция</a>",
|
||||
"generic_count_weeks": "{{count}} седмица",
|
||||
"generic_count_weeks_plural": "{{count}} седмици",
|
||||
"search_filters_features_option_subtitles": "Субтитри",
|
||||
"videoinfo_watch_on_youTube": "Виж в YouTube",
|
||||
"Portuguese": "Португалски",
|
||||
"Music in this video": "Музика в това видео",
|
||||
"Hide replies": "Скрий отговорите",
|
||||
"Password cannot be longer than 55 characters": "Паролата не може да бъде по-дълга от 55 символа",
|
||||
"footer_modfied_source_code": "Променен изходен код",
|
||||
"Bosnian": "Босненски",
|
||||
"Deleted or invalid channel": "Изтрит или невалиден канал",
|
||||
"Popular": "Популярно",
|
||||
"search_filters_type_label": "Тип",
|
||||
"preferences_locale_label": "Език: ",
|
||||
"Playlists": "Плейлисти",
|
||||
"generic_button_rss": "RSS",
|
||||
"Export": "Експортиране",
|
||||
"preferences_quality_dash_option_4320p": "4320p",
|
||||
"Erroneous challenge": "Невалиден тест",
|
||||
"History": "История",
|
||||
"generic_count_hours": "{{count}} час",
|
||||
"generic_count_hours_plural": "{{count}} часа",
|
||||
"Registration enabled: ": "Активиране на регистрация: ",
|
||||
"Music": "Музика",
|
||||
"Incorrect password": "Грешна парола",
|
||||
"Persian": "Перскийски",
|
||||
"Import": "Импортиране",
|
||||
"Import/export data": "Импортиране/Експортиране на информация",
|
||||
"Shared `x`": "Споделено `x`",
|
||||
"Javanese": "Явански",
|
||||
"French (auto-generated)": "Френски (автоматично генерирано)",
|
||||
"Norwegian Bokmål": "Норвежки",
|
||||
"Catalan": "Каталунски",
|
||||
"Hindi": "Хинди",
|
||||
"Tamil": "Тамилски",
|
||||
"search_filters_features_option_live": "На живо",
|
||||
"crash_page_read_the_faq": "прочел <a href=\"`x`\">Често задавани въпроси (FAQ)</a>",
|
||||
"preferences_default_home_label": "Начална страница по подразбиране: ",
|
||||
"Download": "Изтегляне",
|
||||
"Show less": "Покажи по-малко",
|
||||
"Password": "Парола",
|
||||
"User ID": "Потребителско име",
|
||||
"Subscription manager": "Управляване на абонаменти",
|
||||
"search": "търсене",
|
||||
"No such user": "Няма такъв потребител",
|
||||
"View privacy policy.": "Виж политиката за поверителност.",
|
||||
"Only show latest unwatched video from channel: ": "Показвай само най-новите негледани видеа в канала: ",
|
||||
"user_created_playlists": "`x` създаде плейлисти",
|
||||
"Editing playlist `x`": "Редактиране на плейлист `x`",
|
||||
"preferences_thin_mode_label": "Тънък режим: ",
|
||||
"E-mail": "Имейл",
|
||||
"Haitian Creole": "Хаитянски креол",
|
||||
"Irish": "Ирландски",
|
||||
"channel_tab_channels_label": "Канали",
|
||||
"Delete account?": "Изтрий акаунта?",
|
||||
"Redirect homepage to feed: ": "Препращане на началната страница до препоръки ",
|
||||
"Urdu": "Урду",
|
||||
"preferences_vr_mode_label": "Интерактивни 360 градусови видеа (изисква WebGL): ",
|
||||
"Password cannot be empty": "Паролата не може да бъде празна",
|
||||
"Mongolian": "Монголски",
|
||||
"Authorize token?": "Разреши токена?",
|
||||
"search_filters_type_option_all": "Всякакъв тип",
|
||||
"Romanian": "Румънски",
|
||||
"Belarusian": "Беларуски",
|
||||
"channel name - reverse": "име на канал - в обратен ред",
|
||||
"Erroneous CAPTCHA": "Невалидна CAPTCHA",
|
||||
"Watch on YouTube": "Гледай в YouTube",
|
||||
"search_filters_features_option_location": "Местоположение",
|
||||
"Could not pull trending pages.": "Получаването на трендинг страниците е неуспешно.",
|
||||
"German": "Немски",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"Family friendly? ": "За всяка възраст? ",
|
||||
"Hidden field \"token\" is a required field": "Скритото поле \"токен\" е задължително поле",
|
||||
"Russian (auto-generated)": "Руски (автоматично генерирано)",
|
||||
"preferences_quality_dash_option_480p": "480p",
|
||||
"Corsican": "Корсикански",
|
||||
"Macedonian": "Македонски",
|
||||
"comments_view_x_replies": "Виж {{count}} отговор",
|
||||
"comments_view_x_replies_plural": "Виж {{count}} отговора",
|
||||
"footer_original_source_code": "Оригинален изходен код",
|
||||
"Import YouTube subscriptions": "Импортиране на YouTube/OPML абонаменти",
|
||||
"Lithuanian": "Литовски",
|
||||
"Nyanja": "Нянджа",
|
||||
"Updated `x` ago": "Актуализирано преди `x`",
|
||||
"JavaScript license information": "Информация за Javascript лиценза",
|
||||
"Spanish": "Испански",
|
||||
"Latin": "Латински",
|
||||
"Shona": "Шона",
|
||||
"Portuguese (Brazil)": "Португалски (Бразилия)",
|
||||
"Show more": "Покажи още",
|
||||
"Clear watch history?": "Изчисти историята на търсене?",
|
||||
"Manage tokens": "Управление на токени",
|
||||
"Hausa": "Хауса",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"preferences_category_visual": "Визуални предпочитания",
|
||||
"Italian": "Италиански",
|
||||
"preferences_volume_label": "Сила на звука на плейъра: ",
|
||||
"error_video_not_in_playlist": "Заявеното видео не съществува в този плейлист. <a href=\"`x`\">Натиснете тук за началната страница на плейлиста.</a>",
|
||||
"preferences_listen_label": "Само звук по подразбиране: ",
|
||||
"Dutch (auto-generated)": "Холандски (автоматично генерирано)",
|
||||
"preferences_captions_label": "Надписи по подразбиране: ",
|
||||
"generic_count_days": "{{count}} ден",
|
||||
"generic_count_days_plural": "{{count}} дни",
|
||||
"Hawaiian": "Хавайски",
|
||||
"Could not get channel info.": "Получаването на информация за канала е неуспешно.",
|
||||
"View as playlist": "Виж като плейлист",
|
||||
"Vietnamese (auto-generated)": "Виетнамски (автоматично генерирано)",
|
||||
"search_filters_duration_option_none": "Всякаква продължителност",
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"Latvian": "Латвийски",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"preferences_sort_label": "Сортирай видеата по: ",
|
||||
"Estonian": "Естонски",
|
||||
"Hidden field \"challenge\" is a required field": "Скритото поле \"тест\" е задължително поле",
|
||||
"footer_documentation": "Документация",
|
||||
"Kyrgyz": "Киргизски",
|
||||
"preferences_continue_autoplay_label": "Пускай следващотото видео автоматично: ",
|
||||
"Chinese": "Китайски",
|
||||
"search_filters_sort_option_relevance": "Уместност",
|
||||
"source": "източник",
|
||||
"Fallback comments: ": "Резервни коментари: ",
|
||||
"preferences_automatic_instance_redirect_label": "Автоматично препращане на инстанция (чрез redirect.invidious.io): ",
|
||||
"Maori": "Маори",
|
||||
"generic_button_delete": "Изтрий",
|
||||
"Import YouTube playlist (.csv)": "Импортиране на YouTube плейлист (.csv)",
|
||||
"Switch Invidious Instance": "Смени Invidious инстанция",
|
||||
"channel name": "име на канал",
|
||||
"Audio mode": "Аудио режим",
|
||||
"search_filters_type_option_show": "Сериал",
|
||||
"search_filters_date_option_today": "Днес",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"next_steps_error_message": "След което можеш да пробваш да: ",
|
||||
"Hide annotations": "Скрий анотации",
|
||||
"Standard YouTube license": "Стандартен YouTube лиценз",
|
||||
"Text CAPTCHA": "Текст CAPTCHA",
|
||||
"Log in/register": "Вход/регистрация",
|
||||
"Punjabi": "Пенджаби",
|
||||
"Change password": "Смяна на паролата",
|
||||
"License: ": "Лиценз: ",
|
||||
"search_filters_duration_option_medium": "Средно (4 - 20 минути)",
|
||||
"Delete playlist": "Изтриване на плейлист",
|
||||
"Delete playlist `x`?": "Изтрий плейлиста `x`?",
|
||||
"Korean": "Корейски",
|
||||
"Export subscriptions as OPML": "Експортиране на абонаментите като OPML",
|
||||
"unsubscribe": "отписване",
|
||||
"View YouTube comments": "Виж YouTube коментарите",
|
||||
"Kannada": "Каннада",
|
||||
"Not a playlist.": "Невалиден плейлист.",
|
||||
"Wrong answer": "Грешен отговор",
|
||||
"Released under the AGPLv3 on Github.": "Публикувано под AGPLv3 в GitHub.",
|
||||
"Burmese": "Бирмански",
|
||||
"Sundanese": "Сундански",
|
||||
"Hungarian": "Унгарски",
|
||||
"generic_count_seconds": "{{count}} секунда",
|
||||
"generic_count_seconds_plural": "{{count}} секунди",
|
||||
"search_filters_date_label": "Дата на качване",
|
||||
"Greek": "Гръцки",
|
||||
"crash_page_you_found_a_bug": "Изглежда намери бъг в Invidious!",
|
||||
"View all playlists": "Виж всички плейлисти",
|
||||
"Khmer": "Кхмерски",
|
||||
"preferences_annotations_label": "Покажи анотаций по подразбиране: ",
|
||||
"generic_views_count": "{{count}} гледане",
|
||||
"generic_views_count_plural": "{{count}} гледания",
|
||||
"Next page": "Следваща страница",
|
||||
"Import YouTube watch history (.json)": "Импортиране на историята на гледане от YouTube (.json)"
|
||||
}
|
|
@ -476,5 +476,16 @@
|
|||
"Redirect homepage to feed: ": "Redirigeix la pàgina d'inici al feed: ",
|
||||
"Standard YouTube license": "Llicència estàndard de YouTube",
|
||||
"Download is disabled": "Les baixades s'han inhabilitat",
|
||||
"Import YouTube playlist (.csv)": "Importar llista de reproducció de YouTube (.csv)"
|
||||
"Import YouTube playlist (.csv)": "Importar llista de reproducció de YouTube (.csv)",
|
||||
"channel_tab_podcasts_label": "Podcasts",
|
||||
"playlist_button_add_items": "Afegeix vídeos",
|
||||
"generic_button_save": "Desa",
|
||||
"generic_button_cancel": "Cancel·la",
|
||||
"channel_tab_releases_label": "Publicacions",
|
||||
"generic_channels_count": "{{count}} canal",
|
||||
"generic_channels_count_plural": "{{count}} canals",
|
||||
"generic_button_edit": "Edita",
|
||||
"generic_button_rss": "RSS",
|
||||
"generic_button_delete": "Suprimeix",
|
||||
"Import YouTube watch history (.json)": "Importa l'historial de visualitzacions de YouTube (.json)"
|
||||
}
|
||||
|
|
|
@ -500,5 +500,10 @@
|
|||
"channel_tab_releases_label": "Vydání",
|
||||
"generic_button_edit": "Upravit",
|
||||
"generic_button_rss": "RSS",
|
||||
"playlist_button_add_items": "Přidat videa"
|
||||
"playlist_button_add_items": "Přidat videa",
|
||||
"generic_channels_count_0": "{{count}} kanál",
|
||||
"generic_channels_count_1": "{{count}} kanály",
|
||||
"generic_channels_count_2": "{{count}} kanálů",
|
||||
"Import YouTube watch history (.json)": "Importovat historii sledování z YouTube (.json)",
|
||||
"toggle_theme": "Přepnout motiv"
|
||||
}
|
||||
|
|
|
@ -452,5 +452,40 @@
|
|||
"crash_page_you_found_a_bug": "Det ser ud til, at du har fundet en fejl i Invidious!",
|
||||
"crash_page_read_the_faq": "læs <a href=\"`x`\">Ofte stillede spørgsmål (FAQ)</a>",
|
||||
"crash_page_search_issue": "søgte efter <a href=\"`x`\">eksisterende problemer på GitHub</a>",
|
||||
"search_filters_title": "Filter"
|
||||
"search_filters_title": "Filter",
|
||||
"playlist_button_add_items": "Tilføj videoer",
|
||||
"search_message_no_results": "Ingen resultater fundet.",
|
||||
"Import YouTube watch history (.json)": "Importer YouTube afspilningshistorik (.json)",
|
||||
"search_message_change_filters_or_query": "Prøv at udvide din søgeforspørgsel og/eller ændre filtrene.",
|
||||
"search_message_use_another_instance": " Du kan også <a href=\"`x`\">søge på en anden instans</a>.",
|
||||
"Music in this video": "Musik i denne video",
|
||||
"search_filters_date_option_none": "Enhver dato",
|
||||
"search_filters_type_option_all": "Enhver type",
|
||||
"search_filters_duration_option_none": "Enhver varighed",
|
||||
"search_filters_duration_option_medium": "Medium (4 - 20 minutter)",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"generic_channels_count": "{{count}} kanal",
|
||||
"generic_channels_count_plural": "{{count}} kanaler",
|
||||
"Import YouTube playlist (.csv)": "Importer YouTube playliste (.csv)",
|
||||
"Standard YouTube license": "Standard Youtube-licens",
|
||||
"Album: ": "Album: ",
|
||||
"Channel Sponsor": "Kanal-sponsor",
|
||||
"Song: ": "Sang: ",
|
||||
"channel_tab_playlists_label": "Playlister",
|
||||
"channel_tab_channels_label": "Kanaler",
|
||||
"Artist: ": "Kunstner: ",
|
||||
"search_filters_date_label": "Uploaddato",
|
||||
"generic_button_delete": "Slet",
|
||||
"generic_button_edit": "Rediger",
|
||||
"generic_button_save": "Gem",
|
||||
"generic_button_cancel": "Afbryd",
|
||||
"generic_button_rss": "RSS",
|
||||
"Popular enabled: ": "Populær aktiveret: ",
|
||||
"search_filters_apply_button": "Anvend udvalgte filtre",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"channel_tab_streams_label": "Livestreams",
|
||||
"channel_tab_podcasts_label": "Podcasts",
|
||||
"channel_tab_releases_label": "Udgivelser",
|
||||
"Download is disabled": "Download er slået fra",
|
||||
"error_video_not_in_playlist": "Den ønskede video findes ikke i denne playliste. <a href=\"`x`\">Klik her for playlistens startside.</a>"
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
"Change password": "Passwort ändern",
|
||||
"Manage subscriptions": "Abonnements verwalten",
|
||||
"Manage tokens": "Tokens verwalten",
|
||||
"Watch history": "Verlauf",
|
||||
"Watch history": "Wiedergabeverlauf",
|
||||
"Delete account": "Account löschen",
|
||||
"preferences_category_admin": "Administrator-Einstellungen",
|
||||
"preferences_default_home_label": "Standard-Startseite: ",
|
||||
|
@ -149,7 +149,7 @@
|
|||
"Whitelisted regions: ": "Erlaubte Regionen: ",
|
||||
"Blacklisted regions: ": "Unerlaubte Regionen: ",
|
||||
"Shared `x`": "Geteilt `x`",
|
||||
"Premieres in `x`": "Zuerst gesehen in `x`",
|
||||
"Premieres in `x`": "Premiere in `x`",
|
||||
"Premieres `x`": "Erster Start `x`",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Hallo! Anscheinend haben Sie JavaScript deaktiviert. Klicken Sie hier um Kommentare anzuzeigen, beachten sie dass es etwas länger dauern kann um sie zu laden.",
|
||||
"View YouTube comments": "YouTube Kommentare anzeigen",
|
||||
|
@ -477,11 +477,16 @@
|
|||
"Standard YouTube license": "Standard YouTube-Lizenz",
|
||||
"Song: ": "Musik: ",
|
||||
"Download is disabled": "Herunterladen ist deaktiviert",
|
||||
"Import YouTube playlist (.csv)": "YouTube Playlist Importieren (.csv)",
|
||||
"Import YouTube playlist (.csv)": "YouTube Wiedergabeliste importieren (.csv)",
|
||||
"generic_button_delete": "Löschen",
|
||||
"generic_button_edit": "Bearbeiten",
|
||||
"generic_button_save": "Speichern",
|
||||
"generic_button_cancel": "Abbrechen",
|
||||
"generic_button_rss": "RSS",
|
||||
"playlist_button_add_items": "Videos hinzufügen"
|
||||
"playlist_button_add_items": "Videos hinzufügen",
|
||||
"channel_tab_podcasts_label": "Podcasts",
|
||||
"channel_tab_releases_label": "Veröffentlichungen",
|
||||
"generic_channels_count": "{{count}} Kanal",
|
||||
"generic_channels_count_plural": "{{count}} Kanäle",
|
||||
"Import YouTube watch history (.json)": "YouTube Wiedergabeverlauf importieren (.json)"
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"Time (h:mm:ss):": "Ώρα (ω:λλ:δδ):",
|
||||
"Text CAPTCHA": "Κείμενο CAPTCHA",
|
||||
"Image CAPTCHA": "Εικόνα CAPTCHA",
|
||||
"Sign In": "Σύνδεση",
|
||||
"Sign In": "Εγγραφή",
|
||||
"Register": "Εγγραφή",
|
||||
"E-mail": "Ηλεκτρονικό ταχυδρομείο",
|
||||
"Preferences": "Προτιμήσεις",
|
||||
|
@ -145,7 +145,7 @@
|
|||
"View YouTube comments": "Προβολή σχολίων από το YouTube",
|
||||
"View more comments on Reddit": "Προβολή περισσότερων σχολίων στο Reddit",
|
||||
"View `x` comments": {
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Προβολή `x` σχολίων",
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Προβολή `x` σχολίου",
|
||||
"": "Προβολή `x` σχολίων"
|
||||
},
|
||||
"View Reddit comments": "Προβολή σχολίων από το Reddit",
|
||||
|
@ -349,7 +349,7 @@
|
|||
"crash_page_you_found_a_bug": "Φαίνεται ότι βρήκατε ένα σφάλμα στο Invidious!",
|
||||
"crash_page_before_reporting": "Πριν αναφέρετε ένα σφάλμα, βεβαιωθείτε ότι έχετε:",
|
||||
"crash_page_refresh": "προσπαθήσει να <a href=\"`x`\">ανανεώσετε τη σελίδα</a>",
|
||||
"crash_page_read_the_faq": "διαβάσει τις <a href=\"`x`\">Συχνές Ερωτήσεις (ΣΕ)</a>",
|
||||
"crash_page_read_the_faq": "διαβάστε τις <a href=\"`x`\">Συχνές Ερωτήσεις (ΣΕ)</a>",
|
||||
"crash_page_search_issue": "αναζητήσει για <a href=\"`x`\">υπάρχοντα θέματα στο GitHub</a>",
|
||||
"generic_views_count": "{{count}} προβολή",
|
||||
"generic_views_count_plural": "{{count}} προβολές",
|
||||
|
@ -442,5 +442,49 @@
|
|||
"search_filters_type_option_show": "Μπάρα προόδου διαβάσματος",
|
||||
"preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: ",
|
||||
"search_filters_title": "Φίλτρο",
|
||||
"search_message_no_results": "Δε βρέθηκαν αποτελέσματα."
|
||||
"search_message_no_results": "Δε βρέθηκαν αποτελέσματα.",
|
||||
"channel_tab_podcasts_label": "Podcast",
|
||||
"preferences_save_player_pos_label": "Αποθήκευση σημείου αναπαραγωγής: ",
|
||||
"search_filters_apply_button": "Εφαρμογή επιλεγμένων φίλτρων",
|
||||
"Download is disabled": "Είναι απενεργοποιημένη η λήψη",
|
||||
"comments_points_count": "{{count}} βαθμός",
|
||||
"comments_points_count_plural": "{{count}} βαθμοί",
|
||||
"search_filters_sort_option_views": "Προβολές",
|
||||
"search_message_change_filters_or_query": "Προσπαθήστε να διευρύνετε το ερώτημα αναζήτησης ή/και να αλλάξετε τα φίλτρα.",
|
||||
"Channel Sponsor": "Χορηγός Καναλιού",
|
||||
"channel_tab_streams_label": "Ζωντανή μετάδοση",
|
||||
"playlist_button_add_items": "Προσθήκη βίντεο",
|
||||
"Artist: ": "Καλλιτέχνης: ",
|
||||
"search_message_use_another_instance": " Μπορείτε επίσης <a href=\"`x`\">να αναζητήσετε σε άλλο instance</a>.",
|
||||
"generic_button_save": "Αποθήκευση",
|
||||
"generic_button_cancel": "Ακύρωση",
|
||||
"subscriptions_unseen_notifs_count": "{{count}} μη αναγνωσμένη ειδοποίηση",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}} μη αναγνωσμένες ειδοποιήσεις",
|
||||
"Album: ": "Δίσκος: ",
|
||||
"tokens_count": "{{count}} σύμβολο",
|
||||
"tokens_count_plural": "{{count}} σύμβολα",
|
||||
"channel_tab_shorts_label": "Short",
|
||||
"channel_tab_releases_label": "Κυκλοφορίες",
|
||||
"Song: ": "Τραγούδι: ",
|
||||
"generic_channels_count": "{{count}} κανάλι",
|
||||
"generic_channels_count_plural": "{{count}} κανάλια",
|
||||
"Popular enabled: ": "Ενεργοποιημένα Δημοφιλή: ",
|
||||
"channel_tab_playlists_label": "Λίστες αναπαραγωγής",
|
||||
"generic_button_edit": "Επεξεργασία",
|
||||
"search_filters_date_option_none": "Οποιαδήποτε ημερομηνία",
|
||||
"crash_page_switch_instance": "προσπάθεια <a href=\"`x`\">χρήσης άλλου instance</a>",
|
||||
"Music in this video": "Μουσική σε αυτό το βίντεο",
|
||||
"generic_button_rss": "RSS",
|
||||
"channel_tab_channels_label": "Κανάλια",
|
||||
"search_filters_type_option_all": "Οποιοσδήποτε τύπος",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"error_video_not_in_playlist": "Το αιτούμενο βίντεο δεν υπάρχει στη δεδομένη λίστα αναπαραγωγής. <a href=\"`x`\">Πατήστε εδώ για επιστροφή στη κεντρική σελίδα λιστών αναπαραγωγής.</a>",
|
||||
"search_filters_duration_option_none": "Οποιαδήποτε διάρκεια",
|
||||
"preferences_automatic_instance_redirect_label": "Αυτόματη ανακατεύθυνση instance (εναλλακτική σε redirect.invidious.io): ",
|
||||
"generic_button_delete": "Διαγραφή",
|
||||
"Import YouTube playlist (.csv)": "Εισαγωγή λίστας αναπαραγωγής YouTube (.csv)",
|
||||
"Switch Invidious Instance": "Αλλαγή Instance Invidious",
|
||||
"Standard YouTube license": "Τυπική άδεια YouTube",
|
||||
"search_filters_duration_option_medium": "Μεσαία (4 - 20 λεπτά)",
|
||||
"search_filters_date_label": "Ημερομηνία αναφόρτωσης"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"Add to playlist": "Add to playlist",
|
||||
"Add to playlist: ": "Add to playlist: ",
|
||||
"Answer": "Answer",
|
||||
"Search for videos": "Search for videos",
|
||||
"The Popular feed has been disabled by the administrator.": "The Popular feed has been disabled by the administrator.",
|
||||
"generic_channels_count": "{{count}} channel",
|
||||
"generic_channels_count_plural": "{{count}} channels",
|
||||
"generic_views_count": "{{count}} view",
|
||||
|
@ -41,6 +46,7 @@
|
|||
"Import Invidious data": "Import Invidious JSON data",
|
||||
"Import YouTube subscriptions": "Import YouTube/OPML subscriptions",
|
||||
"Import YouTube playlist (.csv)": "Import YouTube playlist (.csv)",
|
||||
"Import YouTube watch history (.json)": "Import YouTube watch history (.json)",
|
||||
"Import FreeTube subscriptions (.db)": "Import FreeTube subscriptions (.db)",
|
||||
"Import NewPipe subscriptions (.json)": "Import NewPipe subscriptions (.json)",
|
||||
"Import NewPipe data (.zip)": "Import NewPipe data (.zip)",
|
||||
|
@ -487,5 +493,9 @@
|
|||
"channel_tab_releases_label": "Releases",
|
||||
"channel_tab_playlists_label": "Playlists",
|
||||
"channel_tab_community_label": "Community",
|
||||
"channel_tab_channels_label": "Channels"
|
||||
"channel_tab_channels_label": "Channels",
|
||||
"toggle_theme": "Toggle Theme",
|
||||
"carousel_slide": "Slide {{current}} of {{total}}",
|
||||
"carousel_skip": "Skip the Carousel",
|
||||
"carousel_go_to": "Go to slide `x`"
|
||||
}
|
||||
|
|
|
@ -484,5 +484,7 @@
|
|||
"channel_tab_podcasts_label": "Podkastoj",
|
||||
"generic_button_cancel": "Nuligi",
|
||||
"channel_tab_releases_label": "Eldonoj",
|
||||
"generic_button_save": "Konservi"
|
||||
"generic_button_save": "Konservi",
|
||||
"generic_channels_count": "{{count}} kanalo",
|
||||
"generic_channels_count_plural": "{{count}} kanaloj"
|
||||
}
|
||||
|
|
109
locales/es.json
109
locales/es.json
|
@ -90,7 +90,7 @@
|
|||
"preferences_notifications_only_label": "Mostrar solo notificaciones (si hay alguna): ",
|
||||
"Enable web notifications": "Habilitar notificaciones web",
|
||||
"`x` uploaded a video": "`x` subió un video",
|
||||
"`x` is live": "`x` esta en vivo",
|
||||
"`x` is live": "`x` está en directo",
|
||||
"preferences_category_data": "Preferencias de los datos",
|
||||
"Clear watch history": "Borrar el historial de reproducción",
|
||||
"Import/export data": "Importar/Exportar datos",
|
||||
|
@ -102,7 +102,7 @@
|
|||
"preferences_category_admin": "Preferencias de administrador",
|
||||
"preferences_default_home_label": "Página de inicio por defecto: ",
|
||||
"preferences_feed_menu_label": "Menú de fuentes: ",
|
||||
"preferences_show_nick_label": "Mostrar nombre de usuario arriba: ",
|
||||
"preferences_show_nick_label": "Mostrar nombre de usuario encima: ",
|
||||
"Top enabled: ": "¿Habilitar los destacados? ",
|
||||
"CAPTCHA enabled: ": "¿Habilitar los CAPTCHA? ",
|
||||
"Login enabled: ": "¿Habilitar el inicio de sesión? ",
|
||||
|
@ -144,13 +144,13 @@
|
|||
"License: ": "Licencia: ",
|
||||
"Family friendly? ": "¿Filtrar contenidos? ",
|
||||
"Wilson score: ": "Puntuación Wilson: ",
|
||||
"Engagement: ": "Compromiso: ",
|
||||
"Engagement: ": "Retención: ",
|
||||
"Whitelisted regions: ": "Regiones permitidas: ",
|
||||
"Blacklisted regions: ": "Regiones bloqueadas: ",
|
||||
"Shared `x`": "Compartido `x`",
|
||||
"Premieres in `x`": "Se estrena en `x`",
|
||||
"Premieres `x`": "Estrenos `x`",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "¡Hola! Parece que tienes JavaScript desactivado. Haz clic aquí para ver los comentarios, pero tengas en cuenta que pueden tardar un poco más en cargarse.",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "¡Hola! Parece que tienes JavaScript desactivado. Haz clic aquí para ver los comentarios, ten en cuenta que pueden tardar un poco más en cargar.",
|
||||
"View YouTube comments": "Ver los comentarios de YouTube",
|
||||
"View more comments on Reddit": "Ver más comentarios en Reddit",
|
||||
"View `x` comments": {
|
||||
|
@ -312,7 +312,7 @@
|
|||
"Download as: ": "Descargar como: ",
|
||||
"%A %B %-d, %Y": "%A %B %-d, %Y",
|
||||
"(edited)": "(editado)",
|
||||
"YouTube comment permalink": "Enlace permanente de YouTube del comentario",
|
||||
"YouTube comment permalink": "Enlace permanente de comentario de YouTube",
|
||||
"permalink": "enlace permanente",
|
||||
"`x` marked it with a ❤": "`x` lo ha marcado con un ❤",
|
||||
"Audio mode": "Modo de audio",
|
||||
|
@ -324,10 +324,10 @@
|
|||
"search_filters_sort_option_rating": "Valoración",
|
||||
"search_filters_sort_option_date": "Fecha de subida",
|
||||
"search_filters_sort_option_views": "Visualizaciones",
|
||||
"search_filters_type_label": "tipo de contenido",
|
||||
"search_filters_duration_label": "duración",
|
||||
"search_filters_features_label": "funcionalidades",
|
||||
"search_filters_sort_label": "ordenar",
|
||||
"search_filters_type_label": "Tipo de contenido",
|
||||
"search_filters_duration_label": "Duración",
|
||||
"search_filters_features_label": "Funcionalidades",
|
||||
"search_filters_sort_label": "Ordenar",
|
||||
"search_filters_date_option_hour": "Última hora",
|
||||
"search_filters_date_option_today": "Hoy",
|
||||
"search_filters_date_option_week": "Esta semana",
|
||||
|
@ -390,43 +390,58 @@
|
|||
"search_filters_features_option_three_sixty": "360°",
|
||||
"videoinfo_watch_on_youTube": "Ver en YouTube",
|
||||
"preferences_save_player_pos_label": "Guardar posición de reproducción: ",
|
||||
"generic_views_count": "{{count}} visualización",
|
||||
"generic_views_count_plural": "{{count}} visualizaciones",
|
||||
"generic_subscribers_count": "{{count}} suscriptor",
|
||||
"generic_subscribers_count_plural": "{{count}} suscriptores",
|
||||
"generic_subscriptions_count": "{{count}} suscripción",
|
||||
"generic_subscriptions_count_plural": "{{count}} suscripciones",
|
||||
"subscriptions_unseen_notifs_count": "{{count}} notificación no vista",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}} notificaciones no vistas",
|
||||
"generic_count_days": "{{count}} día",
|
||||
"generic_count_days_plural": "{{count}} días",
|
||||
"comments_view_x_replies": "Ver {{count}} respuesta",
|
||||
"comments_view_x_replies_plural": "Ver {{count}} respuestas",
|
||||
"generic_count_weeks": "{{count}} semana",
|
||||
"generic_count_weeks_plural": "{{count}} semanas",
|
||||
"generic_playlists_count": "{{count}} lista de reproducción",
|
||||
"generic_playlists_count_plural": "{{count}} listas de reproducciones",
|
||||
"generic_videos_count": "{{count}} video",
|
||||
"generic_videos_count_plural": "{{count}} video",
|
||||
"generic_count_months": "{{count}} mes",
|
||||
"generic_count_months_plural": "{{count}} meses",
|
||||
"comments_points_count": "{{count}} punto",
|
||||
"comments_points_count_plural": "{{count}} puntos",
|
||||
"generic_count_years": "{{count}} año",
|
||||
"generic_count_years_plural": "{{count}} años",
|
||||
"generic_count_hours": "{{count}} hora",
|
||||
"generic_count_hours_plural": "{{count}} horas",
|
||||
"generic_count_minutes": "{{count}} minuto",
|
||||
"generic_count_minutes_plural": "{{count}} minutos",
|
||||
"generic_count_seconds": "{{count}} segundo",
|
||||
"generic_count_seconds_plural": "{{count}} segundos",
|
||||
"generic_views_count_0": "{{count}} visualización",
|
||||
"generic_views_count_1": "{{count}} visualizaciones",
|
||||
"generic_views_count_2": "{{count}} visualizaciones",
|
||||
"generic_subscribers_count_0": "{{count}} suscriptor",
|
||||
"generic_subscribers_count_1": "{{count}} suscriptores",
|
||||
"generic_subscribers_count_2": "{{count}} suscriptores",
|
||||
"generic_subscriptions_count_0": "{{count}} suscripción",
|
||||
"generic_subscriptions_count_1": "{{count}} suscripciones",
|
||||
"generic_subscriptions_count_2": "{{count}} suscripciones",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} notificación sin ver",
|
||||
"subscriptions_unseen_notifs_count_1": "{{count}} notificaciones sin ver",
|
||||
"subscriptions_unseen_notifs_count_2": "{{count}} notificaciones sin ver",
|
||||
"generic_count_days_0": "{{count}} día",
|
||||
"generic_count_days_1": "{{count}} días",
|
||||
"generic_count_days_2": "{{count}} días",
|
||||
"comments_view_x_replies_0": "Ver {{count}} respuesta",
|
||||
"comments_view_x_replies_1": "Ver {{count}} respuestas",
|
||||
"comments_view_x_replies_2": "Ver {{count}} respuestas",
|
||||
"generic_count_weeks_0": "{{count}} semana",
|
||||
"generic_count_weeks_1": "{{count}} semanas",
|
||||
"generic_count_weeks_2": "{{count}} semanas",
|
||||
"generic_playlists_count_0": "{{count}} lista de reproducción",
|
||||
"generic_playlists_count_1": "{{count}} listas de reproducciones",
|
||||
"generic_playlists_count_2": "{{count}} listas de reproducciones",
|
||||
"generic_videos_count_0": "{{count}} video",
|
||||
"generic_videos_count_1": "{{count}} videos",
|
||||
"generic_videos_count_2": "{{count}} videos",
|
||||
"generic_count_months_0": "{{count}} mes",
|
||||
"generic_count_months_1": "{{count}} meses",
|
||||
"generic_count_months_2": "{{count}} meses",
|
||||
"comments_points_count_0": "{{count}} punto",
|
||||
"comments_points_count_1": "{{count}} puntos",
|
||||
"comments_points_count_2": "{{count}} puntos",
|
||||
"generic_count_years_0": "{{count}} año",
|
||||
"generic_count_years_1": "{{count}} años",
|
||||
"generic_count_years_2": "{{count}} años",
|
||||
"generic_count_hours_0": "{{count}} hora",
|
||||
"generic_count_hours_1": "{{count}} horas",
|
||||
"generic_count_hours_2": "{{count}} horas",
|
||||
"generic_count_minutes_0": "{{count}} minuto",
|
||||
"generic_count_minutes_1": "{{count}} minutos",
|
||||
"generic_count_minutes_2": "{{count}} minutos",
|
||||
"generic_count_seconds_0": "{{count}} segundo",
|
||||
"generic_count_seconds_1": "{{count}} segundos",
|
||||
"generic_count_seconds_2": "{{count}} segundos",
|
||||
"crash_page_before_reporting": "Antes de notificar un error asegúrate de que has:",
|
||||
"crash_page_switch_instance": "probado a <a href=\"`x`\">usar otra instancia</a>",
|
||||
"crash_page_read_the_faq": "leído las <a href=\"`x`\">Preguntas Frecuentes</a>",
|
||||
"crash_page_search_issue": "buscado <a href=\"`x`\">problemas existentes en GitHub</a>",
|
||||
"crash_page_you_found_a_bug": "¡Parece que has encontrado un error en Invidious!",
|
||||
"crash_page_refresh": "probado a <a href=\"`x`\">recargar la página</a>",
|
||||
"crash_page_report_issue": "Si nada de lo anterior ha sido de ayuda, por favor, <a href=\"`x`\">abre una nueva incidencia en GitHub</a> (preferiblemente en inglés) e incluye verbatim el siguiente texto en tu mensaje:",
|
||||
"crash_page_report_issue": "Si nada de lo anterior ha sido de ayuda, por favor, <a href=\"`x`\">abre una nueva incidencia en GitHub</a> (preferiblemente en inglés) e incluye el siguiente texto en tu mensaje (NO traduzcas este texto):",
|
||||
"English (United States)": "Inglés (Estados Unidos)",
|
||||
"Cantonese (Hong Kong)": "Cantonés (Hong Kong)",
|
||||
"Dutch (auto-generated)": "Neerlandés (generados automáticamente)",
|
||||
|
@ -454,14 +469,15 @@
|
|||
"search_message_no_results": "No se han encontrado resultados.",
|
||||
"search_message_change_filters_or_query": "Pruebe ampliar la consulta de búsqueda y/o a cambiar los filtros.",
|
||||
"search_filters_title": "Filtros",
|
||||
"search_filters_date_label": "fecha de subida",
|
||||
"search_filters_date_label": "Fecha de subida",
|
||||
"search_filters_date_option_none": "Cualquier fecha",
|
||||
"search_filters_type_option_all": "Cualquier tipo",
|
||||
"search_filters_duration_option_none": "Cualquier duración",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"search_filters_apply_button": "Aplicar filtros",
|
||||
"tokens_count": "{{count}} token",
|
||||
"tokens_count_plural": "{{count}} tokens",
|
||||
"tokens_count_0": "{{count}} token",
|
||||
"tokens_count_1": "{{count}} tokens",
|
||||
"tokens_count_2": "{{count}} tokens",
|
||||
"search_message_use_another_instance": " También puede <a href=\"`x`\">buscar en otra instancia</a>.",
|
||||
"Popular enabled: ": "¿Habilitar la sección popular? ",
|
||||
"error_video_not_in_playlist": "El video que solicitaste no existe en esta lista de reproducción. <a href=\"`x`\">Haz clic aquí para acceder a la página de inicio de la lista de reproducción.</a>",
|
||||
|
@ -484,5 +500,10 @@
|
|||
"generic_button_cancel": "Cancelar",
|
||||
"generic_button_rss": "RSS",
|
||||
"channel_tab_podcasts_label": "Podcasts",
|
||||
"channel_tab_releases_label": "Publicaciones"
|
||||
"channel_tab_releases_label": "Publicaciones",
|
||||
"generic_channels_count_0": "{{count}} canal",
|
||||
"generic_channels_count_1": "{{count}} canales",
|
||||
"generic_channels_count_2": "{{count}} canales",
|
||||
"Import YouTube watch history (.json)": "Importar el historial de las visualizaciones de YouTube (.json)",
|
||||
"toggle_theme": "Alternar tema"
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
{
|
||||
"generic_views_count_0": "{{count}} بازدید",
|
||||
"generic_videos_count_0": "{{count}} ویدئو",
|
||||
"generic_playlists_count_0": "{{count}} فهرست پخش",
|
||||
"generic_subscribers_count_0": "{{count}} دنبال کننده",
|
||||
"generic_subscriptions_count_0": "{{count}} اشتراک ها",
|
||||
"generic_views_count": "{{count}} بازدید",
|
||||
"generic_views_count_plural": "{{count}} بازدید",
|
||||
"generic_videos_count": "{{count}} ویدئو",
|
||||
"generic_videos_count_plural": "{{count}} ویدئو",
|
||||
"generic_playlists_count": "{{count}} فهرست پخش",
|
||||
"generic_playlists_count_plural": "{{count}} فهرست پخش",
|
||||
"generic_subscribers_count": "{{count}} دنبال کننده",
|
||||
"generic_subscribers_count_plural": "{{count}} دنبال کننده",
|
||||
"generic_subscriptions_count": "{{count}} اشتراک",
|
||||
"generic_subscriptions_count_plural": "{{count}} اشتراک",
|
||||
"LIVE": "زنده",
|
||||
"Shared `x` ago": "`x` پیش به اشتراک گذاشته شده",
|
||||
"Unsubscribe": "لغو اشتراک",
|
||||
|
@ -117,13 +122,15 @@
|
|||
"Subscription manager": "مدیریت اشتراک",
|
||||
"Token manager": "مدیر توکن",
|
||||
"Token": "توکن",
|
||||
"tokens_count_0": "{{count}} توکن ها",
|
||||
"tokens_count": "{{count}} توکن",
|
||||
"tokens_count_plural": "{{count}} توکن",
|
||||
"Import/export": "وارد کردن/خارج کردن",
|
||||
"unsubscribe": "لغو اشتراک",
|
||||
"revoke": "ابطال",
|
||||
"Subscriptions": "اشتراک ها",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} اعلان نادیده",
|
||||
"search": "جستجو",
|
||||
"subscriptions_unseen_notifs_count": "{{count}} اعلان نادیده",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}} اعلان نادیده",
|
||||
"search": "جست و جو",
|
||||
"Log out": "خروج",
|
||||
"Released under the AGPLv3 on Github.": "منتشر شده تحت پروانه AGPLv3 روی گیتهاب.",
|
||||
"Source available here.": "منبع اینجا دردسترس است.",
|
||||
|
@ -183,10 +190,12 @@
|
|||
"This channel does not exist.": "این کانال وجود ندارد.",
|
||||
"Could not get channel info.": "نمیتوان اطلاعات کانال را دریافت کرد.",
|
||||
"Could not fetch comments": "نمیتوان نظرات را دریافت کرد",
|
||||
"comments_view_x_replies_0": "نمایش {{count}} پاسخ ها",
|
||||
"comments_view_x_replies": "نمایش {{count}} پاسخ",
|
||||
"comments_view_x_replies_plural": "نمایش {{count}} پاسخ",
|
||||
"`x` ago": "`x` پیش",
|
||||
"Load more": "بارگذاری بیشتر",
|
||||
"comments_points_count_0": "{{count}} نقطه ها",
|
||||
"comments_points_count": "{{count}} نقطه",
|
||||
"comments_points_count_plural": "{{count}} نقطه",
|
||||
"Could not create mix.": "نمیتوان میکس ساخت.",
|
||||
"Empty playlist": "سیاههٔ پخش خالی",
|
||||
"Not a playlist.": "یک سیاههٔ پخش نیست.",
|
||||
|
@ -304,16 +313,23 @@
|
|||
"Yiddish": "ییدیش",
|
||||
"Yoruba": "یوروبایی",
|
||||
"Zulu": "زولو",
|
||||
"generic_count_years_0": "{{count}} سال",
|
||||
"generic_count_months_0": "{{count}} ماه",
|
||||
"generic_count_weeks_0": "{{count}} هفته",
|
||||
"generic_count_days_0": "{{count}} روز",
|
||||
"generic_count_hours_0": "{{count}} ساعت",
|
||||
"generic_count_minutes_0": "{{count}} دقیقه",
|
||||
"generic_count_seconds_0": "{{count}} ثانیه",
|
||||
"generic_count_years": "{{count}} سال",
|
||||
"generic_count_years_plural": "{{count}} سال",
|
||||
"generic_count_months": "{{count}} ماه",
|
||||
"generic_count_months_plural": "{{count}} ماه",
|
||||
"generic_count_weeks": "{{count}} هفته",
|
||||
"generic_count_weeks_plural": "{{count}} هفته",
|
||||
"generic_count_days": "{{count}} روز",
|
||||
"generic_count_days_plural": "{{count}} روز",
|
||||
"generic_count_hours": "{{count}} ساعت",
|
||||
"generic_count_hours_plural": "{{count}} ساعت",
|
||||
"generic_count_minutes": "{{count}} دقیقه",
|
||||
"generic_count_minutes_plural": "{{count}} دقیقه",
|
||||
"generic_count_seconds": "{{count}} ثانیه",
|
||||
"generic_count_seconds_plural": "{{count}} ثانیه",
|
||||
"Fallback comments: ": "نظرات عقب گرد: ",
|
||||
"Popular": "محبوب",
|
||||
"Search": "جستجو",
|
||||
"Search": "جست و جو",
|
||||
"Top": "بالا",
|
||||
"About": "درباره",
|
||||
"Rating: ": "رتبه دهی: ",
|
||||
|
@ -445,5 +461,28 @@
|
|||
"Song: ": "آهنگ: ",
|
||||
"Channel Sponsor": "اسپانسر کانال",
|
||||
"Standard YouTube license": "پروانه استاندارد YouTube",
|
||||
"search_message_use_another_instance": " شما همچنین میتوانید <a href=\"`x`\">در نمونه دیگر هم جستجو کنید</a>."
|
||||
"search_message_use_another_instance": " شما همچنین میتوانید <a href=\"`x`\">در نمونه دیگر هم جستجو کنید</a>.",
|
||||
"Download is disabled": "دریافت غیرفعال است",
|
||||
"crash_page_before_reporting": "پیش از گزارش ایراد، مطمئنید شوید که:",
|
||||
"playlist_button_add_items": "افزودن ویدیو",
|
||||
"user_saved_playlists": "فهرستهای پخش ذخیره شده",
|
||||
"crash_page_refresh": "که صفحه را <a href=\"`x`\">بازنشانی</a> کردهاید",
|
||||
"generic_button_save": "ذخیره",
|
||||
"generic_button_cancel": "لغو",
|
||||
"generic_channels_count": "{{count}} کانال",
|
||||
"generic_channels_count_plural": "{{count}} کانال",
|
||||
"generic_button_edit": "ویرایش",
|
||||
"crash_page_switch_instance": "که تلاش کردهاید <a href=\"`x`\">از یک نمونهٔ دیگر</a> استفاده کنید",
|
||||
"generic_button_rss": "خوراک RSS",
|
||||
"crash_page_read_the_faq": "که <a href=\"`x`\">سوالات بیشتر پرسیده شده (FAQ)</a> را خواندهاید",
|
||||
"generic_button_delete": "حذف",
|
||||
"Import YouTube playlist (.csv)": "واردکردن فهرستپخش YouTube (.csv)",
|
||||
"Import YouTube watch history (.json)": "وارد کردن فهرست پخش YouTube (.json)",
|
||||
"crash_page_you_found_a_bug": "به نظر میرسد که ایرادی در Invidious پیدا کردهاید!",
|
||||
"channel_tab_podcasts_label": "پادکستها",
|
||||
"channel_tab_streams_label": "پخش زندهها",
|
||||
"channel_tab_shorts_label": "Shortها",
|
||||
"channel_tab_playlists_label": "فهرستهای پخش",
|
||||
"channel_tab_channels_label": "کانالها",
|
||||
"error_video_not_in_playlist": "ویدیوی درخواستی معلق به این فهرست پخش نیست. <a href=\"`x`\">کلیک کنید تا به صفحهٔ اصلی فهرست پخش بروید.</a>"
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"Clear watch history?": "Tyhjennä katseluhistoria?",
|
||||
"New password": "Uusi salasana",
|
||||
"New passwords must match": "Uusien salasanojen täytyy täsmätä",
|
||||
"Authorize token?": "Valuutetaanko tunnus?",
|
||||
"Authorize token?": "Valtuutetaanko tunnus?",
|
||||
"Authorize token for `x`?": "Valtuutetaanko tunnus `x`:lle?",
|
||||
"Yes": "Kyllä",
|
||||
"No": "Ei",
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
{
|
||||
"generic_channels_count": "{{count}} chaîne",
|
||||
"generic_channels_count_plural": "{{count}} chaînes",
|
||||
"generic_views_count": "{{count}} vue",
|
||||
"generic_views_count_plural": "{{count}} vues",
|
||||
"generic_videos_count": "{{count}} vidéo",
|
||||
"generic_videos_count_plural": "{{count}} vidéos",
|
||||
"generic_playlists_count": "{{count}} liste de lecture",
|
||||
"generic_playlists_count_plural": "{{count}} listes de lecture",
|
||||
"generic_subscribers_count": "{{count}} abonné",
|
||||
"generic_subscribers_count_plural": "{{count}} abonnés",
|
||||
"generic_subscriptions_count": "{{count}} abonnement",
|
||||
"generic_subscriptions_count_plural": "{{count}} abonnements",
|
||||
"generic_channels_count_0": "{{count}} chaîne",
|
||||
"generic_channels_count_1": "{{count}} de chaînes",
|
||||
"generic_channels_count_2": "{{count}} chaînes",
|
||||
"generic_views_count_0": "{{count}} vue",
|
||||
"generic_views_count_1": "{{count}} de vues",
|
||||
"generic_views_count_2": "{{count}} vues",
|
||||
"generic_videos_count_0": "{{count}} vidéo",
|
||||
"generic_videos_count_1": "{{count}} de vidéos",
|
||||
"generic_videos_count_2": "{{count}} vidéos",
|
||||
"generic_playlists_count_0": "{{count}} liste de lecture",
|
||||
"generic_playlists_count_1": "{{count}} listes de lecture",
|
||||
"generic_playlists_count_2": "{{count}} listes de lecture",
|
||||
"generic_subscribers_count_0": "{{count}} abonné",
|
||||
"generic_subscribers_count_1": "{{count}} d'abonnés",
|
||||
"generic_subscribers_count_2": "{{count}} abonnés",
|
||||
"generic_subscriptions_count_0": "{{count}} abonnement",
|
||||
"generic_subscriptions_count_1": "{{count}} d'abonnements",
|
||||
"generic_subscriptions_count_2": "{{count}} abonnements",
|
||||
"generic_button_delete": "Supprimer",
|
||||
"generic_button_edit": "Editer",
|
||||
"generic_button_save": "Enregistrer",
|
||||
|
@ -130,14 +136,16 @@
|
|||
"Subscription manager": "Gestionnaire d'abonnement",
|
||||
"Token manager": "Gestionnaire de token",
|
||||
"Token": "Token",
|
||||
"tokens_count": "{{count}} jeton",
|
||||
"tokens_count_plural": "{{count}} jetons",
|
||||
"tokens_count_0": "{{count}} jeton",
|
||||
"tokens_count_1": "{{count}} de jetons",
|
||||
"tokens_count_2": "{{count}} jetons",
|
||||
"Import/export": "Importer/Exporter",
|
||||
"unsubscribe": "se désabonner",
|
||||
"revoke": "révoquer",
|
||||
"Subscriptions": "Abonnements",
|
||||
"subscriptions_unseen_notifs_count": "{{count}} notification non vue",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}} notifications non vues",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} notification non vue",
|
||||
"subscriptions_unseen_notifs_count_1": "{{count}} de notifications non vues",
|
||||
"subscriptions_unseen_notifs_count_2": "{{count}} notifications non vues",
|
||||
"search": "rechercher",
|
||||
"Log out": "Se déconnecter",
|
||||
"Released under the AGPLv3 on Github.": "Publié sous licence AGPLv3 sur GitHub.",
|
||||
|
@ -199,12 +207,14 @@
|
|||
"This channel does not exist.": "Cette chaine n'existe pas.",
|
||||
"Could not get channel info.": "Impossible de charger les informations de cette chaîne.",
|
||||
"Could not fetch comments": "Impossible de charger les commentaires",
|
||||
"comments_view_x_replies": "Voir {{count}} réponse",
|
||||
"comments_view_x_replies_plural": "Voir {{count}} réponses",
|
||||
"comments_view_x_replies_0": "Voir {{count}} réponse",
|
||||
"comments_view_x_replies_1": "Voir {{count}} de réponses",
|
||||
"comments_view_x_replies_2": "Voir {{count}} réponses",
|
||||
"`x` ago": "il y a `x`",
|
||||
"Load more": "Voir plus",
|
||||
"comments_points_count": "{{count}} point",
|
||||
"comments_points_count_plural": "{{count}} points",
|
||||
"comments_points_count_0": "{{count}} point",
|
||||
"comments_points_count_1": "{{count}} de points",
|
||||
"comments_points_count_2": "{{count}} points",
|
||||
"Could not create mix.": "Impossible de charger cette liste de lecture.",
|
||||
"Empty playlist": "La liste de lecture est vide",
|
||||
"Not a playlist.": "La liste de lecture est invalide.",
|
||||
|
@ -322,20 +332,27 @@
|
|||
"Yiddish": "Yiddish",
|
||||
"Yoruba": "Yoruba",
|
||||
"Zulu": "Zoulou",
|
||||
"generic_count_years": "{{count}} an",
|
||||
"generic_count_years_plural": "{{count}} ans",
|
||||
"generic_count_months": "{{count}} mois",
|
||||
"generic_count_months_plural": "{{count}} mois",
|
||||
"generic_count_weeks": "{{count}} semaine",
|
||||
"generic_count_weeks_plural": "{{count}} semaines",
|
||||
"generic_count_days": "{{count}} jour",
|
||||
"generic_count_days_plural": "{{count}} jours",
|
||||
"generic_count_hours": "{{count}} heure",
|
||||
"generic_count_hours_plural": "{{count}} heures",
|
||||
"generic_count_minutes": "{{count}} minute",
|
||||
"generic_count_minutes_plural": "{{count}} minutes",
|
||||
"generic_count_seconds": "{{count}} seconde",
|
||||
"generic_count_seconds_plural": "{{count}} secondes",
|
||||
"generic_count_years_0": "{{count}} an",
|
||||
"generic_count_years_1": "{{count}} ans",
|
||||
"generic_count_years_2": "{{count}} ans",
|
||||
"generic_count_months_0": "{{count}} mois",
|
||||
"generic_count_months_1": "{{count}} mois",
|
||||
"generic_count_months_2": "{{count}} mois",
|
||||
"generic_count_weeks_0": "{{count}} semaine",
|
||||
"generic_count_weeks_1": "{{count}} semaines",
|
||||
"generic_count_weeks_2": "{{count}} semaines",
|
||||
"generic_count_days_0": "{{count}} jour",
|
||||
"generic_count_days_1": "{{count}} jours",
|
||||
"generic_count_days_2": "{{count}} jours",
|
||||
"generic_count_hours_0": "{{count}} heure",
|
||||
"generic_count_hours_1": "{{count}} heures",
|
||||
"generic_count_hours_2": "{{count}} heures",
|
||||
"generic_count_minutes_0": "{{count}} minute",
|
||||
"generic_count_minutes_1": "{{count}} minutes",
|
||||
"generic_count_minutes_2": "{{count}} minutes",
|
||||
"generic_count_seconds_0": "{{count}} seconde",
|
||||
"generic_count_seconds_1": "{{count}} secondes",
|
||||
"generic_count_seconds_2": "{{count}} secondes",
|
||||
"Fallback comments: ": "Commentaires alternatifs : ",
|
||||
"Popular": "Populaire",
|
||||
"Search": "Rechercher",
|
||||
|
@ -486,5 +503,6 @@
|
|||
"Download is disabled": "Le téléchargement est désactivé",
|
||||
"Import YouTube playlist (.csv)": "Importer des listes de lecture de Youtube (.csv)",
|
||||
"channel_tab_releases_label": "Parutions",
|
||||
"channel_tab_podcasts_label": "Émissions audio"
|
||||
"channel_tab_podcasts_label": "Émissions audio",
|
||||
"Import YouTube watch history (.json)": "Importer l'historique de visionnement YouTube (.json)"
|
||||
}
|
||||
|
|
|
@ -476,7 +476,7 @@
|
|||
"generic_button_cancel": "रद्द करें",
|
||||
"generic_button_rss": "आरएसएस",
|
||||
"generic_button_edit": "संपादित करें",
|
||||
"generic_button_delete": "मिटाएं",
|
||||
"generic_button_delete": "हटाएं",
|
||||
"playlist_button_add_items": "वीडियो जोड़ें",
|
||||
"Song: ": "गाना: ",
|
||||
"channel_tab_podcasts_label": "पाॅडकास्ट",
|
||||
|
@ -484,5 +484,8 @@
|
|||
"Import YouTube playlist (.csv)": "YouTube प्लेलिस्ट (.csv) आयात करें",
|
||||
"Standard YouTube license": "मानक यूट्यूब लाइसेंस",
|
||||
"Channel Sponsor": "चैनल प्रायोजक",
|
||||
"Download is disabled": "डाउनलोड करना अक्षम है"
|
||||
"Download is disabled": "डाउनलोड करना अक्षम है",
|
||||
"generic_channels_count": "{{count}} चैनल",
|
||||
"generic_channels_count_plural": "{{count}} चैनल",
|
||||
"Import YouTube watch history (.json)": "YouTube पर देखने का इतिहास आयात करें (.json)"
|
||||
}
|
||||
|
|
|
@ -500,5 +500,9 @@
|
|||
"generic_button_save": "Spremi",
|
||||
"generic_button_cancel": "Odustani",
|
||||
"generic_button_rss": "RSS",
|
||||
"channel_tab_releases_label": "Izdanja"
|
||||
"channel_tab_releases_label": "Izdanja",
|
||||
"generic_channels_count_0": "{{count}} kanal",
|
||||
"generic_channels_count_1": "{{count}} kanala",
|
||||
"generic_channels_count_2": "{{count}} kanala",
|
||||
"Import YouTube watch history (.json)": "Uvezi YouTube povijest gledanja (.json)"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"New password": "Nove contrasigno",
|
||||
"preferences_player_style_label": "Stylo de reproductor: ",
|
||||
"preferences_region_label": "Pais de contento: ",
|
||||
"oldest": "plus ancian",
|
||||
"published": "data de publication",
|
||||
"invidious": "Invidious",
|
||||
"Image CAPTCHA": "Imagine CAPTCHA",
|
||||
"newest": "plus nove",
|
||||
"generic_button_save": "Salvar",
|
||||
"Dark mode: ": "Modo obscur: ",
|
||||
"preferences_dark_mode_label": "Thema: ",
|
||||
"preferences_category_subscription": "Preferentias de subscription",
|
||||
"last": "ultime",
|
||||
"generic_button_cancel": "Cancellar",
|
||||
"popular": "popular",
|
||||
"Time (h:mm:ss):": "Tempore (h:mm:ss):",
|
||||
"preferences_autoplay_label": "Reproduction automatic: ",
|
||||
"Sign In": "Aperir le session",
|
||||
"Log in": "Initiar le session",
|
||||
"preferences_speed_label": "Velocitate per predefinition: ",
|
||||
"preferences_comments_label": "Commentos predefinite: ",
|
||||
"light": "clar",
|
||||
"No": "Non",
|
||||
"youtube": "YouTube",
|
||||
"LIVE": "IN DIRECTE",
|
||||
"reddit": "Reddit",
|
||||
"preferences_category_player": "Preferentias de reproductor",
|
||||
"Preferences": "Preferentias",
|
||||
"preferences_quality_dash_option_auto": "Automatic",
|
||||
"dark": "obscur",
|
||||
"generic_button_rss": "RSS",
|
||||
"Export": "Exportar",
|
||||
"History": "Chronologia",
|
||||
"Password": "Contrasigno",
|
||||
"User ID": "ID de usator",
|
||||
"E-mail": "E-mail",
|
||||
"Delete account?": "Deler conto?",
|
||||
"preferences_volume_label": "Volumine del reproductor: ",
|
||||
"preferences_sort_label": "Ordinar le videos per: "
|
||||
}
|
|
@ -446,5 +446,29 @@
|
|||
"crash_page_read_the_faq": "baca <a href=\"`x`\">Soal Sering Ditanya (SSD/FAQ)</a>",
|
||||
"crash_page_search_issue": "mencari <a href=\"`x`\">isu yang ada di GitHub</a>",
|
||||
"crash_page_report_issue": "Jika yang di atas tidak membantu, <a href=\"`x`\">buka isu baru di GitHub</a> (sebaiknya dalam bahasa Inggris) dan sertakan teks berikut dalam pesan Anda (JANGAN terjemahkan teks tersebut):",
|
||||
"Popular enabled: ": "Populer diaktifkan: "
|
||||
"Popular enabled: ": "Populer diaktifkan: ",
|
||||
"channel_tab_podcasts_label": "Podcast",
|
||||
"Download is disabled": "Download dinonaktifkan",
|
||||
"Channel Sponsor": "Saluran Sponsor",
|
||||
"channel_tab_streams_label": "Streaming langsung",
|
||||
"playlist_button_add_items": "Tambahkan video",
|
||||
"Artist: ": "Artis: ",
|
||||
"generic_button_save": "Simpan",
|
||||
"generic_button_cancel": "Batal",
|
||||
"Album: ": "Album: ",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"channel_tab_releases_label": "Terbit",
|
||||
"Interlingue": "Interlingue",
|
||||
"Song: ": "Lagu: ",
|
||||
"generic_channels_count_0": "Saluran {{count}}",
|
||||
"channel_tab_playlists_label": "Daftar putar",
|
||||
"generic_button_edit": "Ubah",
|
||||
"Music in this video": "Musik dalam video ini",
|
||||
"generic_button_rss": "RSS",
|
||||
"channel_tab_channels_label": "Saluran",
|
||||
"error_video_not_in_playlist": "Video yang diminta tidak ada dalam daftar putar ini. <a href=\"`x`\">Klik di sini untuk halaman beranda daftar putar.</a>",
|
||||
"generic_button_delete": "Hapus",
|
||||
"Import YouTube playlist (.csv)": "Impor daftar putar YouTube (.csv)",
|
||||
"Standard YouTube license": "Lisensi YouTube standar",
|
||||
"Import YouTube watch history (.json)": "Impor riwayat tontonan YouTube (.json)"
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
{
|
||||
"generic_subscribers_count": "{{count}} iscritto",
|
||||
"generic_subscribers_count_plural": "{{count}} iscritti",
|
||||
"generic_videos_count": "{{count}} video",
|
||||
"generic_videos_count_plural": "{{count}} video",
|
||||
"generic_playlists_count": "{{count}} playlist",
|
||||
"generic_playlists_count_plural": "{{count}} playlist",
|
||||
"generic_subscribers_count_0": "{{count}} iscritto",
|
||||
"generic_subscribers_count_1": "{{count}} iscritti",
|
||||
"generic_subscribers_count_2": "{{count}} iscritti",
|
||||
"generic_videos_count_0": "{{count}} video",
|
||||
"generic_videos_count_1": "{{count}} video",
|
||||
"generic_videos_count_2": "{{count}} video",
|
||||
"generic_playlists_count_0": "{{count}} playlist",
|
||||
"generic_playlists_count_1": "{{count}} playlist",
|
||||
"generic_playlists_count_2": "{{count}} playlist",
|
||||
"LIVE": "IN DIRETTA",
|
||||
"Shared `x` ago": "Condiviso `x` fa",
|
||||
"Unsubscribe": "Disiscriviti",
|
||||
|
@ -113,16 +116,19 @@
|
|||
"Subscription manager": "Gestione delle iscrizioni",
|
||||
"Token manager": "Gestione dei gettoni",
|
||||
"Token": "Gettone",
|
||||
"generic_subscriptions_count": "{{count}} iscrizione",
|
||||
"generic_subscriptions_count_plural": "{{count}} iscrizioni",
|
||||
"tokens_count": "{{count}} gettone",
|
||||
"tokens_count_plural": "{{count}} gettoni",
|
||||
"generic_subscriptions_count_0": "{{count}} iscrizione",
|
||||
"generic_subscriptions_count_1": "{{count}} iscrizioni",
|
||||
"generic_subscriptions_count_2": "{{count}} iscrizioni",
|
||||
"tokens_count_0": "{{count}} gettone",
|
||||
"tokens_count_1": "{{count}} gettoni",
|
||||
"tokens_count_2": "{{count}} gettoni",
|
||||
"Import/export": "Importa/esporta",
|
||||
"unsubscribe": "disiscriviti",
|
||||
"revoke": "revoca",
|
||||
"Subscriptions": "Iscrizioni",
|
||||
"subscriptions_unseen_notifs_count": "{{count}} notifica non visualizzata",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}} notifiche non visualizzate",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} notifica non visualizzata",
|
||||
"subscriptions_unseen_notifs_count_1": "{{count}} notifiche non visualizzate",
|
||||
"subscriptions_unseen_notifs_count_2": "{{count}} notifiche non visualizzate",
|
||||
"search": "Cerca",
|
||||
"Log out": "Esci",
|
||||
"Source available here.": "Codice sorgente.",
|
||||
|
@ -151,8 +157,9 @@
|
|||
"Whitelisted regions: ": "Regioni in lista bianca: ",
|
||||
"Blacklisted regions: ": "Regioni in lista nera: ",
|
||||
"Shared `x`": "Condiviso `x`",
|
||||
"generic_views_count": "{{count}} visualizzazione",
|
||||
"generic_views_count_plural": "{{count}} visualizzazioni",
|
||||
"generic_views_count_0": "{{count}} visualizzazione",
|
||||
"generic_views_count_1": "{{count}} visualizzazioni",
|
||||
"generic_views_count_2": "{{count}} visualizzazioni",
|
||||
"Premieres in `x`": "In anteprima in `x`",
|
||||
"Premieres `x`": "In anteprima `x`",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Ciao, Sembra che tu abbia disattivato JavaScript. Clicca qui per visualizzare i commenti, ma considera che il caricamento potrebbe richiedere più tempo.",
|
||||
|
@ -300,20 +307,27 @@
|
|||
"Yiddish": "Yiddish",
|
||||
"Yoruba": "Yoruba",
|
||||
"Zulu": "Zulu",
|
||||
"generic_count_years": "{{count}} anno",
|
||||
"generic_count_years_plural": "{{count}} anni",
|
||||
"generic_count_months": "{{count}} mese",
|
||||
"generic_count_months_plural": "{{count}} mesi",
|
||||
"generic_count_weeks": "{{count}} settimana",
|
||||
"generic_count_weeks_plural": "{{count}} settimane",
|
||||
"generic_count_days": "{{count}} giorno",
|
||||
"generic_count_days_plural": "{{count}} giorni",
|
||||
"generic_count_hours": "{{count}} ora",
|
||||
"generic_count_hours_plural": "{{count}} ore",
|
||||
"generic_count_minutes": "{{count}} minuto",
|
||||
"generic_count_minutes_plural": "{{count}} minuti",
|
||||
"generic_count_seconds": "{{count}} secondo",
|
||||
"generic_count_seconds_plural": "{{count}} secondi",
|
||||
"generic_count_years_0": "{{count}} anno",
|
||||
"generic_count_years_1": "{{count}} anni",
|
||||
"generic_count_years_2": "{{count}} anni",
|
||||
"generic_count_months_0": "{{count}} mese",
|
||||
"generic_count_months_1": "{{count}} mesi",
|
||||
"generic_count_months_2": "{{count}} mesi",
|
||||
"generic_count_weeks_0": "{{count}} settimana",
|
||||
"generic_count_weeks_1": "{{count}} settimane",
|
||||
"generic_count_weeks_2": "{{count}} settimane",
|
||||
"generic_count_days_0": "{{count}} giorno",
|
||||
"generic_count_days_1": "{{count}} giorni",
|
||||
"generic_count_days_2": "{{count}} giorni",
|
||||
"generic_count_hours_0": "{{count}} ora",
|
||||
"generic_count_hours_1": "{{count}} ore",
|
||||
"generic_count_hours_2": "{{count}} ore",
|
||||
"generic_count_minutes_0": "{{count}} minuto",
|
||||
"generic_count_minutes_1": "{{count}} minuti",
|
||||
"generic_count_minutes_2": "{{count}} minuti",
|
||||
"generic_count_seconds_0": "{{count}} secondo",
|
||||
"generic_count_seconds_1": "{{count}} secondi",
|
||||
"generic_count_seconds_2": "{{count}} secondi",
|
||||
"Fallback comments: ": "Commenti alternativi: ",
|
||||
"Popular": "Popolare",
|
||||
"Search": "Cerca",
|
||||
|
@ -417,10 +431,12 @@
|
|||
"search_filters_duration_option_short": "Corto (< 4 minuti)",
|
||||
"search_filters_duration_option_long": "Lungo (> 20 minuti)",
|
||||
"search_filters_features_option_purchased": "Acquistato",
|
||||
"comments_view_x_replies": "Vedi {{count}} risposta",
|
||||
"comments_view_x_replies_plural": "Vedi {{count}} risposte",
|
||||
"comments_points_count": "{{count}} punto",
|
||||
"comments_points_count_plural": "{{count}} punti",
|
||||
"comments_view_x_replies_0": "Vedi {{count}} risposta",
|
||||
"comments_view_x_replies_1": "Vedi {{count}} risposte",
|
||||
"comments_view_x_replies_2": "Vedi {{count}} risposte",
|
||||
"comments_points_count_0": "{{count}} punto",
|
||||
"comments_points_count_1": "{{count}} punti",
|
||||
"comments_points_count_2": "{{count}} punti",
|
||||
"Portuguese (auto-generated)": "Portoghese (generati automaticamente)",
|
||||
"crash_page_you_found_a_bug": "Sembra che tu abbia trovato un bug in Invidious!",
|
||||
"crash_page_switch_instance": "provato a <a href=\"`x`\">usare un'altra istanza</a>",
|
||||
|
@ -484,5 +500,9 @@
|
|||
"generic_button_delete": "Elimina",
|
||||
"generic_button_save": "Salva",
|
||||
"playlist_button_add_items": "Aggiungi video",
|
||||
"channel_tab_podcasts_label": "Podcast"
|
||||
"channel_tab_podcasts_label": "Podcast",
|
||||
"generic_channels_count_0": "{{count}} canale",
|
||||
"generic_channels_count_1": "{{count}} canali",
|
||||
"generic_channels_count_2": "{{count}} canali",
|
||||
"Import YouTube watch history (.json)": "Importa la cronologia delle visualizzazioni di YouTube (.json)"
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
"preferences_category_player": "プレイヤーの設定",
|
||||
"preferences_video_loop_label": "常にループ: ",
|
||||
"preferences_autoplay_label": "自動再生: ",
|
||||
"preferences_continue_label": "次の動画を自動再生: ",
|
||||
"preferences_continue_label": "次の動画に移動: ",
|
||||
"preferences_continue_autoplay_label": "次の動画を自動再生: ",
|
||||
"preferences_listen_label": "音声モードを使用: ",
|
||||
"preferences_local_label": "動画視聴にプロキシを経由: ",
|
||||
|
@ -68,7 +68,7 @@
|
|||
"preferences_related_videos_label": "関連動画を表示: ",
|
||||
"preferences_annotations_label": "最初からアノテーションを表示: ",
|
||||
"preferences_extend_desc_label": "動画の説明文を自動的に拡張: ",
|
||||
"preferences_vr_mode_label": "対話的な360°動画 (WebGL が必要): ",
|
||||
"preferences_vr_mode_label": "対話的な360°動画 (WebGLが必要): ",
|
||||
"preferences_category_visual": "外観設定",
|
||||
"preferences_player_style_label": "プレイヤーのスタイル: ",
|
||||
"Dark mode: ": "ダークモード: ",
|
||||
|
@ -125,9 +125,9 @@
|
|||
"subscriptions_unseen_notifs_count_0": "{{count}}件の未読通知",
|
||||
"search": "検索",
|
||||
"Log out": "ログアウト",
|
||||
"Released under the AGPLv3 on Github.": "GitHub 上で AGPLv3 の元で公開",
|
||||
"Released under the AGPLv3 on Github.": "GitHub上でAGPLv3の元で公開",
|
||||
"Source available here.": "ソースはここで閲覧可能です。",
|
||||
"View JavaScript license information.": "JavaScript ライセンス情報",
|
||||
"View JavaScript license information.": "JavaScriptライセンス情報",
|
||||
"View privacy policy.": "個人情報保護方針",
|
||||
"Trending": "急上昇",
|
||||
"Public": "公開",
|
||||
|
@ -144,7 +144,7 @@
|
|||
"Show more": "もっと見る",
|
||||
"Show less": "表示を少なく",
|
||||
"Watch on YouTube": "YouTubeで視聴",
|
||||
"Switch Invidious Instance": "Invidious インスタンスの変更",
|
||||
"Switch Invidious Instance": "Invidiousインスタンスの変更",
|
||||
"Hide annotations": "アノテーションを隠す",
|
||||
"Show annotations": "アノテーションを表示",
|
||||
"Genre: ": "ジャンル: ",
|
||||
|
@ -363,9 +363,9 @@
|
|||
"search_filters_features_option_location": "場所",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"Current version: ": "現在のバージョン: ",
|
||||
"next_steps_error_message": "下記のものを試して下さい: ",
|
||||
"next_steps_error_message_refresh": "再読込",
|
||||
"next_steps_error_message_go_to_youtube": "YouTubeへ",
|
||||
"next_steps_error_message": "以下をお試してください: ",
|
||||
"next_steps_error_message_refresh": "再読み込み",
|
||||
"next_steps_error_message_go_to_youtube": "YouTubeを開く",
|
||||
"search_filters_duration_option_short": "4分未満",
|
||||
"footer_documentation": "説明書",
|
||||
"footer_source_code": "ソースコード",
|
||||
|
@ -459,7 +459,7 @@
|
|||
"Song: ": "曲: ",
|
||||
"Channel Sponsor": "チャンネルのスポンサー",
|
||||
"Standard YouTube license": "標準 Youtube ライセンス",
|
||||
"Download is disabled": "ダウンロード: このインスタンスでは未対応",
|
||||
"Download is disabled": "ダウンロード: このインスタンスは未対応",
|
||||
"Import YouTube playlist (.csv)": "YouTube 再生リストをインポート (.csv)",
|
||||
"generic_button_delete": "削除",
|
||||
"generic_button_cancel": "キャンセル",
|
||||
|
@ -468,5 +468,7 @@
|
|||
"generic_button_edit": "編集",
|
||||
"generic_button_save": "保存",
|
||||
"generic_button_rss": "RSS",
|
||||
"playlist_button_add_items": "動画を追加"
|
||||
"playlist_button_add_items": "動画を追加",
|
||||
"generic_channels_count_0": "{{count}}個のチャンネル",
|
||||
"Import YouTube watch history (.json)": "YouTube 視聴履歴をインポート (.json)"
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
"source": "출처",
|
||||
"JavaScript license information": "자바스크립트 라이선스 정보",
|
||||
"An alternative front-end to YouTube": "유튜브의 프론트엔드 대안",
|
||||
"History": "역사",
|
||||
"History": "시청 기록",
|
||||
"Delete account?": "계정을 삭제 하시겠습니까?",
|
||||
"Export data as JSON": "JSON으로 데이터 내보내기",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML로 구독 내보내기 (뉴파이프 및 프리튜브)",
|
||||
|
@ -351,7 +351,7 @@
|
|||
"News": "뉴스",
|
||||
"Gaming": "게임",
|
||||
"Music": "음악",
|
||||
"Default": "디폴트",
|
||||
"Default": "전체",
|
||||
"Rating: ": "평점: ",
|
||||
"About": "정보",
|
||||
"Top": "최고",
|
||||
|
@ -468,5 +468,7 @@
|
|||
"generic_button_save": "저장",
|
||||
"generic_button_cancel": "취소",
|
||||
"generic_button_rss": "RSS",
|
||||
"channel_tab_releases_label": "출시"
|
||||
"channel_tab_releases_label": "출시",
|
||||
"generic_channels_count_0": "{{count}} 채널",
|
||||
"Import YouTube watch history (.json)": "유튜브 시청 기록 가져오기 (.json)"
|
||||
}
|
||||
|
|
|
@ -484,5 +484,8 @@
|
|||
"generic_button_save": "Lagre",
|
||||
"generic_button_cancel": "Avbryt",
|
||||
"generic_button_rss": "RSS",
|
||||
"playlist_button_add_items": "Legg til videoer"
|
||||
"playlist_button_add_items": "Legg til videoer",
|
||||
"generic_channels_count": "{{count}} kanal",
|
||||
"generic_channels_count_plural": "{{count}} kanaler",
|
||||
"Import YouTube watch history (.json)": "Importere YouTube visningshistorikk (.json)"
|
||||
}
|
||||
|
|
|
@ -107,10 +107,10 @@
|
|||
"Report statistics: ": "Statistieken bijhouden? ",
|
||||
"Save preferences": "Instellingen opslaan",
|
||||
"Subscription manager": "Abonnementen beheren",
|
||||
"Token manager": "Toegangssleutels beheren",
|
||||
"Token manager": "Toegangssleutelbeheerder",
|
||||
"Token": "Toegangssleutel",
|
||||
"Import/export": "Importeren/Exporteren",
|
||||
"unsubscribe": "Deabonneren",
|
||||
"unsubscribe": "deabonneren",
|
||||
"revoke": "Intrekken",
|
||||
"Subscriptions": "Abonnementen",
|
||||
"search": "zoeken",
|
||||
|
@ -357,7 +357,7 @@
|
|||
"footer_original_source_code": "Originele bron-code",
|
||||
"footer_modfied_source_code": "Gewijzigde bron-code",
|
||||
"adminprefs_modified_source_code_url_label": "URL naar gewijzigde bron-code-opslagplaats",
|
||||
"next_steps_error_message": "Waarna u moet proberen om: ",
|
||||
"next_steps_error_message": "Daarna moet u proberen om: ",
|
||||
"footer_source_code": "Bron-code",
|
||||
"search_filters_duration_option_long": "Lang (> 20 minuten)",
|
||||
"preferences_quality_option_dash": "DASH (adaptieve kwaliteit)",
|
||||
|
@ -462,5 +462,30 @@
|
|||
"Spanish (auto-generated)": "Spaans (automatisch gegenereerd)",
|
||||
"crash_page_you_found_a_bug": "Je lijkt een bug in Invidious tegengekomen te zijn!",
|
||||
"search_filters_duration_option_medium": "Gemiddeld (4 - 20 minuten)",
|
||||
"crash_page_report_issue": "Indien het bovenstaande niet hielp, gelieve dan <a href=\"`x`\">een nieuw ticket op GitHub</a> te openen (liefst in het Engels) en neem de volgende tekst op in je bericht (gelieve deze NIET te vertalen):"
|
||||
"crash_page_report_issue": "Indien het bovenstaande niet hielp, gelieve dan <a href=\"`x`\">een nieuw ticket op GitHub</a> te openen (liefst in het Engels) en neem de volgende tekst op in je bericht (gelieve deze NIET te vertalen):",
|
||||
"channel_tab_podcasts_label": "Podcasts",
|
||||
"Download is disabled": "Downloaden is uitgeschakeld",
|
||||
"Channel Sponsor": "Kanaalsponsor",
|
||||
"channel_tab_streams_label": "Livestreams",
|
||||
"playlist_button_add_items": "Video's toevoegen",
|
||||
"Artist: ": "Artiest: ",
|
||||
"generic_button_save": "Opslaan",
|
||||
"generic_button_cancel": "Annuleren",
|
||||
"Album: ": "Album: ",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"channel_tab_releases_label": "Uitgaves",
|
||||
"Song: ": "Lied: ",
|
||||
"generic_channels_count": "{{count}} kanaal",
|
||||
"generic_channels_count_plural": "{{count}} kanalen",
|
||||
"Popular enabled: ": "Populair geactiveerd: ",
|
||||
"channel_tab_playlists_label": "Afspeellijsten",
|
||||
"generic_button_edit": "Bewerken",
|
||||
"Music in this video": "Muziek in deze video",
|
||||
"generic_button_rss": "RSS",
|
||||
"channel_tab_channels_label": "Kanalen",
|
||||
"error_video_not_in_playlist": "De gevraagde video bestaat niet in deze afspeellijst. <a href=\"`x`\">Klik hier voor de startpagina van de afspeellijst.</a>",
|
||||
"generic_button_delete": "Verwijderen",
|
||||
"Import YouTube playlist (.csv)": "YouTube-afspeellijst importeren (.csv)",
|
||||
"Standard YouTube license": "Standaard YouTube-licentie",
|
||||
"Import YouTube watch history (.json)": "YouTube-kijkgeschiedenis importeren (.json)"
|
||||
}
|
||||
|
|
|
@ -492,7 +492,7 @@
|
|||
"Song: ": "Piosenka: ",
|
||||
"Channel Sponsor": "Sponsor kanału",
|
||||
"Standard YouTube license": "Standardowa licencja YouTube",
|
||||
"Import YouTube playlist (.csv)": "Importuj playlistę YouTube (.csv)",
|
||||
"Import YouTube playlist (.csv)": "Importuj playlistę z YouTube (.csv)",
|
||||
"generic_button_edit": "Edytuj",
|
||||
"generic_button_cancel": "Anuluj",
|
||||
"generic_button_rss": "RSS",
|
||||
|
@ -500,5 +500,10 @@
|
|||
"channel_tab_releases_label": "Wydania",
|
||||
"generic_button_delete": "Usuń",
|
||||
"generic_button_save": "Zapisz",
|
||||
"playlist_button_add_items": "Dodaj filmy"
|
||||
"playlist_button_add_items": "Dodaj filmy",
|
||||
"generic_channels_count_0": "{{count}} kanał",
|
||||
"generic_channels_count_1": "{{count}} kanały",
|
||||
"generic_channels_count_2": "{{count}} kanałów",
|
||||
"Import YouTube watch history (.json)": "Importuj historię oglądania z YouTube (.json)",
|
||||
"toggle_theme": "Przełącz motyw"
|
||||
}
|
||||
|
|
|
@ -112,8 +112,9 @@
|
|||
"Subscription manager": "Gerenciador de inscrições",
|
||||
"Token manager": "Gerenciador de tokens",
|
||||
"Token": "Token",
|
||||
"tokens_count": "{{count}} token",
|
||||
"tokens_count_plural": "{{count}} tokens",
|
||||
"tokens_count_0": "{{count}} token",
|
||||
"tokens_count_1": "{{count}} tokens",
|
||||
"tokens_count_2": "{{count}} tokens",
|
||||
"Import/export": "Importar/Exportar",
|
||||
"unsubscribe": "cancelar inscrição",
|
||||
"revoke": "revogar",
|
||||
|
@ -297,20 +298,27 @@
|
|||
"Yiddish": "Iídiche",
|
||||
"Yoruba": "Iorubá",
|
||||
"Zulu": "Zulu",
|
||||
"generic_count_years": "{{count}} ano",
|
||||
"generic_count_years_plural": "{{count}} anos",
|
||||
"generic_count_months": "{{count}} mês",
|
||||
"generic_count_months_plural": "{{count}} meses",
|
||||
"generic_count_weeks": "{{count}} semana",
|
||||
"generic_count_weeks_plural": "{{count}} semanas",
|
||||
"generic_count_days": "{{count}} dia",
|
||||
"generic_count_days_plural": "{{count}} dias",
|
||||
"generic_count_hours": "{{count}} hora",
|
||||
"generic_count_hours_plural": "{{count}} horas",
|
||||
"generic_count_minutes": "{{count}} minuto",
|
||||
"generic_count_minutes_plural": "{{count}} minutos",
|
||||
"generic_count_seconds": "{{count}} segundo",
|
||||
"generic_count_seconds_plural": "{{count}} segundos",
|
||||
"generic_count_years_0": "{{count}} ano",
|
||||
"generic_count_years_1": "{{count}} anos",
|
||||
"generic_count_years_2": "{{count}} anos",
|
||||
"generic_count_months_0": "{{count}} mês",
|
||||
"generic_count_months_1": "{{count}} meses",
|
||||
"generic_count_months_2": "{{count}} meses",
|
||||
"generic_count_weeks_0": "{{count}} semana",
|
||||
"generic_count_weeks_1": "{{count}} semanas",
|
||||
"generic_count_weeks_2": "{{count}} semanas",
|
||||
"generic_count_days_0": "{{count}} dia",
|
||||
"generic_count_days_1": "{{count}} dias",
|
||||
"generic_count_days_2": "{{count}} dias",
|
||||
"generic_count_hours_0": "{{count}} hora",
|
||||
"generic_count_hours_1": "{{count}} horas",
|
||||
"generic_count_hours_2": "{{count}} horas",
|
||||
"generic_count_minutes_0": "{{count}} minuto",
|
||||
"generic_count_minutes_1": "{{count}} minutos",
|
||||
"generic_count_minutes_2": "{{count}} minutos",
|
||||
"generic_count_seconds_0": "{{count}} segundo",
|
||||
"generic_count_seconds_1": "{{count}} segundos",
|
||||
"generic_count_seconds_2": "{{count}} segundos",
|
||||
"Fallback comments: ": "Comentários alternativos: ",
|
||||
"Popular": "Populares",
|
||||
"Search": "Procurar",
|
||||
|
@ -377,20 +385,27 @@
|
|||
"preferences_quality_dash_label": "Qualidade de vídeo do painel preferida: ",
|
||||
"preferences_region_label": "País do conteúdo: ",
|
||||
"preferences_quality_dash_option_4320p": "4320p",
|
||||
"generic_videos_count": "{{count}} vídeo",
|
||||
"generic_videos_count_plural": "{{count}} vídeos",
|
||||
"generic_playlists_count": "{{count}} lista de reprodução",
|
||||
"generic_playlists_count_plural": "{{count}} listas de reprodução",
|
||||
"generic_subscribers_count": "{{count}} inscrito",
|
||||
"generic_subscribers_count_plural": "{{count}} inscritos",
|
||||
"generic_subscriptions_count": "{{count}} inscrição",
|
||||
"generic_subscriptions_count_plural": "{{count}} inscrições",
|
||||
"subscriptions_unseen_notifs_count": "{{count}} notificação não vista",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}} notificações não vistas",
|
||||
"comments_view_x_replies": "Ver {{count}} resposta",
|
||||
"comments_view_x_replies_plural": "Ver {{count}} respostas",
|
||||
"comments_points_count": "{{count}} ponto",
|
||||
"comments_points_count_plural": "{{count}} pontos",
|
||||
"generic_videos_count_0": "{{count}} vídeo",
|
||||
"generic_videos_count_1": "{{count}} vídeos",
|
||||
"generic_videos_count_2": "{{count}} vídeos",
|
||||
"generic_playlists_count_0": "{{count}} lista de reprodução",
|
||||
"generic_playlists_count_1": "{{count}} listas de reprodução",
|
||||
"generic_playlists_count_2": "{{count}} listas de reprodução",
|
||||
"generic_subscribers_count_0": "{{count}} inscrito",
|
||||
"generic_subscribers_count_1": "{{count}} inscritos",
|
||||
"generic_subscribers_count_2": "{{count}} inscritos",
|
||||
"generic_subscriptions_count_0": "{{count}} inscrição",
|
||||
"generic_subscriptions_count_1": "{{count}} inscrições",
|
||||
"generic_subscriptions_count_2": "{{count}} inscrições",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} notificação não vista",
|
||||
"subscriptions_unseen_notifs_count_1": "{{count}} notificações não vistas",
|
||||
"subscriptions_unseen_notifs_count_2": "{{count}} notificações não vistas",
|
||||
"comments_view_x_replies_0": "Ver {{count}} resposta",
|
||||
"comments_view_x_replies_1": "Ver {{count}} respostas",
|
||||
"comments_view_x_replies_2": "Ver {{count}} respostas",
|
||||
"comments_points_count_0": "{{count}} ponto",
|
||||
"comments_points_count_1": "{{count}} pontos",
|
||||
"comments_points_count_2": "{{count}} pontos",
|
||||
"crash_page_you_found_a_bug": "Parece que você encontrou um erro no Invidious!",
|
||||
"crash_page_before_reporting": "Antes de reportar um erro, verifique se você:",
|
||||
"preferences_save_player_pos_label": "Salvar a posição de reprodução: ",
|
||||
|
@ -400,8 +415,9 @@
|
|||
"crash_page_search_issue": "procurou por um <a href=\"`x`\">erro existente no GitHub</a>",
|
||||
"crash_page_report_issue": "Se nenhuma opção acima ajudou, por favor <a href=\"`x`\">abra um novo problema no Github</a> (preferencialmente em inglês) e inclua o seguinte texto (NÃO traduza):",
|
||||
"crash_page_read_the_faq": "leia as <a href=\"`x`\">Perguntas frequentes (FAQ)</a>",
|
||||
"generic_views_count": "{{count}} visualização",
|
||||
"generic_views_count_plural": "{{count}} visualizações",
|
||||
"generic_views_count_0": "{{count}} visualização",
|
||||
"generic_views_count_1": "{{count}} visualizações",
|
||||
"generic_views_count_2": "{{count}} visualizações",
|
||||
"preferences_quality_option_dash": "DASH (qualidade adaptável)",
|
||||
"preferences_quality_option_hd720": "HD720",
|
||||
"preferences_quality_option_small": "Pequeno",
|
||||
|
@ -484,5 +500,10 @@
|
|||
"channel_tab_releases_label": "Lançamentos",
|
||||
"channel_tab_podcasts_label": "Podcasts",
|
||||
"generic_button_cancel": "Cancelar",
|
||||
"generic_button_rss": "RSS"
|
||||
"generic_button_rss": "RSS",
|
||||
"generic_channels_count_0": "{{count}} canal",
|
||||
"generic_channels_count_1": "{{count}} canais",
|
||||
"generic_channels_count_2": "{{count}} canais",
|
||||
"Import YouTube watch history (.json)": "Importar histórico de reprodução do YouTube (.json)",
|
||||
"toggle_theme": "Alternar Tema"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"search_filters_type_option_show": "Espetáculo",
|
||||
"search_filters_type_option_show": "Série",
|
||||
"search_filters_sort_option_views": "Visualizações",
|
||||
"search_filters_sort_option_date": "Data de envio",
|
||||
"search_filters_sort_option_date": "Data de carregamento",
|
||||
"search_filters_sort_option_rating": "Avaliação",
|
||||
"search_filters_sort_option_relevance": "Relevância",
|
||||
"Switch Invidious Instance": "Mudar a instância do Invidious",
|
||||
|
@ -13,7 +13,7 @@
|
|||
"preferences_category_misc": "Preferências diversas",
|
||||
"preferences_vr_mode_label": "Vídeos interativos de 360 graus (necessita de WebGL): ",
|
||||
"preferences_extend_desc_label": "Estender automaticamente a descrição do vídeo: ",
|
||||
"next_steps_error_message_go_to_youtube": "Ir ao YouTube",
|
||||
"next_steps_error_message_go_to_youtube": "Ir para o YouTube",
|
||||
"next_steps_error_message": "Pode tentar as seguintes opções: ",
|
||||
"next_steps_error_message_refresh": "Atualizar",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
|
@ -44,20 +44,27 @@
|
|||
"Default": "Predefinido",
|
||||
"Top": "Destaques",
|
||||
"Search": "Pesquisar",
|
||||
"generic_count_years": "{{count}} segundo",
|
||||
"generic_count_years_plural": "{{count}} segundos",
|
||||
"generic_count_months": "{{count}} minuto",
|
||||
"generic_count_months_plural": "{{count}} minutos",
|
||||
"generic_count_weeks": "{{count}} hora",
|
||||
"generic_count_weeks_plural": "{{count}} horas",
|
||||
"generic_count_days": "{{count}} dia",
|
||||
"generic_count_days_plural": "{{count}} dias",
|
||||
"generic_count_hours": "{{count}} seman",
|
||||
"generic_count_hours_plural": "{{count}} semanas",
|
||||
"generic_count_minutes": "{{count}} mês",
|
||||
"generic_count_minutes_plural": "{{count}} meses",
|
||||
"generic_count_seconds": "{{count}} ano",
|
||||
"generic_count_seconds_plural": "{{count}} anos",
|
||||
"generic_count_years_0": "{{count}} ano",
|
||||
"generic_count_years_1": "{{count}} anos",
|
||||
"generic_count_years_2": "{{count}} anos",
|
||||
"generic_count_months_0": "{{count}} mês",
|
||||
"generic_count_months_1": "{{count}} meses",
|
||||
"generic_count_months_2": "{{count}} meses",
|
||||
"generic_count_weeks_0": "{{count}} semana",
|
||||
"generic_count_weeks_1": "{{count}} semanas",
|
||||
"generic_count_weeks_2": "{{count}} semanas",
|
||||
"generic_count_days_0": "{{count}} dia",
|
||||
"generic_count_days_1": "{{count}} dias",
|
||||
"generic_count_days_2": "{{count}} dias",
|
||||
"generic_count_hours_0": "{{count}} hora",
|
||||
"generic_count_hours_1": "{{count}} horas",
|
||||
"generic_count_hours_2": "{{count}} horas",
|
||||
"generic_count_minutes_0": "{{count}} minuto",
|
||||
"generic_count_minutes_1": "{{count}} minutos",
|
||||
"generic_count_minutes_2": "{{count}} minutos",
|
||||
"generic_count_seconds_0": "{{count}} segundo",
|
||||
"generic_count_seconds_1": "{{count}} segundos",
|
||||
"generic_count_seconds_2": "{{count}} segundos",
|
||||
"Chinese (Traditional)": "Chinês (tradicional)",
|
||||
"Chinese (Simplified)": "Chinês (simplificado)",
|
||||
"Could not pull trending pages.": "Não foi possível obter as páginas de tendências.",
|
||||
|
@ -75,7 +82,7 @@
|
|||
"Import/export data": "Importar / exportar dados",
|
||||
"preferences_annotations_label": "Mostrar anotações sempre: ",
|
||||
"preferences_continue_label": "Reproduzir sempre o próximo: ",
|
||||
"Sign In": "Iniciar sessão",
|
||||
"Sign In": "Entrar",
|
||||
"Log in/register": "Iniciar sessão/registar",
|
||||
"Delete account?": "Eliminar conta?",
|
||||
"Import and Export Data": "Importar e exportar dados",
|
||||
|
@ -167,8 +174,9 @@
|
|||
"Log out": "Terminar sessão",
|
||||
"Subscriptions": "Subscrições",
|
||||
"revoke": "revogar",
|
||||
"tokens_count": "{{count}} token",
|
||||
"tokens_count_plural": "{{count}} tokens",
|
||||
"tokens_count_0": "{{count}} Token",
|
||||
"tokens_count_1": "{{count}} Tokens",
|
||||
"tokens_count_2": "{{count}} Tokens",
|
||||
"Token": "Token",
|
||||
"Token manager": "Gerir tokens",
|
||||
"Subscription manager": "Gerir subscrições",
|
||||
|
@ -402,31 +410,39 @@
|
|||
"videoinfo_youTube_embed_link": "Incorporar",
|
||||
"preferences_save_player_pos_label": "Guardar a posição de reprodução atual do vídeo: ",
|
||||
"download_subtitles": "Legendas - `x` (.vtt)",
|
||||
"generic_views_count": "{{count}} visualização",
|
||||
"generic_views_count_plural": "{{count}} visualizações",
|
||||
"generic_views_count_0": "{{count}} visualização",
|
||||
"generic_views_count_1": "{{count}} visualizações",
|
||||
"generic_views_count_2": "{{count}} visualizações",
|
||||
"videoinfo_started_streaming_x_ago": "Iniciou a transmissão há `x`",
|
||||
"user_saved_playlists": "`x` listas de reprodução guardadas",
|
||||
"generic_videos_count": "{{count}} vídeo",
|
||||
"generic_videos_count_plural": "{{count}} vídeos",
|
||||
"generic_playlists_count": "{{count}} lista de reprodução",
|
||||
"generic_playlists_count_plural": "{{count}} listas de reprodução",
|
||||
"subscriptions_unseen_notifs_count": "{{count}} notificação não vista",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}} notificações não vistas",
|
||||
"comments_view_x_replies": "Ver {{count}} resposta",
|
||||
"comments_view_x_replies_plural": "Ver {{count}} respostas",
|
||||
"generic_subscribers_count": "{{count}} inscrito",
|
||||
"generic_subscribers_count_plural": "{{count}} inscritos",
|
||||
"generic_subscriptions_count": "{{count}} inscrição",
|
||||
"generic_subscriptions_count_plural": "{{count}} inscrições",
|
||||
"comments_points_count": "{{count}} ponto",
|
||||
"comments_points_count_plural": "{{count}} pontos",
|
||||
"generic_videos_count_0": "{{count}} vídeo",
|
||||
"generic_videos_count_1": "{{count}} vídeos",
|
||||
"generic_videos_count_2": "{{count}} vídeos",
|
||||
"generic_playlists_count_0": "{{count}} lista de reprodução",
|
||||
"generic_playlists_count_1": "{{count}} listas de reprodução",
|
||||
"generic_playlists_count_2": "{{count}} listas de reprodução",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} notificação não vista",
|
||||
"subscriptions_unseen_notifs_count_1": "{{count}} notificações não vistas",
|
||||
"subscriptions_unseen_notifs_count_2": "{{count}} notificações não vistas",
|
||||
"comments_view_x_replies_0": "Ver {{count}} resposta",
|
||||
"comments_view_x_replies_1": "Ver {{count}} respostas",
|
||||
"comments_view_x_replies_2": "Ver {{count}} respostas",
|
||||
"generic_subscribers_count_0": "{{count}} inscrito",
|
||||
"generic_subscribers_count_1": "{{count}} inscritos",
|
||||
"generic_subscribers_count_2": "{{count}} inscritos",
|
||||
"generic_subscriptions_count_0": "{{count}} inscrição",
|
||||
"generic_subscriptions_count_1": "{{count}} inscrições",
|
||||
"generic_subscriptions_count_2": "{{count}} inscrições",
|
||||
"comments_points_count_0": "{{count}} ponto",
|
||||
"comments_points_count_1": "{{count}} pontos",
|
||||
"comments_points_count_2": "{{count}} pontos",
|
||||
"crash_page_you_found_a_bug": "Parece que encontrou um erro no Invidious!",
|
||||
"crash_page_before_reporting": "Antes de reportar um erro, verifique se:",
|
||||
"crash_page_refresh": "tentou <a href=\"`x`\">recarregar a página</a>",
|
||||
"crash_page_switch_instance": "tentou <a href=\"`x`\">usar outra instância</a>",
|
||||
"crash_page_read_the_faq": "leia as <a href=\"`x`\">Perguntas frequentes (FAQ)</a>",
|
||||
"crash_page_search_issue": "procurou se <a href=\"`x`\">o erro já foi reportado no GitHub</a>",
|
||||
"crash_page_report_issue": "Se nenhuma opção acima ajudou, por favor <a href=\"`x`\">abra um novo problema no Github</a> (preferencialmente em inglês) e inclua o seguinte texto tal qual (NÃO o traduza):",
|
||||
"crash_page_report_issue": "Se nenhuma opção acima ajudou, por favor <a href=\"`x`\">abra um novo problema no Github</a> (preferencialmente em inglês) e inclua o seguinte texto (NÃO o traduza):",
|
||||
"user_created_playlists": "`x` listas de reprodução criadas",
|
||||
"search_filters_title": "Filtro",
|
||||
"Chinese (Taiwan)": "Chinês (Taiwan)",
|
||||
|
@ -464,7 +480,7 @@
|
|||
"search_filters_type_option_all": "Qualquer tipo",
|
||||
"search_filters_duration_option_none": "Qualquer duração",
|
||||
"Popular enabled: ": "Página \"popular\" ativada: ",
|
||||
"error_video_not_in_playlist": "O vídeo pedido não existe nesta lista de reprodução. <a href=\"`x`\">Clique aqui para a página inicial da lista de reprodução.</a>",
|
||||
"error_video_not_in_playlist": "O vídeo pedido não existe nesta lista de reprodução. <a href=\"`x`\">Clique aqui para voltar à página inicial da lista de reprodução.</a>",
|
||||
"channel_tab_playlists_label": "Listas de reprodução",
|
||||
"channel_tab_channels_label": "Canais",
|
||||
"channel_tab_shorts_label": "Curtos",
|
||||
|
@ -484,5 +500,10 @@
|
|||
"channel_tab_releases_label": "Lançamentos",
|
||||
"generic_button_save": "Salvar",
|
||||
"generic_button_cancel": "Cancelar",
|
||||
"playlist_button_add_items": "Adicionar vídeos"
|
||||
"playlist_button_add_items": "Adicionar vídeos",
|
||||
"generic_channels_count_0": "{{count}} canal",
|
||||
"generic_channels_count_1": "{{count}} canais",
|
||||
"generic_channels_count_2": "{{count}} canais",
|
||||
"Import YouTube watch history (.json)": "Importar histórico de reprodução do YouTube (.json)",
|
||||
"toggle_theme": "Trocar tema"
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"newest": "сначала новые",
|
||||
"oldest": "сначала старые",
|
||||
"popular": "популярные",
|
||||
"last": "недавние",
|
||||
"last": "последние",
|
||||
"Next page": "Следующая страница",
|
||||
"Previous page": "Предыдущая страница",
|
||||
"First page": "Первая страница",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"New password": "Новый пароль",
|
||||
"New passwords must match": "Новые пароли не совпадают",
|
||||
"Authorize token?": "Авторизовать токен?",
|
||||
"Authorize token for `x`?": "Авторизовать токен для `x`?",
|
||||
"Authorize token for `x`?": "Токен авторизации для `x`?",
|
||||
"Yes": "Да",
|
||||
"No": "Нет",
|
||||
"Import and Export Data": "Импорт и экспорт данных",
|
||||
|
@ -30,7 +30,7 @@
|
|||
"Export subscriptions as OPML": "Экспортировать подписки в формате OPML",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в формате OPML (для NewPipe и FreeTube)",
|
||||
"Export data as JSON": "Экспортировать данные Invidious в формате JSON",
|
||||
"Delete account?": "Удалить учётку?",
|
||||
"Delete account?": "Удалить учётную запись?",
|
||||
"History": "История",
|
||||
"An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube",
|
||||
"JavaScript license information": "Информация о лицензиях JavaScript",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"Text CAPTCHA": "Текстовая капча (англ.)",
|
||||
"Image CAPTCHA": "Капча-картинка",
|
||||
"Sign In": "Войти",
|
||||
"Register": "Зарегистрироваться",
|
||||
"Register": "Регистрация",
|
||||
"E-mail": "Эл. почта",
|
||||
"Preferences": "Настройки",
|
||||
"preferences_category_player": "Настройки проигрывателя",
|
||||
|
@ -62,7 +62,7 @@
|
|||
"preferences_captions_label": "Основной язык субтитров: ",
|
||||
"Fallback captions: ": "Дополнительный язык субтитров: ",
|
||||
"preferences_related_videos_label": "Показывать похожие видео? ",
|
||||
"preferences_annotations_label": "Всегда показывать аннотации? ",
|
||||
"preferences_annotations_label": "Показывать аннотации по умолчанию: ",
|
||||
"preferences_extend_desc_label": "Автоматически раскрывать описание видео: ",
|
||||
"preferences_vr_mode_label": "Интерактивные 360-градусные видео (необходим WebGL): ",
|
||||
"preferences_category_visual": "Настройки сайта",
|
||||
|
@ -78,13 +78,13 @@
|
|||
"preferences_annotations_subscribed_label": "Всегда показывать аннотации на каналах из ваших подписок? ",
|
||||
"Redirect homepage to feed: ": "Показывать подписки на главной странице: ",
|
||||
"preferences_max_results_label": "Число видео в ленте: ",
|
||||
"preferences_sort_label": "Сортировать видео: ",
|
||||
"published": "по дате публикации",
|
||||
"published - reverse": "по дате публикации в обратном порядке",
|
||||
"alphabetically": "по алфавиту",
|
||||
"alphabetically - reverse": "по алфавиту в обратном порядке",
|
||||
"channel name": "по названию канала",
|
||||
"channel name - reverse": "по названию канала в обратном порядке",
|
||||
"preferences_sort_label": "Сортировать видео по: ",
|
||||
"published": "дате публикации",
|
||||
"published - reverse": "дате публикации в обратном порядке",
|
||||
"alphabetically": "алфавиту",
|
||||
"alphabetically - reverse": "алфавиту в обратном порядке",
|
||||
"channel name": "названию канала",
|
||||
"channel name - reverse": "названию канала в обратном порядке",
|
||||
"Only show latest video from channel: ": "Показывать только последние видео с каналов: ",
|
||||
"Only show latest unwatched video from channel: ": "Показывать только последние непросмотренные видео с канала: ",
|
||||
"preferences_unseen_only_label": "Показывать только непросмотренные видео: ",
|
||||
|
@ -135,8 +135,8 @@
|
|||
"Title": "Заголовок",
|
||||
"Playlist privacy": "Видимость плейлиста",
|
||||
"Editing playlist `x`": "Редактирование плейлиста `x`",
|
||||
"Show more": "Развернуть",
|
||||
"Show less": "Свернуть",
|
||||
"Show more": "Показать больше",
|
||||
"Show less": "Показать меньше",
|
||||
"Watch on YouTube": "Смотреть на YouTube",
|
||||
"Switch Invidious Instance": "Сменить зеркало Invidious",
|
||||
"Hide annotations": "Скрыть аннотации",
|
||||
|
@ -415,7 +415,7 @@
|
|||
"generic_count_days_0": "{{count}} день",
|
||||
"generic_count_days_1": "{{count}} дня",
|
||||
"generic_count_days_2": "{{count}} дней",
|
||||
"preferences_quality_dash_option_auto": "Автоматическое",
|
||||
"preferences_quality_dash_option_auto": "Авто",
|
||||
"preferences_quality_dash_option_1080p": "1080p",
|
||||
"preferences_quality_dash_option_720p": "720p",
|
||||
"generic_subscriptions_count_0": "{{count}} подписка",
|
||||
|
@ -467,7 +467,7 @@
|
|||
"search_filters_features_option_three_sixty": "360°",
|
||||
"Video unavailable": "Видео недоступно",
|
||||
"preferences_save_player_pos_label": "Запоминать позицию: ",
|
||||
"preferences_region_label": "Страна: ",
|
||||
"preferences_region_label": "Страна источник ",
|
||||
"preferences_watch_history_label": "Включить историю просмотров: ",
|
||||
"search_filters_title": "Фильтр",
|
||||
"search_filters_duration_option_none": "Любой длины",
|
||||
|
@ -477,7 +477,7 @@
|
|||
"search_message_no_results": "Ничего не найдено.",
|
||||
"search_message_use_another_instance": " Дополнительно вы можете <a href=\"`x`\">поискать на других зеркалах</a>.",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"search_message_change_filters_or_query": "Попробуйте расширить поисковый запрос или изменить фильтры.",
|
||||
"search_message_change_filters_or_query": "Попробуйте расширить поисковый запрос и/или изменить фильтры.",
|
||||
"search_filters_duration_option_medium": "Средние (4 - 20 минут)",
|
||||
"search_filters_apply_button": "Применить фильтры",
|
||||
"Popular enabled: ": "Популярное включено: ",
|
||||
|
@ -501,5 +501,9 @@
|
|||
"generic_button_cancel": "Отменить",
|
||||
"generic_button_rss": "RSS",
|
||||
"playlist_button_add_items": "Добавить видео",
|
||||
"channel_tab_podcasts_label": "Подкасты"
|
||||
"channel_tab_podcasts_label": "Подкасты",
|
||||
"generic_channels_count_0": "{{count}} канал",
|
||||
"generic_channels_count_1": "{{count}} канала",
|
||||
"generic_channels_count_2": "{{count}} каналов",
|
||||
"Import YouTube watch history (.json)": "Импортировать историю просмотра из YouTube (.json)"
|
||||
}
|
||||
|
|
|
@ -516,5 +516,10 @@
|
|||
"generic_button_rss": "RSS",
|
||||
"playlist_button_add_items": "Dodaj videoposnetke",
|
||||
"channel_tab_podcasts_label": "Poddaje",
|
||||
"channel_tab_releases_label": "Izdaje"
|
||||
"channel_tab_releases_label": "Izdaje",
|
||||
"generic_channels_count_0": "{{count}} kanal",
|
||||
"generic_channels_count_1": "{{count}} kanala",
|
||||
"generic_channels_count_2": "{{count}} kanali",
|
||||
"generic_channels_count_3": "{{count}} kanalov",
|
||||
"Import YouTube watch history (.json)": "Uvozi zgodovino gledanja YouTube (.json)"
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
"invidious": "Invidious",
|
||||
"preferences_captions_label": "Titra parazgjedhje: ",
|
||||
"preferences_extend_desc_label": "Zgjero automatikisht përshkrimin e videos: ",
|
||||
"preferences_player_style_label": "Silt lojtësi: ",
|
||||
"preferences_player_style_label": "Stil lojtësi: ",
|
||||
"Dark mode: ": "Mënyra e errët: ",
|
||||
"preferences_dark_mode_label": "Temë: ",
|
||||
"dark": "e errët",
|
||||
|
@ -257,7 +257,7 @@
|
|||
"Video mode": "Mënyrë video",
|
||||
"channel_tab_videos_label": "Video",
|
||||
"search_filters_sort_option_rating": "Vlerësim",
|
||||
"search_filters_sort_option_date": "Datë ngarkimi",
|
||||
"search_filters_sort_option_date": "Datë Ngarkimi",
|
||||
"search_filters_sort_option_views": "Numër parjesh",
|
||||
"search_filters_type_label": "Lloj",
|
||||
"search_filters_duration_label": "Kohëzgjatje",
|
||||
|
@ -345,7 +345,7 @@
|
|||
"View YouTube comments": "Shihni komente Youtube",
|
||||
"View more comments on Reddit": "Shihni më tepër komente në Reddit",
|
||||
"View `x` comments": {
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Shihni `x` komente",
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Shihni `x` koment",
|
||||
"": "Shihni `x` komente"
|
||||
},
|
||||
"View Reddit comments": "Shihni komente Reddit",
|
||||
|
@ -462,5 +462,27 @@
|
|||
"channel_tab_channels_label": "Kanale",
|
||||
"Music in this video": "Muzikë në këtë video",
|
||||
"channel_tab_shorts_label": "Të shkurtra",
|
||||
"channel_tab_streams_label": "Transmetime të drejtpërdrejta"
|
||||
"channel_tab_streams_label": "Transmetime të drejtpërdrejta",
|
||||
"generic_button_cancel": "Anuloje",
|
||||
"generic_channels_count": "{{count}} kanal",
|
||||
"generic_channels_count_plural": "{{count}} kanale",
|
||||
"generic_button_rss": "RSS",
|
||||
"generic_button_delete": "Fshije",
|
||||
"generic_button_save": "Ruaje",
|
||||
"generic_button_edit": "Përpunoni",
|
||||
"playlist_button_add_items": "Shtoni video",
|
||||
"Report statistics: ": "Statistika raportimesh: ",
|
||||
"Download is disabled": "Shkarkimi është i çaktivizuar",
|
||||
"Channel Sponsor": "Sponsor Kanali",
|
||||
"channel_tab_releases_label": "Hedhje në qarkullim",
|
||||
"Song: ": "Pjesë: ",
|
||||
"Import YouTube playlist (.csv)": "Importoni luajlistë YouTube (.csv)",
|
||||
"Standard YouTube license": "Licencë YouTube standarde",
|
||||
"published - reverse": "publikuar më - së prapthi",
|
||||
"channel_tab_podcasts_label": "Podcast-e",
|
||||
"channel name - reverse": "emër kanali - së prapthi",
|
||||
"Import YouTube watch history (.json)": "Importo historik parjesh YouTube (.json)",
|
||||
"preferences_local_label": "Video përmes ndërmjetësi: ",
|
||||
"Fallback captions: ": "Titra nga halli: ",
|
||||
"Erroneous challenge": "Zgjidhje e gabuar"
|
||||
}
|
||||
|
|
466
locales/sr.json
466
locales/sr.json
|
@ -1,90 +1,90 @@
|
|||
{
|
||||
"LIVE": "UŽIVO",
|
||||
"Shared `x` ago": "Podeljeno pre `x`",
|
||||
"Shared `x` ago": "Deljeno pre `x`",
|
||||
"Unsubscribe": "Prekini praćenje",
|
||||
"Subscribe": "Prati",
|
||||
"Subscribe": "Zaprati",
|
||||
"View channel on YouTube": "Pogledaj kanal na YouTube-u",
|
||||
"View playlist on YouTube": "Pogledaj spisak izvođenja na YouTube-u",
|
||||
"View playlist on YouTube": "Pogledaj plejlistu na YouTube-u",
|
||||
"newest": "najnovije",
|
||||
"oldest": "najstarije",
|
||||
"popular": "popularno",
|
||||
"last": "poslednje",
|
||||
"Next page": "Sledeća stranica",
|
||||
"Previous page": "Prethodna stranica",
|
||||
"Clear watch history?": "Izbrisati povest pregledanja?",
|
||||
"Clear watch history?": "Očistiti istoriju gledanja?",
|
||||
"New password": "Nova lozinka",
|
||||
"New passwords must match": "Nove lozinke moraju biti istovetne",
|
||||
"Authorize token?": "Ovlasti žeton?",
|
||||
"Authorize token for `x`?": "Ovlasti žeton za `x`?",
|
||||
"New passwords must match": "Nove lozinke moraju da se podudaraju",
|
||||
"Authorize token?": "Autorizovati token?",
|
||||
"Authorize token for `x`?": "Autorizovati token za `x`?",
|
||||
"Yes": "Da",
|
||||
"No": "Ne",
|
||||
"Import and Export Data": "Uvoz i Izvoz Podataka",
|
||||
"Import and Export Data": "Uvoz i izvoz podataka",
|
||||
"Import": "Uvezi",
|
||||
"Import Invidious data": "Uvezi podatke sa Invidious-a",
|
||||
"Import YouTube subscriptions": "Uvezi praćenja sa YouTube-a",
|
||||
"Import FreeTube subscriptions (.db)": "Uvezi praćenja sa FreeTube-a (.db)",
|
||||
"Import NewPipe subscriptions (.json)": "Uvezi praćenja sa NewPipe-a (.json)",
|
||||
"Import NewPipe data (.zip)": "Uvezi podatke sa NewPipe-a (.zip)",
|
||||
"Import Invidious data": "Uvezi Invidious JSON podatke",
|
||||
"Import YouTube subscriptions": "Uvezi YouTube/OPML praćenja",
|
||||
"Import FreeTube subscriptions (.db)": "Uvezi FreeTube praćenja (.db)",
|
||||
"Import NewPipe subscriptions (.json)": "Uvezi NewPipe praćenja (.json)",
|
||||
"Import NewPipe data (.zip)": "Uvezi NewPipe podatke (.zip)",
|
||||
"Export": "Izvezi",
|
||||
"Export subscriptions as OPML": "Izvezi praćenja kao OPML datoteku",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Izvezi praćenja kao OPML datoteku (za NewPipe i FreeTube)",
|
||||
"Export data as JSON": "Izvezi podatke kao JSON datoteku",
|
||||
"Delete account?": "Izbrišite nalog?",
|
||||
"Export subscriptions as OPML": "Izvezi praćenja kao OPML",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Izvezi praćenja kao OPML (za NewPipe i FreeTube)",
|
||||
"Export data as JSON": "Izvezi Invidious podatke kao JSON",
|
||||
"Delete account?": "Izbrisati nalog?",
|
||||
"History": "Istorija",
|
||||
"An alternative front-end to YouTube": "Zamenski korisnički sloj za YouTube",
|
||||
"JavaScript license information": "Izveštaj o JavaScript odobrenju",
|
||||
"An alternative front-end to YouTube": "Alternativni front-end za YouTube",
|
||||
"JavaScript license information": "Informacije o JavaScript licenci",
|
||||
"source": "izvor",
|
||||
"Log in": "Prijavi se",
|
||||
"Log in/register": "Prijavi se/Otvori nalog",
|
||||
"User ID": "Korisnički ID",
|
||||
"Log in": "Prijava",
|
||||
"Log in/register": "Prijava/registracija",
|
||||
"User ID": "ID korisnika",
|
||||
"Password": "Lozinka",
|
||||
"Time (h:mm:ss):": "Vreme (č:mm:ss):",
|
||||
"Text CAPTCHA": "Znakovni CAPTCHA",
|
||||
"Image CAPTCHA": "Slikovni CAPTCHA",
|
||||
"Text CAPTCHA": "Tekst CAPTCHA",
|
||||
"Image CAPTCHA": "Slika CAPTCHA",
|
||||
"Sign In": "Prijava",
|
||||
"Register": "Otvori nalog",
|
||||
"E-mail": "E-pošta",
|
||||
"Register": "Registracija",
|
||||
"E-mail": "Imejl",
|
||||
"Preferences": "Podešavanja",
|
||||
"preferences_category_player": "Podešavanja reproduktora",
|
||||
"preferences_category_player": "Podešavanja plejera",
|
||||
"preferences_video_loop_label": "Uvek ponavljaj: ",
|
||||
"preferences_autoplay_label": "Samopuštanje: ",
|
||||
"preferences_continue_label": "Uvek podrazumevano puštaj sledeće: ",
|
||||
"preferences_continue_autoplay_label": "Samopuštanje sledećeg video zapisa: ",
|
||||
"preferences_listen_label": "Uvek podrazumevano uključen samo zvuk: ",
|
||||
"preferences_local_label": "Prikaz video zapisa preko posrednika: ",
|
||||
"Playlist privacy": "Podešavanja privatnosti plej liste",
|
||||
"Editing playlist `x`": "Izmena plej liste `x`",
|
||||
"Playlist does not exist.": "Nepostojeća plej lista.",
|
||||
"preferences_autoplay_label": "Automatski pusti: ",
|
||||
"preferences_continue_label": "Podrazumevano pusti sledeće: ",
|
||||
"preferences_continue_autoplay_label": "Automatski pusti sledeći video snimak: ",
|
||||
"preferences_listen_label": "Podrazumevano uključi samo zvuk: ",
|
||||
"preferences_local_label": "Proksi video snimci: ",
|
||||
"Playlist privacy": "Privatnost plejliste",
|
||||
"Editing playlist `x`": "Izmenjivanje plejliste `x`",
|
||||
"Playlist does not exist.": "Plejlista ne postoji.",
|
||||
"Erroneous challenge": "Pogrešan izazov",
|
||||
"Maltese": "Malteški",
|
||||
"Download": "Preuzmi",
|
||||
"Download as: ": "Preuzmi kao: ",
|
||||
"Bangla": "Bangla/Bengalski",
|
||||
"preferences_quality_dash_label": "Preferirani kvalitet DASH video formata: ",
|
||||
"Token manager": "Upravljanje žetonima",
|
||||
"Token": "Žeton",
|
||||
"Import/export": "Uvezi/Izvezi",
|
||||
"Download as: ": "Preuzeti kao: ",
|
||||
"Bangla": "Bengalski",
|
||||
"preferences_quality_dash_label": "Preferirani DASH kvalitet video snimka: ",
|
||||
"Token manager": "Upravljanje tokenima",
|
||||
"Token": "Token",
|
||||
"Import/export": "Uvoz/izvoz",
|
||||
"revoke": "opozovi",
|
||||
"search": "pretraga",
|
||||
"Log out": "Odjava",
|
||||
"Source available here.": "Izvorna koda je ovde dostupna.",
|
||||
"Source available here.": "Izvorni kôd je dostupan ovde.",
|
||||
"Trending": "U trendu",
|
||||
"Updated `x` ago": "Ažurirano pre `x`",
|
||||
"Delete playlist `x`?": "Obriši plej listu `x`?",
|
||||
"Create playlist": "Napravi plej listu",
|
||||
"Delete playlist `x`?": "Izbrisati plejlistu `x`?",
|
||||
"Create playlist": "Napravi plejlistu",
|
||||
"Show less": "Prikaži manje",
|
||||
"Switch Invidious Instance": "Promeni Invidious instancu",
|
||||
"Hide annotations": "Sakrij napomene",
|
||||
"User ID is a required field": "Korisnički ID je obavezno polje",
|
||||
"User ID is a required field": "ID korisnika je obavezno polje",
|
||||
"Wrong username or password": "Pogrešno korisničko ime ili lozinka",
|
||||
"Please log in": "Molimo vas da se prijavite",
|
||||
"Please log in": "Molimo, prijavite se",
|
||||
"channel:`x`": "kanal:`x`",
|
||||
"Could not fetch comments": "Uzimanje komentara nije uspelo",
|
||||
"Could not create mix.": "Pravljenje miksa nije uspelo.",
|
||||
"Empty playlist": "Prazna plej lista",
|
||||
"Not a playlist.": "Nije plej lista.",
|
||||
"Could not pull trending pages.": "Učitavanje 'U toku' stranica nije uspelo.",
|
||||
"Token is expired, please try again": "Žeton je istekao, molimo vas da pokušate ponovo",
|
||||
"Could not fetch comments": "Nije moguće prikupiti komentare",
|
||||
"Could not create mix.": "Nije moguće napraviti miks.",
|
||||
"Empty playlist": "Prazna plejlista",
|
||||
"Not a playlist.": "Nije plejlista.",
|
||||
"Could not pull trending pages.": "Nije moguće povući stranice „U trendu“.",
|
||||
"Token is expired, please try again": "Token je istekao, pokušajte ponovo",
|
||||
"English (auto-generated)": "Engleski (automatski generisano)",
|
||||
"Afrikaans": "Afrikans",
|
||||
"Albanian": "Albanski",
|
||||
|
@ -95,19 +95,19 @@
|
|||
"Bulgarian": "Bugarski",
|
||||
"Burmese": "Burmanski",
|
||||
"Catalan": "Katalonski",
|
||||
"Cebuano": "Sebuano",
|
||||
"Cebuano": "Cebuanski",
|
||||
"Chinese (Traditional)": "Kineski (Tradicionalni)",
|
||||
"Corsican": "Korzikanski",
|
||||
"Danish": "Danski",
|
||||
"Kannada": "Kanada (Jezik)",
|
||||
"Kannada": "Kanada",
|
||||
"Kazakh": "Kazaški",
|
||||
"Russian": "Ruski",
|
||||
"Scottish Gaelic": "Škotski Gelski",
|
||||
"Sinhala": "Sinhaleški",
|
||||
"Sinhala": "Sinhalski",
|
||||
"Slovak": "Slovački",
|
||||
"Spanish": "Španski",
|
||||
"Spanish (Latin America)": "Španski (Južna Amerika)",
|
||||
"Sundanese": "Sundski",
|
||||
"Spanish (Latin America)": "Španski (Latinska Amerika)",
|
||||
"Sundanese": "Sundanski",
|
||||
"Swedish": "Švedski",
|
||||
"Tajik": "Tadžički",
|
||||
"Telugu": "Telugu",
|
||||
|
@ -116,77 +116,77 @@
|
|||
"Urdu": "Urdu",
|
||||
"Uzbek": "Uzbečki",
|
||||
"Vietnamese": "Vijetnamski",
|
||||
"Rating: ": "Ocena/e: ",
|
||||
"View as playlist": "Pogledaj kao plej listu",
|
||||
"Default": "Podrazumevan/o",
|
||||
"Gaming": "Igrice",
|
||||
"Rating: ": "Ocena: ",
|
||||
"View as playlist": "Pogledaj kao plejlistu",
|
||||
"Default": "Podrazumevano",
|
||||
"Gaming": "Video igre",
|
||||
"Movies": "Filmovi",
|
||||
"%A %B %-d, %Y": "%A %B %-d, %Y",
|
||||
"(edited)": "(izmenjeno)",
|
||||
"YouTube comment permalink": "YouTube komentar trajna veza",
|
||||
"Audio mode": "Audio mod",
|
||||
"Playlists": "Plej liste",
|
||||
"YouTube comment permalink": "Trajni link YouTube komentara",
|
||||
"Audio mode": "Režim audio snimka",
|
||||
"Playlists": "Plejliste",
|
||||
"search_filters_sort_option_relevance": "Relevantnost",
|
||||
"search_filters_sort_option_rating": "Ocene",
|
||||
"search_filters_sort_option_rating": "Ocena",
|
||||
"search_filters_sort_option_date": "Datum otpremanja",
|
||||
"search_filters_sort_option_views": "Broj pregleda",
|
||||
"`x` marked it with a ❤": "`x` je označio/la ovo sa ❤",
|
||||
"`x` marked it with a ❤": "`x` je označio/la sa ❤",
|
||||
"search_filters_duration_label": "Trajanje",
|
||||
"search_filters_features_label": "Karakteristike",
|
||||
"search_filters_date_option_hour": "Poslednji sat",
|
||||
"search_filters_date_option_week": "Ove sedmice",
|
||||
"search_filters_date_option_month": "Ovaj mesec",
|
||||
"search_filters_date_option_week": "Ove nedelje",
|
||||
"search_filters_date_option_month": "Ovog meseca",
|
||||
"search_filters_date_option_year": "Ove godine",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_playlist": "Plej lista",
|
||||
"search_filters_type_option_video": "Video snimak",
|
||||
"search_filters_type_option_playlist": "Plejlista",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_duration_option_long": "Dugo (> 20 minuta)",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_c_commons": "Creative Commons (Licenca)",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_hdr": "Video Visoke Rezolucije",
|
||||
"next_steps_error_message": "Nakon čega bi trebali probati: ",
|
||||
"next_steps_error_message_go_to_youtube": "Idi na YouTube",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"next_steps_error_message": "Nakon toga treba da pokušate da: ",
|
||||
"next_steps_error_message_go_to_youtube": "Odete na YouTube",
|
||||
"footer_documentation": "Dokumentacija",
|
||||
"preferences_region_label": "Država porekla sadržaja: ",
|
||||
"preferences_region_label": "Država sadržaja: ",
|
||||
"preferences_player_style_label": "Stil plejera: ",
|
||||
"preferences_dark_mode_label": "Izgled/Tema: ",
|
||||
"light": "svetlo",
|
||||
"preferences_dark_mode_label": "Tema: ",
|
||||
"light": "svetla",
|
||||
"preferences_thin_mode_label": "Kompaktni režim: ",
|
||||
"preferences_category_misc": "Ostala podešavanja",
|
||||
"preferences_automatic_instance_redirect_label": "Automatsko prebacivanje na drugu instancu u slučaju otkazivanja (preči će nazad na redirect.invidious.io): ",
|
||||
"alphabetically - reverse": "po alfabetu - obrnuto",
|
||||
"Enable web notifications": "Omogući obaveštenja u veb pretraživaču",
|
||||
"`x` is live": "`x` prenosi uživo",
|
||||
"Manage tokens": "Upravljaj žetonima",
|
||||
"preferences_automatic_instance_redirect_label": "Automatsko preusmeravanje instance (povratak na redirect.invidious.io): ",
|
||||
"alphabetically - reverse": "abecedno - obrnuto",
|
||||
"Enable web notifications": "Omogući veb obaveštenja",
|
||||
"`x` is live": "`x` je uživo",
|
||||
"Manage tokens": "Upravljaj tokenima",
|
||||
"Watch history": "Istorija gledanja",
|
||||
"preferences_feed_menu_label": "Dovodna stranica: ",
|
||||
"preferences_feed_menu_label": "Fid meni: ",
|
||||
"preferences_show_nick_label": "Prikaži nadimke na vrhu: ",
|
||||
"CAPTCHA enabled: ": "CAPTCHA omogućena: ",
|
||||
"Registration enabled: ": "Registracija omogućena: ",
|
||||
"Subscription manager": "Upravljanje praćenjima",
|
||||
"Wilson score: ": "Wilsonova ocena: ",
|
||||
"Wilson score: ": "Vilsonova ocena: ",
|
||||
"Engagement: ": "Angažovanje: ",
|
||||
"Whitelisted regions: ": "Dozvoljene oblasti: ",
|
||||
"Shared `x`": "Podeljeno `x`",
|
||||
"Premieres in `x`": "Premera u `x`",
|
||||
"Premieres `x`": "Premere u `x`",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Hej! Izgleda da ste onemogućili JavaScript. Kliknite ovde da vidite komentare, čuvajte na umu da ovo može da potraje duže dok se ne učitaju.",
|
||||
"Whitelisted regions: ": "Dostupni regioni: ",
|
||||
"Shared `x`": "Deljeno `x`",
|
||||
"Premieres in `x`": "Premijera u `x`",
|
||||
"Premieres `x`": "Premijera `x`",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Hej! Izgleda da ste isključili JavaScript. Kliknite ovde da biste videli komentare, imajte na umu da će možda potrajati malo duže da se učitaju.",
|
||||
"View `x` comments": {
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Prikaži `x` komentar",
|
||||
"": "Prikaži `x` komentara"
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Pogledaj `x` komentar",
|
||||
"": "Pogledaj`x` komentare"
|
||||
},
|
||||
"View Reddit comments": "Prikaži Reddit komentare",
|
||||
"View Reddit comments": "Pogledaj Reddit komentare",
|
||||
"CAPTCHA is a required field": "CAPTCHA je obavezno polje",
|
||||
"Croatian": "Hrvatski",
|
||||
"Estonian": "Estonski",
|
||||
"Filipino": "Filipino",
|
||||
"Filipino": "Filipinski",
|
||||
"French": "Francuski",
|
||||
"Galician": "Galicijski",
|
||||
"German": "Nemački",
|
||||
"Greek": "Grčki",
|
||||
"Hausa": "Hausa",
|
||||
"Italian": "Talijanski",
|
||||
"Italian": "Italijanski",
|
||||
"Khmer": "Kmerski",
|
||||
"Kurdish": "Kurdski",
|
||||
"Kyrgyz": "Kirgiski",
|
||||
|
@ -195,68 +195,68 @@
|
|||
"Macedonian": "Makedonski",
|
||||
"Malagasy": "Malgaški",
|
||||
"Malay": "Malajski",
|
||||
"Marathi": "Marathi",
|
||||
"Marathi": "Maratski",
|
||||
"Mongolian": "Mongolski",
|
||||
"Norwegian Bokmål": "Norveški Bokmal",
|
||||
"Nyanja": "Čeva",
|
||||
"Nyanja": "Nijandža",
|
||||
"Pashto": "Paštunski",
|
||||
"Persian": "Persijski",
|
||||
"Punjabi": "Pundžabi",
|
||||
"Punjabi": "Pandžapski",
|
||||
"Romanian": "Rumunski",
|
||||
"Welsh": "Velški",
|
||||
"Western Frisian": "Zapadnofrizijski",
|
||||
"Fallback comments: ": "Komentari u slučaju otkazivanja: ",
|
||||
"Fallback comments: ": "Rezervni komentari: ",
|
||||
"Popular": "Popularno",
|
||||
"Search": "Pretraga",
|
||||
"About": "O programu",
|
||||
"footer_source_code": "Izvorna Koda",
|
||||
"footer_original_source_code": "Originalna Izvorna Koda",
|
||||
"preferences_related_videos_label": "Prikaži slične video klipove: ",
|
||||
"preferences_annotations_label": "Prikaži napomene podrazumevano: ",
|
||||
"preferences_extend_desc_label": "Automatski prikaži ceo opis videa: ",
|
||||
"preferences_vr_mode_label": "Interaktivni video klipovi u 360 stepeni: ",
|
||||
"preferences_category_visual": "Vizuelne preference",
|
||||
"preferences_captions_label": "Podrazumevani titl: ",
|
||||
"About": "O sajtu",
|
||||
"footer_source_code": "Izvorni kôd",
|
||||
"footer_original_source_code": "Originalni izvorni kôd",
|
||||
"preferences_related_videos_label": "Prikaži povezane video snimke: ",
|
||||
"preferences_annotations_label": "Podrazumevano prikaži napomene: ",
|
||||
"preferences_extend_desc_label": "Automatski proširi opis video snimka: ",
|
||||
"preferences_vr_mode_label": "Interaktivni video snimci od 360 stepeni (zahteva WebGl): ",
|
||||
"preferences_category_visual": "Vizuelna podešavanja",
|
||||
"preferences_captions_label": "Podrazumevani titlovi: ",
|
||||
"Music": "Muzika",
|
||||
"search_filters_type_label": "Tip",
|
||||
"search_filters_type_label": "Vrsta",
|
||||
"Tamil": "Tamilski",
|
||||
"Save preferences": "Sačuvaj podešavanja",
|
||||
"Only show latest unwatched video from channel: ": "Prikaži samo poslednje video klipove koji nisu pogledani sa kanala: ",
|
||||
"Xhosa": "Kosa (Jezik)",
|
||||
"Only show latest unwatched video from channel: ": "Prikaži samo najnoviji neodgledani video snimak sa kanala: ",
|
||||
"Xhosa": "Kosa (Khosa)",
|
||||
"search_filters_type_option_channel": "Kanal",
|
||||
"Hungarian": "Mađarski",
|
||||
"Maori": "Maori (Jezik)",
|
||||
"Manage subscriptions": "Upravljaj zapisima",
|
||||
"Maori": "Maorski",
|
||||
"Manage subscriptions": "Upravljaj praćenjima",
|
||||
"Hindi": "Hindi",
|
||||
"`x` ago": "pre `x`",
|
||||
"Import/export data": "Uvezi/Izvezi podatke",
|
||||
"`x` uploaded a video": "`x` je otpremio/la video klip",
|
||||
"Delete account": "Obriši nalog",
|
||||
"`x` uploaded a video": "`x` je otpremio/la video snimak",
|
||||
"Delete account": "Izbriši nalog",
|
||||
"preferences_default_home_label": "Podrazumevana početna stranica: ",
|
||||
"Serbian": "Srpski",
|
||||
"License: ": "Licenca: ",
|
||||
"search_filters_features_option_live": "Uživo",
|
||||
"Report statistics: ": "Izveštavaj o statistici: ",
|
||||
"Only show latest video from channel: ": "Prikazuj poslednje video klipove samo sa kanala: ",
|
||||
"Report statistics: ": "Izveštavaj statistike: ",
|
||||
"Only show latest video from channel: ": "Prikaži samo najnoviji video snimak sa kanala: ",
|
||||
"channel name - reverse": "ime kanala - obrnuto",
|
||||
"Could not get channel info.": "Uzimanje podataka o kanalu nije uspelo.",
|
||||
"View privacy policy.": "Pogledaj izveštaj o privatnosti.",
|
||||
"Could not get channel info.": "Nije moguće prikupiti informacije o kanalu.",
|
||||
"View privacy policy.": "Pogledaj politiku privatnosti.",
|
||||
"Change password": "Promeni lozinku",
|
||||
"Malayalam": "Malajalam",
|
||||
"View more comments on Reddit": "Prikaži više komentara na Reddit-u",
|
||||
"Malayalam": "Malajalamski",
|
||||
"View more comments on Reddit": "Pogledaj više komentara na Reddit-u",
|
||||
"Portuguese": "Portugalski",
|
||||
"View YouTube comments": "Prikaži YouTube komentare",
|
||||
"View YouTube comments": "Pogledaj YouTube komentare",
|
||||
"published - reverse": "objavljeno - obrnuto",
|
||||
"Dutch": "Holandski",
|
||||
"preferences_volume_label": "Jačina zvuka: ",
|
||||
"preferences_volume_label": "Jačina zvuka plejera: ",
|
||||
"preferences_locale_label": "Jezik: ",
|
||||
"adminprefs_modified_source_code_url_label": "URL veza do skladišta sa Izmenjenom Izvornom Kodom",
|
||||
"adminprefs_modified_source_code_url_label": "URL adresa do repozitorijuma izmenjenog izvornog koda",
|
||||
"channel_tab_community_label": "Zajednica",
|
||||
"Video mode": "Video mod",
|
||||
"Fallback captions: ": "Titl u slučaju da glavni nije dostupan: ",
|
||||
"Video mode": "Režim video snimka",
|
||||
"Fallback captions: ": "Rezervni titlovi: ",
|
||||
"Private": "Privatno",
|
||||
"alphabetically": "po alfabetu",
|
||||
"No such user": "Nepostojeći korisnik",
|
||||
"alphabetically": "abecedno",
|
||||
"No such user": "Ne postoji korisnik",
|
||||
"Subscriptions": "Praćenja",
|
||||
"search_filters_date_option_today": "Danas",
|
||||
"Finnish": "Finski",
|
||||
|
@ -265,30 +265,30 @@
|
|||
"Shona": "Šona",
|
||||
"search_filters_features_option_location": "Lokacija",
|
||||
"Load more": "Učitaj više",
|
||||
"Released under the AGPLv3 on Github.": "Izbačeno pod licencom AGPLv3 na GitHub-u.",
|
||||
"Released under the AGPLv3 on Github.": "Objavljeno pod licencom AGPLv3 na GitHub-u.",
|
||||
"Slovenian": "Slovenački",
|
||||
"View JavaScript license information.": "Pogledaj informacije licence vezane za JavaScript.",
|
||||
"View JavaScript license information.": "Pogledaj informacije o JavaScript licenci.",
|
||||
"Chinese (Simplified)": "Kineski (Pojednostavljeni)",
|
||||
"preferences_comments_label": "Podrazumevani komentari: ",
|
||||
"Incorrect password": "Netačna lozinka",
|
||||
"Show replies": "Prikaži odgovore",
|
||||
"Invidious Private Feed for `x`": "Invidious Privatni Dovod za `x`",
|
||||
"Invidious Private Feed for `x`": "Invidious privatni fid za `x`",
|
||||
"Watch on YouTube": "Gledaj na YouTube-u",
|
||||
"Wrong answer": "Pogrešan odgovor",
|
||||
"preferences_quality_label": "Preferirani video kvalitet: ",
|
||||
"preferences_quality_label": "Preferirani kvalitet video snimka: ",
|
||||
"Hide replies": "Sakrij odgovore",
|
||||
"Erroneous CAPTCHA": "Pogrešna CAPTCHA",
|
||||
"Erroneous token": "Pogrešan žeton",
|
||||
"Erroneous token": "Pogrešan token",
|
||||
"Czech": "Češki",
|
||||
"Latin": "Latinski",
|
||||
"channel_tab_videos_label": "Video klipovi",
|
||||
"channel_tab_videos_label": "Video snimci",
|
||||
"search_filters_features_option_four_k": "4К",
|
||||
"footer_donate_page": "Doniraj",
|
||||
"English": "Engleski",
|
||||
"Arabic": "Arapski",
|
||||
"Unlisted": "Nenavedeno",
|
||||
"Hidden field \"challenge\" is a required field": "Sakriveno \"challenge\" polje je obavezno",
|
||||
"Hidden field \"token\" is a required field": "Sakriveno \"token\" polje je obavezno",
|
||||
"Unlisted": "Po pozivu",
|
||||
"Hidden field \"challenge\" is a required field": "Skriveno polje „izazov“ je obavezno polje",
|
||||
"Hidden field \"token\" is a required field": "Skriveno polje „token“ je obavezno polje",
|
||||
"Georgian": "Gruzijski",
|
||||
"Hawaiian": "Havajski",
|
||||
"Hebrew": "Hebrejski",
|
||||
|
@ -297,68 +297,212 @@
|
|||
"Japanese": "Japanski",
|
||||
"Javanese": "Javanski",
|
||||
"Sindhi": "Sindi",
|
||||
"Swahili": "Svahili",
|
||||
"Swahili": "Suvali",
|
||||
"Yiddish": "Jidiš",
|
||||
"Zulu": "Zulu",
|
||||
"search_filters_features_option_subtitles": "Titl/Prevod",
|
||||
"Password cannot be longer than 55 characters": "Lozinka ne može biti duža od 55 karaktera",
|
||||
"search_filters_features_option_subtitles": "Titlovi/Skriveni titlovi",
|
||||
"Password cannot be longer than 55 characters": "Lozinka ne može biti duža od 55 znakova",
|
||||
"This channel does not exist.": "Ovaj kanal ne postoji.",
|
||||
"Belarusian": "Beloruski",
|
||||
"Gujarati": "Gudžarati",
|
||||
"Haitian Creole": "Haićanski Kreolski",
|
||||
"Somali": "Somalijski",
|
||||
"Top": "Vrh",
|
||||
"footer_modfied_source_code": "Izmenjena Izvorna Koda",
|
||||
"Top": "Top",
|
||||
"footer_modfied_source_code": "Izmenjeni izvorni kôd",
|
||||
"preferences_category_subscription": "Podešavanja praćenja",
|
||||
"preferences_annotations_subscribed_label": "Podrazumevano prikazati napomene za kanale koje pratite? ",
|
||||
"preferences_max_results_label": "Broj video klipova prikazanih u dovodnoj listi: ",
|
||||
"preferences_sort_label": "Sortiraj video klipove po: ",
|
||||
"preferences_unseen_only_label": "Prikaži samo video klipove koji nisu pogledani: ",
|
||||
"preferences_notifications_only_label": "Prikaži samo obaveštenja (ako ih uopšte ima): ",
|
||||
"preferences_max_results_label": "Broj video snimaka prikazanih u fidu: ",
|
||||
"preferences_sort_label": "Sortiraj video snimke po: ",
|
||||
"preferences_unseen_only_label": "Prikaži samo neodgledano: ",
|
||||
"preferences_notifications_only_label": "Prikaži samo obaveštenja (ako ih ima): ",
|
||||
"preferences_category_data": "Podešavanja podataka",
|
||||
"Clear watch history": "Obriši istoriju gledanja",
|
||||
"preferences_category_admin": "Administratorska podešavanja",
|
||||
"Clear watch history": "Očisti istoriju gledanja",
|
||||
"preferences_category_admin": "Podešavanja administratora",
|
||||
"published": "objavljeno",
|
||||
"search_filters_sort_label": "Poredaj prema",
|
||||
"search_filters_sort_label": "Sortiranje po",
|
||||
"search_filters_type_option_show": "Emisija",
|
||||
"search_filters_duration_option_short": "Kratko (< 4 minute)",
|
||||
"search_filters_duration_option_short": "Kratko (< 4 minuta)",
|
||||
"Current version: ": "Trenutna verzija: ",
|
||||
"Top enabled: ": "Vrh omogućen: ",
|
||||
"Top enabled: ": "Top omogućeno: ",
|
||||
"Public": "Javno",
|
||||
"Delete playlist": "Obriši plej listu",
|
||||
"Delete playlist": "Izbriši plejlistu",
|
||||
"Title": "Naslov",
|
||||
"Show annotations": "Prikaži napomene",
|
||||
"Password cannot be empty": "Lozinka ne može biti prazna",
|
||||
"Deleted or invalid channel": "Obrisan ili nepostojeći kanal",
|
||||
"Deleted or invalid channel": "Izbrisan ili nevažeći kanal",
|
||||
"Esperanto": "Esperanto",
|
||||
"Hmong": "Hmong",
|
||||
"Luxembourgish": "Luksemburški",
|
||||
"Nepali": "Nepalski",
|
||||
"Samoan": "Samoanski",
|
||||
"News": "Vesti",
|
||||
"permalink": "trajna veza",
|
||||
"permalink": "trajni link",
|
||||
"Password is a required field": "Lozinka je obavezno polje",
|
||||
"Amharic": "Amharski",
|
||||
"Indonesian": "Indonežanski",
|
||||
"Indonesian": "Indonezijski",
|
||||
"Irish": "Irski",
|
||||
"Korean": "Korejski",
|
||||
"Southern Sotho": "Južni Soto",
|
||||
"Thai": "Tajski",
|
||||
"preferences_speed_label": "Podrazumevana brzina: ",
|
||||
"Dark mode: ": "Tamni režim: ",
|
||||
"dark": "tamno",
|
||||
"Redirect homepage to feed: ": "Prebaci sa početne stranice na dovodnu listu: ",
|
||||
"dark": "tamna",
|
||||
"Redirect homepage to feed: ": "Preusmeri početnu stranicu na fid: ",
|
||||
"channel name": "ime kanala",
|
||||
"View all playlists": "Pregledaj sve plej liste",
|
||||
"View all playlists": "Pogledaj sve plejliste",
|
||||
"Show more": "Prikaži više",
|
||||
"Genre: ": "Žanr: ",
|
||||
"Family friendly? ": "Pogodno za porodicu? ",
|
||||
"next_steps_error_message_refresh": "Osveži stranicu",
|
||||
"next_steps_error_message_refresh": "Osvežite",
|
||||
"youtube": "YouTube",
|
||||
"reddit": "Reddit",
|
||||
"unsubscribe": "prekini sa praćenjem",
|
||||
"Blacklisted regions: ": "Zabranjene oblasti: ",
|
||||
"unsubscribe": "prekini praćenje",
|
||||
"Blacklisted regions: ": "Nedostupni regioni: ",
|
||||
"Polish": "Poljski",
|
||||
"Yoruba": "Joruba",
|
||||
"search_filters_title": "Filter"
|
||||
"search_filters_title": "Filteri",
|
||||
"Korean (auto-generated)": "Korejski (automatski generisano)",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"preferences_quality_dash_option_worst": "Najgore",
|
||||
"channel_tab_podcasts_label": "Podkasti",
|
||||
"preferences_save_player_pos_label": "Sačuvaj poziciju reprodukcije: ",
|
||||
"Spanish (Mexico)": "Španski (Meksiko)",
|
||||
"generic_subscriptions_count_0": "{{count}} praćenje",
|
||||
"generic_subscriptions_count_1": "{{count}} praćenja",
|
||||
"generic_subscriptions_count_2": "{{count}} praćenja",
|
||||
"search_filters_apply_button": "Primeni izabrane filtere",
|
||||
"Download is disabled": "Preuzimanje je onemogućeno",
|
||||
"comments_points_count_0": "{{count}} poen",
|
||||
"comments_points_count_1": "{{count}} poena",
|
||||
"comments_points_count_2": "{{count}} poena",
|
||||
"preferences_quality_dash_option_2160p": "2160p",
|
||||
"German (auto-generated)": "Nemački (automatski generisano)",
|
||||
"Japanese (auto-generated)": "Japanski (automatski generisano)",
|
||||
"preferences_quality_option_medium": "Srednje",
|
||||
"search_message_change_filters_or_query": "Pokušajte da proširite upit za pretragu i/ili promenite filtere.",
|
||||
"crash_page_before_reporting": "Pre nego što prijavite grešku, uverite se da ste:",
|
||||
"preferences_quality_dash_option_best": "Najbolje",
|
||||
"Channel Sponsor": "Sponzor kanala",
|
||||
"generic_videos_count_0": "{{count}} video snimak",
|
||||
"generic_videos_count_1": "{{count}} video snimka",
|
||||
"generic_videos_count_2": "{{count}} video snimaka",
|
||||
"videoinfo_started_streaming_x_ago": "Započeto strimovanje pre `x`",
|
||||
"videoinfo_youTube_embed_link": "Ugrađeno",
|
||||
"channel_tab_streams_label": "Strimovi uživo",
|
||||
"playlist_button_add_items": "Dodaj video snimke",
|
||||
"generic_count_minutes_0": "{{count}} minut",
|
||||
"generic_count_minutes_1": "{{count}} minuta",
|
||||
"generic_count_minutes_2": "{{count}} minuta",
|
||||
"preferences_quality_dash_option_720p": "720p",
|
||||
"preferences_watch_history_label": "Omogući istoriju gledanja: ",
|
||||
"user_saved_playlists": "Sačuvanih plejlista: `x`",
|
||||
"Spanish (Spain)": "Španski (Španija)",
|
||||
"invidious": "Invidious",
|
||||
"crash_page_refresh": "pokušali da <a href=\"`x`\">osvežite stranicu</a>",
|
||||
"Chinese (Hong Kong)": "Kineski (Hong Kong)",
|
||||
"Artist: ": "Izvođač: ",
|
||||
"generic_count_months_0": "{{count}} mesec",
|
||||
"generic_count_months_1": "{{count}} meseca",
|
||||
"generic_count_months_2": "{{count}} meseci",
|
||||
"search_message_use_another_instance": " Takođe, možete <a href=\"`x`\">pretraživati na drugoj instanci</a>.",
|
||||
"generic_subscribers_count_0": "{{count}} pratilac",
|
||||
"generic_subscribers_count_1": "{{count}} pratioca",
|
||||
"generic_subscribers_count_2": "{{count}} pratilaca",
|
||||
"download_subtitles": "Titlovi - `x` (.vtt)",
|
||||
"generic_button_save": "Sačuvaj",
|
||||
"crash_page_search_issue": "pretražili <a href=\"`x`\">postojeće izveštaje o problemima na GitHub-u</a>",
|
||||
"generic_button_cancel": "Otkaži",
|
||||
"none": "nijedno",
|
||||
"English (United States)": "Engleski (Sjedinjene Američke Države)",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} neviđeno obaveštenje",
|
||||
"subscriptions_unseen_notifs_count_1": "{{count}} neviđena obaveštenja",
|
||||
"subscriptions_unseen_notifs_count_2": "{{count}} neviđenih obaveštenja",
|
||||
"Album: ": "Album: ",
|
||||
"preferences_quality_option_dash": "DASH (adaptivni kvalitet)",
|
||||
"preferences_quality_dash_option_1080p": "1080p",
|
||||
"Video unavailable": "Video snimak nedostupan",
|
||||
"tokens_count_0": "{{count}} token",
|
||||
"tokens_count_1": "{{count}} tokena",
|
||||
"tokens_count_2": "{{count}} tokena",
|
||||
"Chinese (China)": "Kineski (Kina)",
|
||||
"Italian (auto-generated)": "Italijanski (automatski generisano)",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"preferences_quality_dash_option_1440p": "1440p",
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
"search_message_no_results": "Nisu pronađeni rezultati.",
|
||||
"channel_tab_releases_label": "Izdanja",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"Interlingue": "Interlingva",
|
||||
"Song: ": "Pesma: ",
|
||||
"generic_channels_count_0": "{{count}} kanal",
|
||||
"generic_channels_count_1": "{{count}} kanala",
|
||||
"generic_channels_count_2": "{{count}} kanala",
|
||||
"Chinese (Taiwan)": "Kineski (Tajvan)",
|
||||
"Turkish (auto-generated)": "Turski (automatski generisano)",
|
||||
"Indonesian (auto-generated)": "Indonezijski (automatski generisano)",
|
||||
"Portuguese (auto-generated)": "Portugalski (automatski generisano)",
|
||||
"generic_count_years_0": "{{count}} godina",
|
||||
"generic_count_years_1": "{{count}} godine",
|
||||
"generic_count_years_2": "{{count}} godina",
|
||||
"videoinfo_invidious_embed_link": "Ugrađeni link",
|
||||
"Popular enabled: ": "Popularno omogućeno: ",
|
||||
"Spanish (auto-generated)": "Španski (automatski generisano)",
|
||||
"preferences_quality_option_small": "Malo",
|
||||
"English (United Kingdom)": "Engleski (Ujedinjeno Kraljevstvo)",
|
||||
"channel_tab_playlists_label": "Plejliste",
|
||||
"generic_button_edit": "Izmeni",
|
||||
"generic_playlists_count_0": "{{count}} plejlista",
|
||||
"generic_playlists_count_1": "{{count}} plejliste",
|
||||
"generic_playlists_count_2": "{{count}} plejlista",
|
||||
"preferences_quality_option_hd720": "HD720",
|
||||
"search_filters_features_option_purchased": "Kupljeno",
|
||||
"search_filters_date_option_none": "Bilo koji datum",
|
||||
"preferences_quality_dash_option_auto": "Automatski",
|
||||
"Cantonese (Hong Kong)": "Kantonski (Hong Kong)",
|
||||
"crash_page_report_issue": "Ako ništa od gorenavedenog nije pomoglo, <a href=\"`x`\">otvorite novi izveštaj o problemu na GitHub-u</a> (po mogućnosti na engleskom) i uključite sledeći tekst u svoju poruku (NE prevodite taj tekst):",
|
||||
"crash_page_switch_instance": "pokušali da <a href=\"`x`\">koristite drugu instancu</a>",
|
||||
"generic_count_weeks_0": "{{count}} nedelja",
|
||||
"generic_count_weeks_1": "{{count}} nedelje",
|
||||
"generic_count_weeks_2": "{{count}} nedelja",
|
||||
"videoinfo_watch_on_youTube": "Gledaj na YouTube-u",
|
||||
"Music in this video": "Muzika u ovom video snimku",
|
||||
"generic_button_rss": "RSS",
|
||||
"preferences_quality_dash_option_4320p": "4320p",
|
||||
"generic_count_hours_0": "{{count}} sat",
|
||||
"generic_count_hours_1": "{{count}} sata",
|
||||
"generic_count_hours_2": "{{count}} sati",
|
||||
"French (auto-generated)": "Francuski (automatski generisano)",
|
||||
"crash_page_read_the_faq": "pročitali <a href=\"`x`\">Često Postavljana Pitanja (ČPP)</a>",
|
||||
"user_created_playlists": "Napravljenih plejlista: `x`",
|
||||
"channel_tab_channels_label": "Kanali",
|
||||
"search_filters_type_option_all": "Bilo koja vrsta",
|
||||
"Russian (auto-generated)": "Ruski (automatski generisano)",
|
||||
"preferences_quality_dash_option_480p": "480p",
|
||||
"comments_view_x_replies_0": "Pogledaj {{count}} odgovor",
|
||||
"comments_view_x_replies_1": "Pogledaj {{count}} odgovora",
|
||||
"comments_view_x_replies_2": "Pogledaj {{count}} odgovora",
|
||||
"Portuguese (Brazil)": "Portugalski (Brazil)",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"error_video_not_in_playlist": "Traženi video snimak ne postoji na ovoj plejlisti. <a href=\"`x`\">Kliknite ovde za početnu stranicu plejliste.</a>",
|
||||
"Dutch (auto-generated)": "Holandski (automatski generisano)",
|
||||
"generic_count_days_0": "{{count}} dan",
|
||||
"generic_count_days_1": "{{count}} dana",
|
||||
"generic_count_days_2": "{{count}} dana",
|
||||
"Vietnamese (auto-generated)": "Vijetnamski (automatski generisano)",
|
||||
"search_filters_duration_option_none": "Bilo koje trajanje",
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"Chinese": "Kineski",
|
||||
"generic_button_delete": "Izbriši",
|
||||
"Import YouTube playlist (.csv)": "Uvezi YouTube plejlistu (.csv)",
|
||||
"Standard YouTube license": "Standardna YouTube licenca",
|
||||
"search_filters_duration_option_medium": "Srednje (4 - 20 minuta)",
|
||||
"generic_count_seconds_0": "{{count}} sekunda",
|
||||
"generic_count_seconds_1": "{{count}} sekunde",
|
||||
"generic_count_seconds_2": "{{count}} sekundi",
|
||||
"search_filters_date_label": "Datum otpremanja",
|
||||
"crash_page_you_found_a_bug": "Izgleda da ste pronašli grešku u Invidious-u!",
|
||||
"generic_views_count_0": "{{count}} pregled",
|
||||
"generic_views_count_1": "{{count}} pregleda",
|
||||
"generic_views_count_2": "{{count}} pregleda",
|
||||
"Import YouTube watch history (.json)": "Uvezi YouTube istoriju gledanja (.json)"
|
||||
}
|
||||
|
|
|
@ -1,166 +1,166 @@
|
|||
{
|
||||
"LIVE": "УЖИВО",
|
||||
"Shared `x` ago": "Подељено пре `x`",
|
||||
"Shared `x` ago": "Дељено пре `x`",
|
||||
"Unsubscribe": "Прекини праћење",
|
||||
"Subscribe": "Прати",
|
||||
"Subscribe": "Запрати",
|
||||
"View channel on YouTube": "Погледај канал на YouTube-у",
|
||||
"View playlist on YouTube": "Погледај списак извођења на YоуТубе-у",
|
||||
"View playlist on YouTube": "Погледај плејлисту на YouTube-у",
|
||||
"newest": "најновије",
|
||||
"oldest": "најстарије",
|
||||
"popular": "популарно",
|
||||
"last": "последње",
|
||||
"Next page": "Следећа страна",
|
||||
"Previous page": "Претходна страна",
|
||||
"Clear watch history?": "Избрисати повест прегледања?",
|
||||
"Next page": "Следећа страница",
|
||||
"Previous page": "Претходна страница",
|
||||
"Clear watch history?": "Очистити историју гледања?",
|
||||
"New password": "Нова лозинка",
|
||||
"New passwords must match": "Нове лозинке морају бити истоветне",
|
||||
"Authorize token?": "Овласти жетон?",
|
||||
"Authorize token for `x`?": "Овласти жетон за `x`?",
|
||||
"New passwords must match": "Нове лозинке морају да се подударају",
|
||||
"Authorize token?": "Ауторизовати токен?",
|
||||
"Authorize token for `x`?": "Ауторизовати токен за `x`?",
|
||||
"Yes": "Да",
|
||||
"No": "Не",
|
||||
"Import and Export Data": "Увоз и извоз података",
|
||||
"Import": "Увези",
|
||||
"Import Invidious data": "Увези податке са Individious-а",
|
||||
"Import YouTube subscriptions": "Увези праћења са YouTube-а",
|
||||
"Import FreeTube subscriptions (.db)": "Увези праћења са FreeTube-а (.db)",
|
||||
"Import NewPipe subscriptions (.json)": "Увези праћења са NewPipe-а (.json)",
|
||||
"Import NewPipe data (.zip)": "Увези податке са NewPipe-a (.zip)",
|
||||
"Import Invidious data": "Увези Invidious JSON податке",
|
||||
"Import YouTube subscriptions": "Увези YouTube/OPML праћења",
|
||||
"Import FreeTube subscriptions (.db)": "Увези FreeTube праћења (.db)",
|
||||
"Import NewPipe subscriptions (.json)": "Увези NewPipe праћења (.json)",
|
||||
"Import NewPipe data (.zip)": "Увези NewPipe податке (.zip)",
|
||||
"Export": "Извези",
|
||||
"Export subscriptions as OPML": "Извези праћења као ОПМЛ датотеку",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Извези праћења као ОПМЛ датотеку (за NewPipe и FreeTube)",
|
||||
"Export data as JSON": "Извези податке као JSON датотеку",
|
||||
"Delete account?": "Избришите налог?",
|
||||
"Export subscriptions as OPML": "Извези праћења као OPML",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Извези праћења као OPML (за NewPipe и FreeTube)",
|
||||
"Export data as JSON": "Извези Invidious податке као JSON",
|
||||
"Delete account?": "Избрисати налог?",
|
||||
"History": "Историја",
|
||||
"An alternative front-end to YouTube": "Заменски кориснички слој за YouTube",
|
||||
"JavaScript license information": "Извештај о JavaScript одобрењу",
|
||||
"An alternative front-end to YouTube": "Алтернативни фронт-енд за YouTube",
|
||||
"JavaScript license information": "Информације о JavaScript лиценци",
|
||||
"source": "извор",
|
||||
"Log in": "Пријави се",
|
||||
"Log in/register": "Пријави се/Отворите налог",
|
||||
"User ID": "Кориснички ИД",
|
||||
"Log in": "Пријава",
|
||||
"Log in/register": "Пријава/регистрација",
|
||||
"User ID": "ID корисника",
|
||||
"Password": "Лозинка",
|
||||
"Time (h:mm:ss):": "Време (ч:мм:сс):",
|
||||
"Text CAPTCHA": "Знаковни ЦАПТЧА",
|
||||
"Image CAPTCHA": "Сликовни CAPTCHA",
|
||||
"Text CAPTCHA": "Текст CAPTCHA",
|
||||
"Image CAPTCHA": "Слика CAPTCHA",
|
||||
"Sign In": "Пријава",
|
||||
"Register": "Отвори налог",
|
||||
"E-mail": "Е-пошта",
|
||||
"Register": "Регистрација",
|
||||
"E-mail": "Имејл",
|
||||
"Preferences": "Подешавања",
|
||||
"preferences_category_player": "Подешавања репродуктора",
|
||||
"preferences_category_player": "Подешавања плејера",
|
||||
"preferences_video_loop_label": "Увек понављај: ",
|
||||
"preferences_autoplay_label": "Самопуштање: ",
|
||||
"preferences_continue_label": "Увек подразумевано пуштај следеће: ",
|
||||
"preferences_continue_autoplay_label": "Самопуштање следећег видео записа: ",
|
||||
"preferences_listen_label": "Увек подразумевано укључен само звук: ",
|
||||
"preferences_local_label": "Приказ видео записа преко посредника: ",
|
||||
"preferences_autoplay_label": "Аутоматски пусти: ",
|
||||
"preferences_continue_label": "Подразумевано пусти следеће: ",
|
||||
"preferences_continue_autoplay_label": "Аутоматски пусти следећи видео снимак: ",
|
||||
"preferences_listen_label": "Подразумевано укључи само звук: ",
|
||||
"preferences_local_label": "Прокси видео снимци: ",
|
||||
"preferences_speed_label": "Подразумевана брзина: ",
|
||||
"preferences_quality_label": "Преферирани видео квалитет: ",
|
||||
"preferences_volume_label": "Јачина звука: ",
|
||||
"preferences_quality_label": "Преферирани квалитет видео снимка: ",
|
||||
"preferences_volume_label": "Јачина звука плејера: ",
|
||||
"preferences_comments_label": "Подразумевани коментари: ",
|
||||
"youtube": "YouTube",
|
||||
"reddit": "Reddit",
|
||||
"preferences_captions_label": "Подразумевани титл: ",
|
||||
"Fallback captions: ": "Титл у случају да главни није доступан: ",
|
||||
"preferences_related_videos_label": "Прикажи сличне видео клипове: ",
|
||||
"preferences_annotations_label": "Прикажи напомене подразумевано: ",
|
||||
"preferences_category_visual": "Визуелне преференце",
|
||||
"preferences_captions_label": "Подразумевани титлови: ",
|
||||
"Fallback captions: ": "Резервни титлови: ",
|
||||
"preferences_related_videos_label": "Прикажи повезане видео снимке: ",
|
||||
"preferences_annotations_label": "Подразумевано прикажи напомене: ",
|
||||
"preferences_category_visual": "Визуелна подешавања",
|
||||
"preferences_player_style_label": "Стил плејера: ",
|
||||
"Dark mode: ": "Тамни режим: ",
|
||||
"preferences_dark_mode_label": "Изглед/Тема: ",
|
||||
"dark": "тамно",
|
||||
"light": "светло",
|
||||
"preferences_dark_mode_label": "Тема: ",
|
||||
"dark": "тамна",
|
||||
"light": "светла",
|
||||
"preferences_thin_mode_label": "Компактни режим: ",
|
||||
"preferences_category_subscription": "Подешавања праћења",
|
||||
"preferences_annotations_subscribed_label": "Подразумевано приказати напомене за канале које пратите? ",
|
||||
"Redirect homepage to feed: ": "Пребаци са почетне странице на доводну листу: ",
|
||||
"preferences_max_results_label": "Број видео клипова приказаних у доводној листи: ",
|
||||
"preferences_sort_label": "Сортирај видео клипове по: ",
|
||||
"Redirect homepage to feed: ": "Преусмери почетну страницу на фид: ",
|
||||
"preferences_max_results_label": "Број видео снимака приказаних у фиду: ",
|
||||
"preferences_sort_label": "Сортирај видео снимке по: ",
|
||||
"published": "објављено",
|
||||
"published - reverse": "објављено - обрнуто",
|
||||
"alphabetically": "по алфабету",
|
||||
"alphabetically - reverse": "по алфабету - обрнуто",
|
||||
"alphabetically": "абецедно",
|
||||
"alphabetically - reverse": "абецедно - обрнуто",
|
||||
"channel name": "име канала",
|
||||
"channel name - reverse": "име канала - обрнуто",
|
||||
"Only show latest video from channel: ": "Приказуј последње видео клипове само са канала: ",
|
||||
"Only show latest unwatched video from channel: ": "Прикажи само последње видео клипове који нису погледани са канала: ",
|
||||
"preferences_unseen_only_label": "Прикажи само видео клипове који нису погледани: ",
|
||||
"preferences_notifications_only_label": "Прикажи само обавештења (ако их уопште има): ",
|
||||
"Enable web notifications": "Омогући обавештења у веб претраживачу",
|
||||
"`x` uploaded a video": "`x` је отпремио/ла видео клип",
|
||||
"`x` is live": "`x` преноси уживо",
|
||||
"Only show latest video from channel: ": "Прикажи само најновији видео снимак са канала: ",
|
||||
"Only show latest unwatched video from channel: ": "Прикажи само најновији неодгледани видео снимак са канала: ",
|
||||
"preferences_unseen_only_label": "Прикажи само недогледано: ",
|
||||
"preferences_notifications_only_label": "Прикажи само обавештења (ако их има): ",
|
||||
"Enable web notifications": "Омогући веб обавештења",
|
||||
"`x` uploaded a video": "`x` је отпремио/ла видео снимак",
|
||||
"`x` is live": "`x` је уживо",
|
||||
"preferences_category_data": "Подешавања података",
|
||||
"Clear watch history": "Обриши историју гледања",
|
||||
"Clear watch history": "Очисти историју гледања",
|
||||
"Import/export data": "Увези/Извези податке",
|
||||
"Change password": "Промени лозинку",
|
||||
"Manage subscriptions": "Управљај записима",
|
||||
"Manage tokens": "Управљај жетонима",
|
||||
"Manage subscriptions": "Управљај праћењима",
|
||||
"Manage tokens": "Управљај токенима",
|
||||
"Watch history": "Историја гледања",
|
||||
"Delete account": "Обриши налог",
|
||||
"preferences_category_admin": "Администраторска подешавања",
|
||||
"Delete account": "Избриши налог",
|
||||
"preferences_category_admin": "Подешавања администратора",
|
||||
"preferences_default_home_label": "Подразумевана почетна страница: ",
|
||||
"preferences_feed_menu_label": "Доводна страница: ",
|
||||
"preferences_feed_menu_label": "Фид мени: ",
|
||||
"CAPTCHA enabled: ": "CAPTCHA омогућена: ",
|
||||
"Login enabled: ": "Пријава омогућена: ",
|
||||
"Registration enabled: ": "Регистрација омогућена: ",
|
||||
"Save preferences": "Сачувај подешавања",
|
||||
"Subscription manager": "Управљање праћењима",
|
||||
"Token manager": "Управљање жетонима",
|
||||
"Token": "Жетон",
|
||||
"Import/export": "Увези/Извези",
|
||||
"unsubscribe": "прекини са праћењем",
|
||||
"Token manager": "Управљање токенима",
|
||||
"Token": "Токен",
|
||||
"Import/export": "Увоз/извоз",
|
||||
"unsubscribe": "прекини праћење",
|
||||
"revoke": "опозови",
|
||||
"Subscriptions": "Праћења",
|
||||
"search": "претрага",
|
||||
"Log out": "Одјава",
|
||||
"Source available here.": "Изворна кода је овде доступна.",
|
||||
"View JavaScript license information.": "Погледај информације лиценце везане за JavaScript.",
|
||||
"View privacy policy.": "Погледај извештај о приватности.",
|
||||
"Source available here.": "Изворни кôд је доступан овде.",
|
||||
"View JavaScript license information.": "Погледај информације о JavaScript лиценци.",
|
||||
"View privacy policy.": "Погледај политику приватности.",
|
||||
"Trending": "У тренду",
|
||||
"Public": "Јавно",
|
||||
"Unlisted": "Ненаведено",
|
||||
"Unlisted": "По позиву",
|
||||
"Private": "Приватно",
|
||||
"View all playlists": "Прегледај све плеј листе",
|
||||
"View all playlists": "Погледај све плејлисте",
|
||||
"Updated `x` ago": "Ажурирано пре `x`",
|
||||
"Delete playlist `x`?": "Обриши плеј листу `x`?",
|
||||
"Delete playlist": "Обриши плеј листу",
|
||||
"Create playlist": "Направи плеј листу",
|
||||
"Delete playlist `x`?": "Избрисати плејлисту `x`?",
|
||||
"Delete playlist": "Избриши плејлисту",
|
||||
"Create playlist": "Направи плејлисту",
|
||||
"Title": "Наслов",
|
||||
"Playlist privacy": "Подешавања приватности плеј листе",
|
||||
"Editing playlist `x`": "Измена плеј листе `x`",
|
||||
"Playlist privacy": "Приватност плејлисте",
|
||||
"Editing playlist `x`": "Измењивање плејлисте `x`",
|
||||
"Watch on YouTube": "Гледај на YouTube-у",
|
||||
"Hide annotations": "Сакриј напомене",
|
||||
"Show annotations": "Прикажи напомене",
|
||||
"Genre: ": "Жанр: ",
|
||||
"License: ": "Лиценца: ",
|
||||
"Engagement: ": "Ангажовање: ",
|
||||
"Whitelisted regions: ": "Дозвољене области: ",
|
||||
"Blacklisted regions: ": "Забрањене области: ",
|
||||
"Premieres in `x`": "Премера у `x`",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Хеј! Изгледа да сте онемогућили JavaScript. Кликните овде да видите коментаре, чувајте на уму да ово може да потраје дуже док се не учитају.",
|
||||
"View YouTube comments": "Прикажи YouTube коментаре",
|
||||
"View more comments on Reddit": "Прикажи више коментара на Reddit-у",
|
||||
"View Reddit comments": "Прикажи Reddit коментаре",
|
||||
"Whitelisted regions: ": "Доступни региони: ",
|
||||
"Blacklisted regions: ": "Недоступни региони: ",
|
||||
"Premieres in `x`": "Премијера у `x`",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Хеј! Изгледа да сте искључили JavaScript. Кликните овде да бисте видели коментаре, имајте на уму да ће можда потрајати мало дуже да се учитају.",
|
||||
"View YouTube comments": "Погледај YouTube коментаре",
|
||||
"View more comments on Reddit": "Погледај више коментара на Reddit-у",
|
||||
"View Reddit comments": "Погледај Reddit коментаре",
|
||||
"Hide replies": "Сакриј одговоре",
|
||||
"Show replies": "Прикажи одговоре",
|
||||
"Incorrect password": "Нетачна лозинка",
|
||||
"Current version: ": "Тренутна верзија: ",
|
||||
"Wilson score: ": "Wилсонова оцена: ",
|
||||
"Wilson score: ": "Вилсонова оцена: ",
|
||||
"Burmese": "Бурмански",
|
||||
"preferences_quality_dash_label": "Преферирани квалитет DASH видео формата: ",
|
||||
"Erroneous token": "Погрешан жетон",
|
||||
"preferences_quality_dash_label": "Преферирани DASH квалитет видео снимка: ",
|
||||
"Erroneous token": "Погрешан токен",
|
||||
"CAPTCHA is a required field": "CAPTCHA је обавезно поље",
|
||||
"No such user": "Непостојећи корисник",
|
||||
"No such user": "Не постоји корисник",
|
||||
"Chinese (Traditional)": "Кинески (Традиционални)",
|
||||
"adminprefs_modified_source_code_url_label": "УРЛ веза до складишта са Измењеном Изворном Кодом",
|
||||
"adminprefs_modified_source_code_url_label": "URL адреса до репозиторијума измењеног изворног кода",
|
||||
"Lao": "Лаоски",
|
||||
"Czech": "Чешки",
|
||||
"Kannada": "Канада (Језик)",
|
||||
"Kannada": "Канада",
|
||||
"Polish": "Пољски",
|
||||
"Cebuano": "Себуано",
|
||||
"Cebuano": "Цебуански",
|
||||
"preferences_show_nick_label": "Прикажи надимке на врху: ",
|
||||
"Report statistics: ": "Извештавај о статистици: ",
|
||||
"Report statistics: ": "Извештавај статистике: ",
|
||||
"Show more": "Прикажи више",
|
||||
"Wrong answer": "Погрешан одговор",
|
||||
"Hidden field \"token\" is a required field": "Сакривено \"token\" поље је обавезно",
|
||||
"Hidden field \"token\" is a required field": "Скривено поље „токен“ је обавезно поље",
|
||||
"English": "Енглески",
|
||||
"Albanian": "Албански",
|
||||
"Amharic": "Амхарски",
|
||||
|
@ -176,38 +176,38 @@
|
|||
"Georgian": "Грузијски",
|
||||
"Greek": "Грчки",
|
||||
"Hausa": "Хауса",
|
||||
"search_filters_type_option_video": "Видео",
|
||||
"search_filters_type_option_playlist": "Плеј листа",
|
||||
"search_filters_type_option_video": "Видео снимак",
|
||||
"search_filters_type_option_playlist": "Плејлиста",
|
||||
"search_filters_type_option_movie": "Филм",
|
||||
"search_filters_duration_option_long": "Дуго (> 20 минута)",
|
||||
"search_filters_features_option_c_commons": "Creative Commons (Лиценца)",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_live": "Уживо",
|
||||
"search_filters_features_option_location": "Локација",
|
||||
"next_steps_error_message": "Након чега би требали пробати: ",
|
||||
"next_steps_error_message": "Након тога би требало да покушате да: ",
|
||||
"footer_donate_page": "Донирај",
|
||||
"footer_documentation": "Документација",
|
||||
"footer_modfied_source_code": "Измењена Изворна Кода",
|
||||
"preferences_region_label": "Држава порекла садржаја: ",
|
||||
"footer_modfied_source_code": "Измењени изворни кôд",
|
||||
"preferences_region_label": "Држава садржаја: ",
|
||||
"preferences_category_misc": "Остала подешавања",
|
||||
"User ID is a required field": "Кориснички ИД је обавезно поље",
|
||||
"User ID is a required field": "ID корисника је обавезно поље",
|
||||
"Password is a required field": "Лозинка је обавезно поље",
|
||||
"Wrong username or password": "Погрешно корисничко име или лозинка",
|
||||
"Password cannot be empty": "Лозинка не може бити празна",
|
||||
"Password cannot be longer than 55 characters": "Лозинка не може бити дужа од 55 карактера",
|
||||
"Invidious Private Feed for `x`": "Инвидиоус Приватни Довод за `x`",
|
||||
"Deleted or invalid channel": "Обрисан или непостојећи канал",
|
||||
"Password cannot be longer than 55 characters": "Лозинка не може бити дужа од 55 знакова",
|
||||
"Invidious Private Feed for `x`": "Invidious приватни фид за `x`",
|
||||
"Deleted or invalid channel": "Избрисан или неважећи канал",
|
||||
"This channel does not exist.": "Овај канал не постоји.",
|
||||
"Could not create mix.": "Прављење микса није успело.",
|
||||
"Empty playlist": "Празна плеј листа",
|
||||
"Not a playlist.": "Није плеј листа.",
|
||||
"Playlist does not exist.": "Непостојећа плеј листа.",
|
||||
"Could not pull trending pages.": "Учитавање 'У току' страница није успело.",
|
||||
"Hidden field \"challenge\" is a required field": "Сакривено \"challenge\" поље је обавезно",
|
||||
"Could not create mix.": "Није могуће направити микс.",
|
||||
"Empty playlist": "Празна плејлиста",
|
||||
"Not a playlist.": "Није плејлиста.",
|
||||
"Playlist does not exist.": "Плејлиста не постоји.",
|
||||
"Could not pull trending pages.": "Није могуће повући странице „У тренду“.",
|
||||
"Hidden field \"challenge\" is a required field": "Скривено поље „изазов“ је обавезно поље",
|
||||
"Telugu": "Телугу",
|
||||
"Turkish": "Турски",
|
||||
"Urdu": "Урду",
|
||||
"Western Frisian": "Западнофрисијски",
|
||||
"Xhosa": "Коса (Језик)",
|
||||
"Western Frisian": "Западнофризијски",
|
||||
"Xhosa": "Коса (Кхоса)",
|
||||
"Yiddish": "Јидиш",
|
||||
"Hawaiian": "Хавајски",
|
||||
"Hmong": "Хмонг",
|
||||
|
@ -217,58 +217,58 @@
|
|||
"Khmer": "Кмерски",
|
||||
"Kyrgyz": "Киргиски",
|
||||
"Macedonian": "Македонски",
|
||||
"Maori": "Маори (Језик)",
|
||||
"Marathi": "Маратхи",
|
||||
"Maori": "Маорски",
|
||||
"Marathi": "Маратски",
|
||||
"Nepali": "Непалски",
|
||||
"Norwegian Bokmål": "Норвешки Бокмал",
|
||||
"Nyanja": "Чева",
|
||||
"Nyanja": "Нијанџа",
|
||||
"Russian": "Руски",
|
||||
"Scottish Gaelic": "Шкотски Гелски",
|
||||
"Shona": "Шона",
|
||||
"Slovak": "Словачки",
|
||||
"Spanish (Latin America)": "Шпански (Јужна Америка)",
|
||||
"Sundanese": "Сундски",
|
||||
"Swahili": "Свахили",
|
||||
"Spanish (Latin America)": "Шпански (Латинска Америка)",
|
||||
"Sundanese": "Сундански",
|
||||
"Swahili": "Сували",
|
||||
"Tajik": "Таџички",
|
||||
"Search": "Претрага",
|
||||
"Rating: ": "Ocena/e: ",
|
||||
"Default": "Подразумеван/о",
|
||||
"Rating: ": "Оцена: ",
|
||||
"Default": "Подразумевано",
|
||||
"News": "Вести",
|
||||
"Download": "Преузми",
|
||||
"(edited)": "(измењено)",
|
||||
"`x` marked it with a ❤": "`x` је означио/ла ово са ❤",
|
||||
"Audio mode": "Аудио мод",
|
||||
"channel_tab_videos_label": "Видео клипови",
|
||||
"`x` marked it with a ❤": "`x` је означио/ла са ❤",
|
||||
"Audio mode": "Режим аудио снимка",
|
||||
"channel_tab_videos_label": "Видео снимци",
|
||||
"search_filters_sort_option_views": "Број прегледа",
|
||||
"search_filters_features_label": "Карактеристике",
|
||||
"search_filters_date_option_today": "Данас",
|
||||
"%A %B %-d, %Y": "%A %B %-d, %Y",
|
||||
"preferences_locale_label": "Језик: ",
|
||||
"Persian": "Перзијски",
|
||||
"Persian": "Персијски",
|
||||
"View `x` comments": {
|
||||
"": "Прикажи `x` коментара",
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Прикажи `x` коментар"
|
||||
"": "Погледај `x` коментаре",
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Погледај `x` коментар"
|
||||
},
|
||||
"search_filters_type_option_channel": "Канал",
|
||||
"Haitian Creole": "Хаићански Креолски",
|
||||
"Armenian": "Јерменски",
|
||||
"next_steps_error_message_go_to_youtube": "Иди на YouTube",
|
||||
"Indonesian": "Индонежански",
|
||||
"preferences_vr_mode_label": "Интерактивни видео клипови у 360 степени: ",
|
||||
"next_steps_error_message_go_to_youtube": "Одете на YouTube",
|
||||
"Indonesian": "Индонезијски",
|
||||
"preferences_vr_mode_label": "Интерактивни видео снимци од 360 степени (захтева WebGL): ",
|
||||
"Switch Invidious Instance": "Промени Invidious инстанцу",
|
||||
"Portuguese": "Португалски",
|
||||
"search_filters_date_option_week": "Ове седмице",
|
||||
"search_filters_date_option_week": "Ове недеље",
|
||||
"search_filters_type_option_show": "Емисија",
|
||||
"Fallback comments: ": "Коментари у случају отказивања: ",
|
||||
"search_filters_features_option_hdr": "Видео Високе Резолуције",
|
||||
"About": "О програму",
|
||||
"Fallback comments: ": "Резервни коментари: ",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"About": "О сајту",
|
||||
"Kazakh": "Казашки",
|
||||
"Shared `x`": "Подељено `x`",
|
||||
"Playlists": "Плеј листе",
|
||||
"Shared `x`": "Дељено `x`",
|
||||
"Playlists": "Плејлисте",
|
||||
"Yoruba": "Јоруба",
|
||||
"Erroneous challenge": "Погрешан изазов",
|
||||
"Danish": "Дански",
|
||||
"Could not get channel info.": "Узимање података о каналу није успело.",
|
||||
"Could not get channel info.": "Није могуће прикупити информације о каналу.",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"Slovenian": "Словеначки",
|
||||
"Load more": "Учитај више",
|
||||
|
@ -276,53 +276,53 @@
|
|||
"Luxembourgish": "Луксембуршки",
|
||||
"Mongolian": "Монголски",
|
||||
"Latvian": "Летонски",
|
||||
"channel:`x`": "kanal:`x`",
|
||||
"channel:`x`": "канал:`x`",
|
||||
"Southern Sotho": "Јужни Сото",
|
||||
"Popular": "Популарно",
|
||||
"Gujarati": "Гуџарати",
|
||||
"search_filters_date_option_year": "Ове године",
|
||||
"Irish": "Ирски",
|
||||
"YouTube comment permalink": "YouTube коментар трајна веза",
|
||||
"YouTube comment permalink": "Трајни линк YouTube коментара",
|
||||
"Malagasy": "Малгашки",
|
||||
"Token is expired, please try again": "Жетон је истекао, молимо вас да покушате поново",
|
||||
"search_filters_duration_option_short": "Кратко (< 4 минуте)",
|
||||
"Token is expired, please try again": "Токен је истекао, покушајте поново",
|
||||
"search_filters_duration_option_short": "Кратко (< 4 минута)",
|
||||
"Samoan": "Самоански",
|
||||
"Tamil": "Тамилски",
|
||||
"Ukrainian": "Украјински",
|
||||
"permalink": "трајна веза",
|
||||
"permalink": "трајни линк",
|
||||
"Pashto": "Паштунски",
|
||||
"channel_tab_community_label": "Заједница",
|
||||
"Sindhi": "Синди",
|
||||
"Could not fetch comments": "Узимање коментара није успело",
|
||||
"Bangla": "Бангла/Бенгалски",
|
||||
"Could not fetch comments": "Није могуће прикупити коментаре",
|
||||
"Bangla": "Бенгалски",
|
||||
"Uzbek": "Узбечки",
|
||||
"Lithuanian": "Литвански",
|
||||
"Icelandic": "Исландски",
|
||||
"Thai": "Тајски",
|
||||
"search_filters_date_option_month": "Овај месец",
|
||||
"search_filters_type_label": "Тип",
|
||||
"search_filters_date_option_month": "Овог месеца",
|
||||
"search_filters_type_label": "Врста",
|
||||
"search_filters_date_option_hour": "Последњи сат",
|
||||
"Spanish": "Шпански",
|
||||
"search_filters_sort_option_date": "Датум отпремања",
|
||||
"View as playlist": "Погледај као плеј листу",
|
||||
"View as playlist": "Погледај као плејлисту",
|
||||
"search_filters_sort_option_relevance": "Релевантност",
|
||||
"Estonian": "Естонски",
|
||||
"Sinhala": "Синхалешки",
|
||||
"Sinhala": "Синхалски",
|
||||
"Corsican": "Корзикански",
|
||||
"Filipino": "Филипино",
|
||||
"Gaming": "Игрице",
|
||||
"Filipino": "Филипински",
|
||||
"Gaming": "Видео игре",
|
||||
"Movies": "Филмови",
|
||||
"search_filters_sort_option_rating": "Оцене",
|
||||
"Top enabled: ": "Врх омогућен: ",
|
||||
"Released under the AGPLv3 on Github.": "Избачено под лиценцом AGPLv3 на GitHub-у.",
|
||||
"search_filters_sort_option_rating": "Оцена",
|
||||
"Top enabled: ": "Топ омогућено: ",
|
||||
"Released under the AGPLv3 on Github.": "Објављено под лиценцом AGPLv3 на GitHub-у.",
|
||||
"Afrikaans": "Африканс",
|
||||
"preferences_automatic_instance_redirect_label": "Аутоматско пребацивање на другу инстанцу у случају отказивања (пречи ће назад на редирецт.инвидиоус.ио): ",
|
||||
"Please log in": "Молимо вас да се пријавите",
|
||||
"preferences_automatic_instance_redirect_label": "Аутоматско преусмеравање инстанце (повратак на redirect.invidious.io): ",
|
||||
"Please log in": "Молимо, пријавите се",
|
||||
"English (auto-generated)": "Енглески (аутоматски генерисано)",
|
||||
"Hindi": "Хинди",
|
||||
"Italian": "Талијански",
|
||||
"Malayalam": "Малајалам",
|
||||
"Punjabi": "Пунџаби",
|
||||
"Italian": "Италијански",
|
||||
"Malayalam": "Малајаламски",
|
||||
"Punjabi": "Панџапски",
|
||||
"Somali": "Сомалијски",
|
||||
"Vietnamese": "Вијетнамски",
|
||||
"Welsh": "Велшки",
|
||||
|
@ -330,25 +330,25 @@
|
|||
"Maltese": "Малтешки",
|
||||
"Swedish": "Шведски",
|
||||
"Music": "Музика",
|
||||
"Download as: ": "Преузми као: ",
|
||||
"Download as: ": "Преузети као: ",
|
||||
"search_filters_duration_label": "Трајање",
|
||||
"search_filters_sort_label": "Поредај према",
|
||||
"search_filters_features_option_subtitles": "Титл/Превод",
|
||||
"preferences_extend_desc_label": "Аутоматски прикажи цео опис видеа: ",
|
||||
"search_filters_sort_label": "Сортирање по",
|
||||
"search_filters_features_option_subtitles": "Титлови/Скривени титлови",
|
||||
"preferences_extend_desc_label": "Аутоматски прошири опис видео снимка: ",
|
||||
"Show less": "Прикажи мање",
|
||||
"Family friendly? ": "Погодно за породицу? ",
|
||||
"Premieres `x`": "Премерe у `x`",
|
||||
"Premieres `x`": "Премијера `x`",
|
||||
"Bosnian": "Босански",
|
||||
"Catalan": "Каталонски",
|
||||
"Japanese": "Јапански",
|
||||
"Latin": "Латински",
|
||||
"next_steps_error_message_refresh": "Освежи страницу",
|
||||
"footer_original_source_code": "Оригинална Изворна Кода",
|
||||
"next_steps_error_message_refresh": "Освежите",
|
||||
"footer_original_source_code": "Оригинални изворни кôд",
|
||||
"Romanian": "Румунски",
|
||||
"Serbian": "Српски",
|
||||
"Top": "Врх",
|
||||
"Video mode": "Видео мод",
|
||||
"footer_source_code": "Изворна Кода",
|
||||
"Top": "Топ",
|
||||
"Video mode": "Режим видео снимка",
|
||||
"footer_source_code": "Изворни кôд",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"Erroneous CAPTCHA": "Погрешна CAPTCHA",
|
||||
|
@ -360,5 +360,150 @@
|
|||
"Korean": "Корејски",
|
||||
"Kurdish": "Курдски",
|
||||
"Malay": "Малајски",
|
||||
"search_filters_title": "Филтер"
|
||||
"search_filters_title": "Филтери",
|
||||
"Korean (auto-generated)": "Корејски (аутоматски генерисано)",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"preferences_quality_dash_option_worst": "Најгоре",
|
||||
"channel_tab_podcasts_label": "Подкасти",
|
||||
"preferences_save_player_pos_label": "Сачувај позицију репродукције: ",
|
||||
"Spanish (Mexico)": "Шпански (Мексико)",
|
||||
"generic_subscriptions_count_0": "{{count}} праћење",
|
||||
"generic_subscriptions_count_1": "{{count}} праћења",
|
||||
"generic_subscriptions_count_2": "{{count}} праћења",
|
||||
"search_filters_apply_button": "Примени изабране филтере",
|
||||
"Download is disabled": "Преузимање је онемогућено",
|
||||
"comments_points_count_0": "{{count}} поен",
|
||||
"comments_points_count_1": "{{count}} поена",
|
||||
"comments_points_count_2": "{{count}} поена",
|
||||
"preferences_quality_dash_option_2160p": "2160p",
|
||||
"German (auto-generated)": "Немачки (аутоматски генерисано)",
|
||||
"Japanese (auto-generated)": "Јапански (аутоматски генерисано)",
|
||||
"preferences_quality_option_medium": "Средње",
|
||||
"search_message_change_filters_or_query": "Покушајте да проширите упит за претрагу и/или промените филтере.",
|
||||
"crash_page_before_reporting": "Пре него што пријавите грешку, уверите се да сте:",
|
||||
"preferences_quality_dash_option_best": "Најбоље",
|
||||
"Channel Sponsor": "Спонзор канала",
|
||||
"generic_videos_count_0": "{{count}} видео снимак",
|
||||
"generic_videos_count_1": "{{count}} видео снимка",
|
||||
"generic_videos_count_2": "{{count}} видео снимака",
|
||||
"videoinfo_started_streaming_x_ago": "Започето стримовање пре `x`",
|
||||
"videoinfo_youTube_embed_link": "Уграђено",
|
||||
"channel_tab_streams_label": "Стримови уживо",
|
||||
"playlist_button_add_items": "Додај видео снимке",
|
||||
"generic_count_minutes_0": "{{count}} минут",
|
||||
"generic_count_minutes_1": "{{count}} минута",
|
||||
"generic_count_minutes_2": "{{count}} минута",
|
||||
"preferences_quality_dash_option_720p": "720p",
|
||||
"preferences_watch_history_label": "Омогући историју гледања: ",
|
||||
"user_saved_playlists": "Сачуваних плејлиста: `x`",
|
||||
"Spanish (Spain)": "Шпански (Шпанија)",
|
||||
"invidious": "Invidious",
|
||||
"crash_page_refresh": "покушали да <a href=\"`x`\">освежите страницу</a>",
|
||||
"Chinese (Hong Kong)": "Кинески (Хонг Конг)",
|
||||
"Artist: ": "Извођач: ",
|
||||
"generic_count_months_0": "{{count}} месец",
|
||||
"generic_count_months_1": "{{count}} месеца",
|
||||
"generic_count_months_2": "{{count}} месеци",
|
||||
"search_message_use_another_instance": " Такође, можете <a href=\"`x`\">претраживати на другој инстанци</a>.",
|
||||
"generic_subscribers_count_0": "{{count}} пратилац",
|
||||
"generic_subscribers_count_1": "{{count}} пратиоца",
|
||||
"generic_subscribers_count_2": "{{count}} пратилаца",
|
||||
"download_subtitles": "Титлови - `x` (.vtt)",
|
||||
"generic_button_save": "Сачувај",
|
||||
"crash_page_search_issue": "претражили <a href=\"`x`\">постојеће извештаје о проблемима на GitHub-у</a>",
|
||||
"generic_button_cancel": "Откажи",
|
||||
"none": "ниједно",
|
||||
"English (United States)": "Енглески (Сједињене Америчке Државе)",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} невиђено обавештење",
|
||||
"subscriptions_unseen_notifs_count_1": "{{count}} невиђена обавештења",
|
||||
"subscriptions_unseen_notifs_count_2": "{{count}} невиђених обавештења",
|
||||
"Album: ": "Албум: ",
|
||||
"preferences_quality_option_dash": "DASH (адаптивни квалитет)",
|
||||
"preferences_quality_dash_option_1080p": "1080p",
|
||||
"Video unavailable": "Видео снимак недоступан",
|
||||
"tokens_count_0": "{{count}} токен",
|
||||
"tokens_count_1": "{{count}} токена",
|
||||
"tokens_count_2": "{{count}} токена",
|
||||
"Chinese (China)": "Кинески (Кина)",
|
||||
"Italian (auto-generated)": "Италијански (аутоматски генерисано)",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"preferences_quality_dash_option_1440p": "1440p",
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
"search_message_no_results": "Нису пронађени резултати.",
|
||||
"channel_tab_releases_label": "Издања",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"Interlingue": "Интерлингва",
|
||||
"Song: ": "Песма: ",
|
||||
"generic_channels_count_0": "{{count}} канал",
|
||||
"generic_channels_count_1": "{{count}} канала",
|
||||
"generic_channels_count_2": "{{count}} канала",
|
||||
"Chinese (Taiwan)": "Кинески (Тајван)",
|
||||
"Turkish (auto-generated)": "Турски (аутоматски генерисано)",
|
||||
"Indonesian (auto-generated)": "Индонезијски (аутоматски генерисано)",
|
||||
"Portuguese (auto-generated)": "Португалски (аутоматски генерисано)",
|
||||
"generic_count_years_0": "{{count}} година",
|
||||
"generic_count_years_1": "{{count}} године",
|
||||
"generic_count_years_2": "{{count}} година",
|
||||
"videoinfo_invidious_embed_link": "Уграђени линк",
|
||||
"Popular enabled: ": "Популарно омогућено: ",
|
||||
"Spanish (auto-generated)": "Шпански (аутоматски генерисано)",
|
||||
"preferences_quality_option_small": "Мало",
|
||||
"English (United Kingdom)": "Енглески (Уједињено Краљевство)",
|
||||
"channel_tab_playlists_label": "Плејлисте",
|
||||
"generic_button_edit": "Измени",
|
||||
"generic_playlists_count_0": "{{count}} плејлиста",
|
||||
"generic_playlists_count_1": "{{count}} плејлисте",
|
||||
"generic_playlists_count_2": "{{count}} плејлиста",
|
||||
"preferences_quality_option_hd720": "HD720",
|
||||
"search_filters_features_option_purchased": "Купљено",
|
||||
"search_filters_date_option_none": "Било који датум",
|
||||
"preferences_quality_dash_option_auto": "Аутоматски",
|
||||
"Cantonese (Hong Kong)": "Кантонски (Хонг Конг)",
|
||||
"crash_page_report_issue": "Ако ништа од горенаведеног није помогло, <a href=\"`x`\">отворите нови извештај о проблему на GitHub-у</a> (по могућности на енглеском) и укључите следећи текст у своју поруку (НЕ преводите тај текст):",
|
||||
"crash_page_switch_instance": "покушали да <a href=\"`x`\">користите другу инстанцу</a>",
|
||||
"generic_count_weeks_0": "{{count}} недеља",
|
||||
"generic_count_weeks_1": "{{count}} недеље",
|
||||
"generic_count_weeks_2": "{{count}} недеља",
|
||||
"videoinfo_watch_on_youTube": "Гледај на YouTube-у",
|
||||
"Music in this video": "Музика у овом видео снимку",
|
||||
"generic_button_rss": "RSS",
|
||||
"preferences_quality_dash_option_4320p": "4320p",
|
||||
"generic_count_hours_0": "{{count}} сат",
|
||||
"generic_count_hours_1": "{{count}} сата",
|
||||
"generic_count_hours_2": "{{count}} сати",
|
||||
"French (auto-generated)": "Француски (аутоматски генерисано)",
|
||||
"crash_page_read_the_faq": "прочитали <a href=\"`x`\">Често Постављана Питања (ЧПП)</a>",
|
||||
"user_created_playlists": "Направљених плејлиста: `x`",
|
||||
"channel_tab_channels_label": "Канали",
|
||||
"search_filters_type_option_all": "Било која врста",
|
||||
"Russian (auto-generated)": "Руски (аутоматски генерисано)",
|
||||
"preferences_quality_dash_option_480p": "480p",
|
||||
"comments_view_x_replies_0": "Погледај {{count}} одговор",
|
||||
"comments_view_x_replies_1": "Погледај {{count}} одговора",
|
||||
"comments_view_x_replies_2": "Погледај {{count}} одговора",
|
||||
"Portuguese (Brazil)": "Португалски (Бразил)",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"error_video_not_in_playlist": "Тражени видео снимак не постоји на овој плејлисти. <a href=\"`x`\">Кликните овде за почетну страницу плејлисте.</a>",
|
||||
"Dutch (auto-generated)": "Холандски (аутоматски генерисано)",
|
||||
"generic_count_days_0": "{{count}} дан",
|
||||
"generic_count_days_1": "{{count}} дана",
|
||||
"generic_count_days_2": "{{count}} дана",
|
||||
"Vietnamese (auto-generated)": "Вијетнамски (аутоматски генерисано)",
|
||||
"search_filters_duration_option_none": "Било које трајање",
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"Chinese": "Кинески",
|
||||
"generic_button_delete": "Избриши",
|
||||
"Import YouTube playlist (.csv)": "Увези YouTube плејлисту (.csv)",
|
||||
"Standard YouTube license": "Стандардна YouTube лиценца",
|
||||
"search_filters_duration_option_medium": "Средње (4 - 20 минута)",
|
||||
"generic_count_seconds_0": "{{count}} секунда",
|
||||
"generic_count_seconds_1": "{{count}} секунде",
|
||||
"generic_count_seconds_2": "{{count}} секунди",
|
||||
"search_filters_date_label": "Датум отпремања",
|
||||
"crash_page_you_found_a_bug": "Изгледа да сте пронашли грешку у Invidious-у!",
|
||||
"generic_views_count_0": "{{count}} преглед",
|
||||
"generic_views_count_1": "{{count}} прегледа",
|
||||
"generic_views_count_2": "{{count}} прегледа",
|
||||
"Import YouTube watch history (.json)": "Увези YouTube историју гледањa (.json)",
|
||||
"toggle_theme": "Укључи тему"
|
||||
}
|
||||
|
|
|
@ -20,15 +20,15 @@
|
|||
"No": "Nej",
|
||||
"Import and Export Data": "Importera och exportera data",
|
||||
"Import": "Importera",
|
||||
"Import Invidious data": "Importera Invidious-data",
|
||||
"Import YouTube subscriptions": "Importera YouTube-prenumerationer",
|
||||
"Import Invidious data": "Importera Invidious JSON data",
|
||||
"Import YouTube subscriptions": "Importera YouTube/OPML prenumerationer",
|
||||
"Import FreeTube subscriptions (.db)": "Importera FreeTube-prenumerationer (.db)",
|
||||
"Import NewPipe subscriptions (.json)": "Importera NewPipe-prenumerationer (.json)",
|
||||
"Import NewPipe data (.zip)": "Importera NewPipe-data (.zip)",
|
||||
"Export": "Exportera",
|
||||
"Export subscriptions as OPML": "Exportera prenumerationer som OPML",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportera prenumerationer som OPML (för NewPipe och FreeTube)",
|
||||
"Export data as JSON": "Exportera data som JSON",
|
||||
"Export data as JSON": "Exportera Invidious data som JSON",
|
||||
"Delete account?": "Radera konto?",
|
||||
"History": "Historik",
|
||||
"An alternative front-end to YouTube": "Ett alternativt gränssnitt till YouTube",
|
||||
|
@ -63,7 +63,7 @@
|
|||
"preferences_related_videos_label": "Visa relaterade videor? ",
|
||||
"preferences_annotations_label": "Visa länkar-i-videon som förval? ",
|
||||
"preferences_extend_desc_label": "Förläng videobeskrivning automatiskt: ",
|
||||
"preferences_vr_mode_label": "Interaktiva 360-gradervideos: ",
|
||||
"preferences_vr_mode_label": "Interaktiva 360-gradervideos (kräver WebGL): ",
|
||||
"preferences_category_visual": "Visuella inställningar",
|
||||
"preferences_player_style_label": "Spelarstil: ",
|
||||
"Dark mode: ": "Mörkt läge: ",
|
||||
|
@ -152,7 +152,7 @@
|
|||
"View YouTube comments": "Visa YouTube-kommentarer",
|
||||
"View more comments on Reddit": "Visa flera kommentarer på Reddit",
|
||||
"View `x` comments": {
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Visa `x` kommentarer",
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Visa `x` kommentar",
|
||||
"": "Visa `x` kommentarer"
|
||||
},
|
||||
"View Reddit comments": "Visa Reddit-kommentarer",
|
||||
|
@ -167,7 +167,7 @@
|
|||
"Wrong username or password": "Ogiltigt användarnamn eller lösenord",
|
||||
"Password cannot be empty": "Lösenordet kan inte vara tomt",
|
||||
"Password cannot be longer than 55 characters": "Lösenordet kan inte vara längre än 55 tecken",
|
||||
"Please log in": "Logga in",
|
||||
"Please log in": "Snälla logga in",
|
||||
"Invidious Private Feed for `x`": "Ogiltig privat flöde för `x`",
|
||||
"channel:`x`": "kanal `x`",
|
||||
"Deleted or invalid channel": "Raderad eller ogiltig kanal",
|
||||
|
@ -311,8 +311,8 @@
|
|||
"%A %B %-d, %Y": "%A %B %-d, %Y",
|
||||
"(edited)": "(redigerad)",
|
||||
"YouTube comment permalink": "Permanent YouTube-länk till innehållet",
|
||||
"permalink": "permalänk",
|
||||
"`x` marked it with a ❤": "`x` lämnade ett ❤",
|
||||
"permalink": "permanent länk",
|
||||
"`x` marked it with a ❤": "`x` markerade det med ett ❤",
|
||||
"Audio mode": "Ljudläge",
|
||||
"Video mode": "Videoläge",
|
||||
"channel_tab_videos_label": "Videor",
|
||||
|
@ -320,30 +320,30 @@
|
|||
"channel_tab_community_label": "Gemenskap",
|
||||
"search_filters_sort_option_relevance": "Relevans",
|
||||
"search_filters_sort_option_rating": "Rankning",
|
||||
"search_filters_sort_option_date": "Datum",
|
||||
"search_filters_sort_option_date": "Uppladdnings Datum",
|
||||
"search_filters_sort_option_views": "Visningar",
|
||||
"search_filters_type_label": "Typ",
|
||||
"search_filters_duration_label": "Varaktighet",
|
||||
"search_filters_features_label": "Funktioner",
|
||||
"search_filters_sort_label": "Sortera efter",
|
||||
"search_filters_date_option_hour": "timme",
|
||||
"search_filters_date_option_today": "idag",
|
||||
"search_filters_date_option_week": "vecka",
|
||||
"search_filters_date_option_month": "månad",
|
||||
"search_filters_date_option_year": "år",
|
||||
"search_filters_type_option_video": "video",
|
||||
"search_filters_type_option_channel": "kanal",
|
||||
"search_filters_type_option_playlist": "spellista",
|
||||
"search_filters_type_option_movie": "film",
|
||||
"search_filters_type_option_show": "tv-serie",
|
||||
"search_filters_features_option_hd": "hd",
|
||||
"search_filters_features_option_subtitles": "undertexter",
|
||||
"search_filters_features_option_c_commons": "creative_commons",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "live",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "plats",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_date_option_hour": "Senaste Timmen",
|
||||
"search_filters_date_option_today": "Idag",
|
||||
"search_filters_date_option_week": "Denna vecka",
|
||||
"search_filters_date_option_month": "Denna månad",
|
||||
"search_filters_date_option_year": "Detta år",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Kanal",
|
||||
"search_filters_type_option_playlist": "Spellista",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_type_option_show": "Serie",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Undertexter/CC",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Live",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Plats",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"Current version: ": "Nuvarande version: ",
|
||||
"next_steps_error_message_refresh": "Uppdatera",
|
||||
"next_steps_error_message_go_to_youtube": "Gå till Youtube",
|
||||
|
@ -352,5 +352,141 @@
|
|||
"search_filters_duration_option_long": "Lång (> 20 minuter)",
|
||||
"footer_documentation": "Dokumentation",
|
||||
"search_filters_duration_option_short": "Kort (< 4 minuter)",
|
||||
"search_filters_title": "Filter"
|
||||
"search_filters_title": "Filter",
|
||||
"Korean (auto-generated)": "Koreanska (auto-genererad)",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"preferences_quality_dash_option_worst": "Sämst",
|
||||
"channel_tab_podcasts_label": "Podcaster",
|
||||
"preferences_save_player_pos_label": "Spara uppspelningsposition: ",
|
||||
"Spanish (Mexico)": "Spanska (Mexiko)",
|
||||
"preferences_region_label": "Innehållsland: ",
|
||||
"generic_subscriptions_count": "{{count}} prenumeration",
|
||||
"generic_subscriptions_count_plural": "{{count}} prenumerationer",
|
||||
"search_filters_apply_button": "Använd valda filter",
|
||||
"Download is disabled": "Nedladdning är inaktiverad",
|
||||
"comments_points_count": "{{count}} poäng",
|
||||
"comments_points_count_plural": "{{count}} poäng",
|
||||
"preferences_quality_dash_option_2160p": "2160p",
|
||||
"German (auto-generated)": "Tyska (auto-genererad)",
|
||||
"Japanese (auto-generated)": "Japanska (auto-genererad)",
|
||||
"preferences_quality_option_medium": "Medium",
|
||||
"footer_donate_page": "Donera",
|
||||
"search_message_change_filters_or_query": "Prova att bredda din sökfråga och/eller ändra filtren.",
|
||||
"crash_page_before_reporting": "Innan du rapporterar en bugg, se till att du har:",
|
||||
"preferences_quality_dash_option_best": "Bäst",
|
||||
"Channel Sponsor": "Kanal Sponsor",
|
||||
"generic_videos_count": "{{count}} video",
|
||||
"generic_videos_count_plural": "{{count}} videor",
|
||||
"videoinfo_started_streaming_x_ago": "Började sända `x` sedan",
|
||||
"videoinfo_youTube_embed_link": "Bädda in",
|
||||
"channel_tab_streams_label": "Livesändningar",
|
||||
"playlist_button_add_items": "Lägg till videor",
|
||||
"generic_count_minutes": "{{count}}minut",
|
||||
"generic_count_minutes_plural": "{{count}}minuter",
|
||||
"preferences_quality_dash_option_720p": "720p",
|
||||
"preferences_watch_history_label": "Aktivera visningshistorik: ",
|
||||
"user_saved_playlists": "`x` sparade spellistor",
|
||||
"Spanish (Spain)": "Spanska (Spanien)",
|
||||
"invidious": "Invidious",
|
||||
"crash_page_refresh": "försökte <a href=\"`x`\">uppdatera sidan</a>",
|
||||
"Chinese (Hong Kong)": "Kinesiska (Hong Kong)",
|
||||
"Artist: ": "Artist: ",
|
||||
"generic_count_months": "{{count}}månad",
|
||||
"generic_count_months_plural": "{{count}}månader",
|
||||
"search_message_use_another_instance": " Du kan också <a href=\"`x`\">söka på en annan instans</a>.",
|
||||
"generic_subscribers_count": "{{count}} prenumerant",
|
||||
"generic_subscribers_count_plural": "{{count}} prenumeranter",
|
||||
"download_subtitles": "Undertexter - `x` (.vtt)",
|
||||
"generic_button_save": "Spara",
|
||||
"crash_page_search_issue": "sökte efter <a href=\"`x`\">befintliga problem på GitHub</a>",
|
||||
"generic_button_cancel": "Avbryt",
|
||||
"none": "ingen",
|
||||
"English (United States)": "English (Förenta staterna)",
|
||||
"subscriptions_unseen_notifs_count": "{{count}}osedd notifikation",
|
||||
"subscriptions_unseen_notifs_count_plural": "{{count}}osedda notifikationer",
|
||||
"Album: ": "Album: ",
|
||||
"preferences_quality_option_dash": "DASH (adaptiv kvalitet)",
|
||||
"preferences_quality_dash_option_1080p": "1080p",
|
||||
"Video unavailable": "Video inte tillgänglig",
|
||||
"tokens_count": "{{count}}nyckel",
|
||||
"tokens_count_plural": "{{count}}nycklar",
|
||||
"Chinese (China)": "Kinesiska (Kina)",
|
||||
"Italian (auto-generated)": "Italienska (auto-genererad)",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"preferences_quality_dash_option_1440p": "1440p",
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
"search_message_no_results": "Inga resultat hittades.",
|
||||
"channel_tab_releases_label": "Releaser",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"Interlingue": "Interlingue (auto-genererad)",
|
||||
"Song: ": "Låt: ",
|
||||
"generic_channels_count": "{{count}} kanal",
|
||||
"generic_channels_count_plural": "{{count}} kanaler",
|
||||
"Chinese (Taiwan)": "Kinesiska (Taiwan)",
|
||||
"preferences_quality_dash_label": "Önskad DASH-videokvalitet: ",
|
||||
"adminprefs_modified_source_code_url_label": "URL till modifierad källkodslager",
|
||||
"Turkish (auto-generated)": "Turkiska (auto-genererad)",
|
||||
"Indonesian (auto-generated)": "Indonesiska (auto-genererad)",
|
||||
"Portuguese (auto-generated)": "Portugisiska (auto-genererad)",
|
||||
"generic_count_years": "{{count}}år",
|
||||
"generic_count_years_plural": "{{count}}år",
|
||||
"videoinfo_invidious_embed_link": "Bädda in länk",
|
||||
"Popular enabled: ": "Populär aktiverad: ",
|
||||
"Spanish (auto-generated)": "Spanska (auto-genererad)",
|
||||
"preferences_quality_option_small": "Liten",
|
||||
"English (United Kingdom)": "Engelska (Storbritannien)",
|
||||
"channel_tab_playlists_label": "Spellistor",
|
||||
"generic_button_edit": "Redigera",
|
||||
"generic_playlists_count": "{{count}} spellista",
|
||||
"generic_playlists_count_plural": "{{count}} spellistor",
|
||||
"preferences_quality_option_hd720": "HD720p",
|
||||
"search_filters_features_option_purchased": "Köpt",
|
||||
"search_filters_date_option_none": "Vilket datum som helst",
|
||||
"preferences_quality_dash_option_auto": "Auto",
|
||||
"Cantonese (Hong Kong)": "Katonesiska (Hong Kong)",
|
||||
"crash_page_report_issue": "Om inget av ovanstående hjälpte, vänligen <a href=\"`x`\">öppna ett nytt nummer på GitHub</a> (helst på engelska) och inkludera följande text i ditt meddelande (översätt INTE den texten):",
|
||||
"crash_page_switch_instance": "försökte <a href=\"`x`\">använda en annan instans</a>",
|
||||
"generic_count_weeks": "{{count}}vecka",
|
||||
"generic_count_weeks_plural": "{{count}}veckor",
|
||||
"videoinfo_watch_on_youTube": "Titta på YouTube",
|
||||
"Music in this video": "Musik i denna video",
|
||||
"footer_modfied_source_code": "Modifierad källkod",
|
||||
"generic_button_rss": "RSS",
|
||||
"preferences_quality_dash_option_4320p": "4320p",
|
||||
"generic_count_hours": "{{count}}timme",
|
||||
"generic_count_hours_plural": "{{count}}timmar",
|
||||
"French (auto-generated)": "Franska (auto-genererad)",
|
||||
"crash_page_read_the_faq": "läs <a href=\"`x`\">Vanliga frågor (FAQ)</a>",
|
||||
"user_created_playlists": "`x` skapade spellistor",
|
||||
"channel_tab_channels_label": "Kanaler",
|
||||
"search_filters_type_option_all": "Vilken typ som helst",
|
||||
"Russian (auto-generated)": "Ryska (auto-genererad)",
|
||||
"preferences_quality_dash_option_480p": "480p",
|
||||
"comments_view_x_replies": "Se {{count}} svar",
|
||||
"comments_view_x_replies_plural": "Se {{count}} svar",
|
||||
"footer_original_source_code": "Ursprunglig källkod",
|
||||
"Portuguese (Brazil)": "Portugisiska (Brasilien)",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"error_video_not_in_playlist": "Den begärda videon finns inte i den här spellistan. <a href=\"`x`\">Klicka här för startsidan för spellistan.</a>",
|
||||
"Dutch (auto-generated)": "Nederländska (auto-genererad)",
|
||||
"generic_count_days": "{{count}}dag",
|
||||
"generic_count_days_plural": "{{count}}dagar",
|
||||
"Vietnamese (auto-generated)": "Vietnamesiska (auto-genererad)",
|
||||
"search_filters_duration_option_none": "Vilken varaktighet som helst",
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"Chinese": "Kinesiska",
|
||||
"preferences_automatic_instance_redirect_label": "Automatisk instansomdirigering (återgång till redirect.invidious.io): ",
|
||||
"generic_button_delete": "Radera",
|
||||
"Import YouTube playlist (.csv)": "Importera YouTube spellista (.csv)",
|
||||
"next_steps_error_message": "Därefter bör du försöka: ",
|
||||
"Standard YouTube license": "Standard YouTube licens",
|
||||
"Import YouTube watch history (.json)": "Importera YouTube visningshistorik (.json)",
|
||||
"search_filters_duration_option_medium": "Medium (4 - 20 minuter)",
|
||||
"generic_count_seconds": "{{count}}sekund",
|
||||
"generic_count_seconds_plural": "{{count}}sekunder",
|
||||
"search_filters_date_label": "Uppladdningsdatum",
|
||||
"crash_page_you_found_a_bug": "Det verkar som att du har hittat en bugg i Invidious!",
|
||||
"generic_views_count": "{{count}} visning",
|
||||
"generic_views_count_plural": "{{count}} visningar",
|
||||
"toggle_theme": "Växla tema"
|
||||
}
|
||||
|
|
|
@ -484,5 +484,9 @@
|
|||
"generic_button_rss": "RSS",
|
||||
"channel_tab_releases_label": "Yayınlar",
|
||||
"playlist_button_add_items": "Video ekle",
|
||||
"channel_tab_podcasts_label": "Podcast'ler"
|
||||
"channel_tab_podcasts_label": "Podcast'ler",
|
||||
"generic_channels_count": "{{count}} kanal",
|
||||
"generic_channels_count_plural": "{{count}} kanal",
|
||||
"Import YouTube watch history (.json)": "YouTube İzleme Geçmişini İçe Aktar (.json)",
|
||||
"toggle_theme": "Temayı Değiştir"
|
||||
}
|
||||
|
|
|
@ -500,5 +500,10 @@
|
|||
"channel_tab_releases_label": "Випуски",
|
||||
"generic_button_delete": "Видалити",
|
||||
"generic_button_edit": "Змінити",
|
||||
"generic_button_save": "Зберегти"
|
||||
"generic_button_save": "Зберегти",
|
||||
"generic_channels_count_0": "{{count}} канал",
|
||||
"generic_channels_count_1": "{{count}} канали",
|
||||
"generic_channels_count_2": "{{count}} каналів",
|
||||
"Import YouTube watch history (.json)": "Імпортувати історію переглядів YouTube (.json)",
|
||||
"toggle_theme": "Перемкнути тему"
|
||||
}
|
||||
|
|
309
locales/vi.json
309
locales/vi.json
|
@ -1,62 +1,62 @@
|
|||
{
|
||||
"generic_videos_count_0": "{{count}} video",
|
||||
"generic_subscribers_count_0": "{{count}} người theo dõi",
|
||||
"generic_subscribers_count_0": "{{count}} người đăng ký",
|
||||
"LIVE": "TRỰC TIẾP",
|
||||
"Shared `x` ago": "Đã chia sẻ `x` trước",
|
||||
"Unsubscribe": "Hủy theo dõi",
|
||||
"Subscribe": "Theo dõi",
|
||||
"Unsubscribe": "Hủy đăng ký",
|
||||
"Subscribe": "Đăng ký",
|
||||
"View channel on YouTube": "Xem kênh trên YouTube",
|
||||
"View playlist on YouTube": "Xem danh sách phát trên YouTube",
|
||||
"newest": "mới nhất",
|
||||
"oldest": "lâu đời nhất",
|
||||
"popular": "phổ biến",
|
||||
"last": "Cuối cùng",
|
||||
"newest": "Mới nhất",
|
||||
"oldest": "Cũ nhất",
|
||||
"popular": "Phổ biến",
|
||||
"last": "cuối cùng",
|
||||
"Next page": "Trang tiếp theo",
|
||||
"Previous page": "Trang trước",
|
||||
"Clear watch history?": "Xóa lịch sử xem?",
|
||||
"New password": "Mật khẩu mới",
|
||||
"New passwords must match": "Mật khẩu mới phải khớp",
|
||||
"Authorize token?": "Cấp phép mã thông báo?",
|
||||
"Authorize token for `x`?": "Cấp phép mã thông báo cho` x`?",
|
||||
"Yes": "Đúng",
|
||||
"Authorize token for `x`?": "Cấp phép mã thông báo cho `x`?",
|
||||
"Yes": "Có",
|
||||
"No": "Không",
|
||||
"Import and Export Data": "Nhập và xuất dữ liệu",
|
||||
"Import": "Nhập",
|
||||
"Import Invidious data": "Nhập dữ liệu Invidious JSON",
|
||||
"Import YouTube subscriptions": "Nhập dữ liệu thuê bao YouTube/OPML",
|
||||
"Import FreeTube subscriptions (.db)": "Nhập đăng ký FreeTube (.db)",
|
||||
"Import NewPipe subscriptions (.json)": "Nhập đăng ký NewPipe (.json)",
|
||||
"Import NewPipe data (.zip)": "Nhập dữ liệu NewPipe (.zip)",
|
||||
"Import Invidious data": "Nhập dữ liệu Invidious dưới dạng JSON",
|
||||
"Import YouTube subscriptions": "Nhập các kênh đã đăng ký từ YouTube/OPML",
|
||||
"Import FreeTube subscriptions (.db)": "Nhập các kênh đã đăng ký từ FreeTube (.db)",
|
||||
"Import NewPipe subscriptions (.json)": "Nhập các kênh đã đăng ký từ NewPipe (.json)",
|
||||
"Import NewPipe data (.zip)": "Nhập dữ liệu từ NewPipe (.zip)",
|
||||
"Export": "Xuất",
|
||||
"Export subscriptions as OPML": "Xuất đăng ký dưới dạng OPML",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Xuất đăng ký dưới dạng OPML (cho NewPipe & FreeTube)",
|
||||
"Export subscriptions as OPML": "Xuất các kênh đã đăng ký dưới dạng OPML",
|
||||
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Xuất các kênh đã đăng ký dưới dạng OPML (cho NewPipe & FreeTube)",
|
||||
"Export data as JSON": "Xuất dữ liệu Invidious dưới dạng JSON",
|
||||
"Delete account?": "Xóa tài khoản?",
|
||||
"History": "Lịch sử",
|
||||
"An alternative front-end to YouTube": "Giao diện người dùng thay thế cho YouTube",
|
||||
"An alternative front-end to YouTube": "Một front-end thay thế cho YouTube",
|
||||
"JavaScript license information": "Thông tin giấy phép JavaScript",
|
||||
"source": "nguồn",
|
||||
"Log in": "Đăng nhập",
|
||||
"Log in/register": "Đăng nhập / đăng ký",
|
||||
"User ID": "Tên người dùng",
|
||||
"User ID": "ID người dùng",
|
||||
"Password": "Mật khẩu",
|
||||
"Time (h:mm:ss):": "Thời gian (h: mm: ss):",
|
||||
"Text CAPTCHA": "Nhắn tin tới CAPTCHA",
|
||||
"Image CAPTCHA": "Hình ảnh CAPTCHA",
|
||||
"Time (h:mm:ss):": "Thời gian (h:mm:ss):",
|
||||
"Text CAPTCHA": "CAPTCHA dạng chữ",
|
||||
"Image CAPTCHA": "CAPTCHA dạng ảnh",
|
||||
"Sign In": "Đăng nhập",
|
||||
"Register": "Đăng ký",
|
||||
"E-mail": "E-mail",
|
||||
"Preferences": "Sở thích",
|
||||
"preferences_category_player": "Tùy chọn trình phát video",
|
||||
"preferences_video_loop_label": "Luôn lặp lại: ",
|
||||
"preferences_autoplay_label": "Tự chạy: ",
|
||||
"preferences_autoplay_label": "Tự động phát: ",
|
||||
"preferences_continue_label": "Phát kế tiếp theo mặc định: ",
|
||||
"preferences_continue_autoplay_label": "Tự động phát video tiếp theo: ",
|
||||
"preferences_listen_label": "Nghe theo mặc định: ",
|
||||
"preferences_local_label": "Video proxy: ",
|
||||
"preferences_speed_label": "Tốc độ mặc định: ",
|
||||
"preferences_quality_label": "Chất lượng video ưa thích: ",
|
||||
"preferences_volume_label": "Âm lượng trình phát video: ",
|
||||
"preferences_volume_label": "Âm lượng video: ",
|
||||
"preferences_comments_label": "Nhận xét mặc định: ",
|
||||
"youtube": "YouTube",
|
||||
"reddit": "Reddit",
|
||||
|
@ -64,7 +64,7 @@
|
|||
"Fallback captions: ": "Phụ đề dự phòng: ",
|
||||
"preferences_related_videos_label": "Hiển thị các video có liên quan: ",
|
||||
"preferences_annotations_label": "Hiển thị chú thích theo mặc định: ",
|
||||
"preferences_extend_desc_label": "Tự động mở rộng mô tả video: ",
|
||||
"preferences_extend_desc_label": "Tự động mở rộng phần mô tả của video: ",
|
||||
"preferences_vr_mode_label": "Video 360 độ tương tác (yêu cầu WebGL): ",
|
||||
"preferences_category_visual": "Tùy chọn hình ảnh",
|
||||
"preferences_player_style_label": "Phong cách trình phát: ",
|
||||
|
@ -82,24 +82,24 @@
|
|||
"preferences_sort_label": "Sắp xếp video theo: ",
|
||||
"published": "được phát hành",
|
||||
"published - reverse": "đã xuất bản - đảo ngược",
|
||||
"alphabetically": "theo thứ tự bảng chữ cái",
|
||||
"alphabetically - reverse": "theo thứ tự bảng chữ cái - đảo ngược",
|
||||
"channel name": "Tên kênh",
|
||||
"channel name - reverse": "tên kênh - đảo ngược",
|
||||
"alphabetically": "Thứ tự (A - Z)",
|
||||
"alphabetically - reverse": "Thứ tự (Z - A)",
|
||||
"channel name": "Tên kênh (A - Z)",
|
||||
"channel name - reverse": "Tên kênh (Z - A)",
|
||||
"Only show latest video from channel: ": "Chỉ hiển thị video mới nhất từ kênh: ",
|
||||
"Only show latest unwatched video from channel: ": "Chỉ hiển thị video chưa xem mới nhất từ kênh: ",
|
||||
"preferences_unseen_only_label": "Chỉ hiển thị chưa xem: ",
|
||||
"preferences_unseen_only_label": "Chỉ hiển thị các video chưa từng xem: ",
|
||||
"preferences_notifications_only_label": "Chỉ hiển thị thông báo (nếu có): ",
|
||||
"Enable web notifications": "Bật thông báo web",
|
||||
"`x` uploaded a video": "` x` đã tải lên một video",
|
||||
"`x` is live": "` x` đang phát trực tiếp",
|
||||
"`x` uploaded a video": "`x` đã tải lên một video",
|
||||
"`x` is live": "`x` đang phát trực tiếp",
|
||||
"preferences_category_data": "Tùy chọn dữ liệu",
|
||||
"Clear watch history": "Xóa lịch sử xem",
|
||||
"Import/export data": "Nhập / xuất dữ liệu",
|
||||
"Change password": "Đổi mật khẩu",
|
||||
"Manage subscriptions": "Quản lý các mục đăng kí",
|
||||
"Manage tokens": "Quản lý mã thông báo",
|
||||
"Watch history": "Lịch sử xem",
|
||||
"Watch history": "Xem lịch sử",
|
||||
"Delete account": "Xóa tài khoản",
|
||||
"preferences_category_admin": "Tùy chọn quản trị viên",
|
||||
"preferences_default_home_label": "Trang chủ mặc định: ",
|
||||
|
@ -121,7 +121,7 @@
|
|||
"View privacy policy.": "Xem chính sách bảo mật.",
|
||||
"Trending": "Xu hướng",
|
||||
"Public": "Công khai",
|
||||
"Unlisted": "Không hiển thị",
|
||||
"Unlisted": "Không công khai",
|
||||
"Private": "Riêng tư",
|
||||
"View all playlists": "Xem tất cả danh sách phát",
|
||||
"Updated `x` ago": "Đã cập nhật` x` trước",
|
||||
|
@ -131,24 +131,24 @@
|
|||
"Title": "Tiêu đề",
|
||||
"Playlist privacy": "Bảo mật danh sách phát",
|
||||
"Editing playlist `x`": "Chỉnh sửa danh sách phát` x`",
|
||||
"Show more": "Cho xem nhiều hơn",
|
||||
"Show less": "Hiện ít hơn",
|
||||
"Show more": "Hiển thị thêm",
|
||||
"Show less": "Hiển thị ít hơn",
|
||||
"Watch on YouTube": "Xem trên YouTube",
|
||||
"Switch Invidious Instance": "Chuyển phiên bản Invidious",
|
||||
"Hide annotations": "Ẩn chú thích",
|
||||
"Show annotations": "Hiển thị chú thích",
|
||||
"Genre: ": "Thể loại: ",
|
||||
"License: ": "Giấy phép: ",
|
||||
"Family friendly? ": "Gia đình thân thiện? ",
|
||||
"Family friendly? ": "Thân thiện với gia đình? ",
|
||||
"Wilson score: ": "Điểm số Wilson: ",
|
||||
"Engagement: ": "Hôn ước: ",
|
||||
"Whitelisted regions: ": "Các vùng nằm trong danh sách trắng: ",
|
||||
"Blacklisted regions: ": "Khu vực nằm trong danh sách đen: ",
|
||||
"Blacklisted regions: ": "Các vùng nằm trong danh sách đen: ",
|
||||
"Shared `x`": "Chia sẻ` x`",
|
||||
"View Reddit comments": "Xem nhận xét trên Reddit",
|
||||
"Hide replies": "Ẩn câu trả lời",
|
||||
"Show replies": "Hiển thị câu trả lời",
|
||||
"Incorrect password": "Mật khẩu không đúng",
|
||||
"View Reddit comments": "Xem bình luận trên Reddit",
|
||||
"Hide replies": "Ẩn phản hồi",
|
||||
"Show replies": "Hiển thị phản hồi",
|
||||
"Incorrect password": "Mật khẩu không chính xác",
|
||||
"Wrong answer": "Câu trả lời sai",
|
||||
"Erroneous CAPTCHA": "CAPTCHA bị lỗi",
|
||||
"CAPTCHA is a required field": "CAPTCHA là trường bắt buộc",
|
||||
|
@ -190,35 +190,35 @@
|
|||
"Bulgarian": "Tiếng Bungari",
|
||||
"Burmese": "Tiếng Miến Điện",
|
||||
"Catalan": "Tiếng Catalan",
|
||||
"Cebuano": "Cebuano",
|
||||
"Cebuano": "Tiếng Cebu",
|
||||
"Chinese (Simplified)": "Tiếng Trung (Giản thể)",
|
||||
"Chinese (Traditional)": "Tiếng Trung (Phồn thể)",
|
||||
"Corsican": "Corsican",
|
||||
"Corsican": "Tiếng Corse",
|
||||
"Croatian": "Tiếng Croatia",
|
||||
"Czech": "Tiếng Séc",
|
||||
"Danish": "Người Đan Mạch",
|
||||
"Danish": "Tiếng Đan Mạch",
|
||||
"Dutch": "Tiếng Hà Lan",
|
||||
"Esperanto": "Quốc tế ngữ",
|
||||
"Estonian": "Tiếng Estonia",
|
||||
"Filipino": "Filipino",
|
||||
"Filipino": "Tiếng Philippines",
|
||||
"Finnish": "Tiếng Phần Lan",
|
||||
"French": "Người Pháp",
|
||||
"French": "Tiếng Pháp",
|
||||
"Galician": "Tiếng Galicia",
|
||||
"Georgian": "Tiếng Georgia",
|
||||
"German": "Tiếng Đức",
|
||||
"Greek": "Người Hy Lạp",
|
||||
"Gujarati": "Gujarati",
|
||||
"Haitian Creole": "Tiếng Creole của Haiti",
|
||||
"Hausa": "Hausa",
|
||||
"Greek": "Tiếng Hy Lạp",
|
||||
"Gujarati": "Tiếng Gujarat",
|
||||
"Haitian Creole": "Tiếng Creole (Haiti)",
|
||||
"Hausa": "Tiếng Hausa",
|
||||
"Hawaiian": "Tiếng Hawaii",
|
||||
"Hebrew": "Tiếng Do Thái",
|
||||
"Hindi": "Tiếng Hindi",
|
||||
"Hmong": "Hmong",
|
||||
"Hungarian": "Người Hungary",
|
||||
"Hmong": "Tiếng Hmong",
|
||||
"Hungarian": "Tiếng Hungary",
|
||||
"Icelandic": "Tiếng Iceland",
|
||||
"Igbo": "Igbo",
|
||||
"Igbo": "Tiếng Igbo",
|
||||
"Indonesian": "Tiếng Indonesia",
|
||||
"Irish": "Tiếng Ailen",
|
||||
"Irish": "Tiếng Ireland",
|
||||
"Italian": "Tiếng Ý",
|
||||
"Japanese": "Tiếng Nhật",
|
||||
"Javanese": "Tiếng Java",
|
||||
|
@ -237,37 +237,37 @@
|
|||
"Malagasy": "Tiếng Malagasy",
|
||||
"Malay": "Tiếng Mã Lai",
|
||||
"Malayalam": "Tiếng Malayalam",
|
||||
"Maltese": "Cây nho",
|
||||
"Maltese": "Tiếng Malta",
|
||||
"Maori": "Tiếng Maori",
|
||||
"Marathi": "Marathi",
|
||||
"Marathi": "Tiếng Marathi",
|
||||
"Mongolian": "Tiếng Mông Cổ",
|
||||
"Nepali": "Tiếng Nepal",
|
||||
"Norwegian Bokmål": "Tiếng Na Uy Bokmål",
|
||||
"Nyanja": "Nyanja",
|
||||
"Pashto": "Pashto",
|
||||
"Norwegian Bokmål": "Tiếng Na Uy (Bokmål)",
|
||||
"Nyanja": "Tiếng Chewa / Nyanja",
|
||||
"Pashto": "Tiếng Pashtun",
|
||||
"Persian": "Tiếng Ba Tư",
|
||||
"Polish": "Đánh bóng",
|
||||
"Polish": "Tiếng Ba Lan",
|
||||
"Portuguese": "Tiếng Bồ Đào Nha",
|
||||
"Punjabi": "Punjabi",
|
||||
"Punjabi": "Tiếng Punjab",
|
||||
"Romanian": "Tiếng Rumani",
|
||||
"Russian": "Tiếng Nga",
|
||||
"Samoan": "Samoan",
|
||||
"Scottish Gaelic": "Tiếng Gaelic Scotland",
|
||||
"Samoan": "Tiếng Samoa",
|
||||
"Scottish Gaelic": "Tiếng Gaelic (Scotland)",
|
||||
"Serbian": "Tiếng Serbia",
|
||||
"Shona": "Shona",
|
||||
"Sindhi": "Sindhi",
|
||||
"Sinhala": "Sinhala",
|
||||
"Shona": "Tiếng Shona",
|
||||
"Sindhi": "Tiếng Sindh",
|
||||
"Sinhala": "Tiếng Sinhala",
|
||||
"Slovak": "Tiếng Slovak",
|
||||
"Slovenian": "Tiếng Slovenia",
|
||||
"Somali": "Tiếng Somali",
|
||||
"Southern Sotho": "Southern Sotho",
|
||||
"Spanish": "Người Tây Ban Nha",
|
||||
"Spanish": "Tiếng Tây Ban Nha",
|
||||
"Spanish (Latin America)": "Tiếng Tây Ban Nha (Mỹ Latinh)",
|
||||
"Sundanese": "Tiếng Sundan",
|
||||
"Swahili": "Tiếng Swahili",
|
||||
"Swedish": "Tiếng Thụy Điển",
|
||||
"Tajik": "Tajik",
|
||||
"Tamil": "Tamil",
|
||||
"Tajik": "Tiếng Tajik",
|
||||
"Tamil": "Tiếng Tamil",
|
||||
"Telugu": "Tiếng Telugu",
|
||||
"Thai": "Tiếng Thái",
|
||||
"Turkish": "Tiếng Thổ Nhĩ Kỳ",
|
||||
|
@ -275,17 +275,17 @@
|
|||
"Urdu": "Tiếng Urdu",
|
||||
"Uzbek": "Tiếng Uzbek",
|
||||
"Vietnamese": "Tiếng Việt",
|
||||
"Welsh": "Người xứ Wales",
|
||||
"Western Frisian": "Western Frisian",
|
||||
"Xhosa": "Xhosa",
|
||||
"Yiddish": "Yiddish",
|
||||
"Yoruba": "Yoruba",
|
||||
"Welsh": "Tiếng Wales",
|
||||
"Western Frisian": "Tiếng Tây Frisia",
|
||||
"Xhosa": "Tiếng Nam Phi",
|
||||
"Yiddish": "Tiếng Yiddish",
|
||||
"Yoruba": "Tiếng Yoruba",
|
||||
"Zulu": "Tiếng Zulu",
|
||||
"Fallback comments: ": "Nhận xét dự phòng: ",
|
||||
"Popular": "Phổ biến",
|
||||
"Search": "Tìm kiếm",
|
||||
"Top": "Hàng đầu",
|
||||
"About": "Trong khoảng",
|
||||
"About": "Giới thiệu",
|
||||
"Rating: ": "Xếp hạng: ",
|
||||
"preferences_locale_label": "Ngôn ngữ: ",
|
||||
"View as playlist": "Xem dưới dạng danh sách phát",
|
||||
|
@ -295,45 +295,45 @@
|
|||
"News": "Tin tức",
|
||||
"Movies": "Phim",
|
||||
"Download": "Tải xuống",
|
||||
"Download as: ": "Tải tệp dưới dạng: ",
|
||||
"Download as: ": "Tải xuống dưới dạng: ",
|
||||
"%A %B %-d, %Y": "% A% B% -d,% Y",
|
||||
"(edited)": "(đã chỉnh sửa)",
|
||||
"YouTube comment permalink": "Liên kết cố định nhận xét trên YouTube",
|
||||
"permalink": "liên kết cố định",
|
||||
"`x` marked it with a ❤": "` x` đã đánh dấu nó bằng một ❤",
|
||||
"Audio mode": "Chế độ âm thanh",
|
||||
"Video mode": "Chế độ quay",
|
||||
"Audio mode": "Chế độ audio",
|
||||
"Video mode": "Chế độ video",
|
||||
"channel_tab_videos_label": "Video",
|
||||
"Playlists": "Danh sách phát",
|
||||
"channel_tab_community_label": "Cộng đồng",
|
||||
"search_filters_sort_option_relevance": "liên quan",
|
||||
"search_filters_sort_option_relevance": "Liên quan",
|
||||
"search_filters_sort_option_rating": "Xếp hạng",
|
||||
"search_filters_sort_option_date": "ngày",
|
||||
"search_filters_sort_option_views": "lượt xem",
|
||||
"search_filters_type_label": "content_type",
|
||||
"search_filters_duration_label": "thời lượng",
|
||||
"search_filters_features_label": "đặc trưng",
|
||||
"search_filters_sort_label": "sắp xếp",
|
||||
"search_filters_date_option_hour": "giờ",
|
||||
"search_filters_date_option_today": "hôm nay",
|
||||
"search_filters_date_option_week": "tuần",
|
||||
"search_filters_date_option_month": "tháng",
|
||||
"search_filters_date_option_year": "năm",
|
||||
"search_filters_sort_option_date": "Ngày tải lên",
|
||||
"search_filters_sort_option_views": "Lượt xem",
|
||||
"search_filters_type_label": "Thể loại",
|
||||
"search_filters_duration_label": "Thời lượng",
|
||||
"search_filters_features_label": "Đặc điểm",
|
||||
"search_filters_sort_label": "Sắp xếp theo",
|
||||
"search_filters_date_option_hour": "Một giờ qua",
|
||||
"search_filters_date_option_today": "Hôm nay",
|
||||
"search_filters_date_option_week": "Tuần này",
|
||||
"search_filters_date_option_month": "Tháng này",
|
||||
"search_filters_date_option_year": "Năm này",
|
||||
"search_filters_type_option_video": "video",
|
||||
"search_filters_type_option_channel": "kênh",
|
||||
"search_filters_type_option_playlist": "danh sách phát",
|
||||
"search_filters_type_option_movie": "bộ phim",
|
||||
"search_filters_type_option_show": "chỉ",
|
||||
"search_filters_features_option_hd": "hd",
|
||||
"search_filters_features_option_subtitles": "phụ đề",
|
||||
"search_filters_features_option_c_commons": "Commons sáng tạo",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "trực tiếp",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "vị trí",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_type_option_channel": "Kênh",
|
||||
"search_filters_type_option_playlist": "Danh sách phát",
|
||||
"search_filters_type_option_movie": "Phim",
|
||||
"search_filters_type_option_show": "Hiện",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Phụ đề",
|
||||
"search_filters_features_option_c_commons": "Giấy phép Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Trực tiếp",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Vị trí",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"Current version: ": "Phiên bản hiện tại: ",
|
||||
"search_filters_title": "bộ lọc",
|
||||
"search_filters_title": "Bộ lọc",
|
||||
"generic_playlists_count": "{{count}} danh sách phát",
|
||||
"generic_views_count": "{{count}} lượt xem",
|
||||
"View `x` comments": {
|
||||
|
@ -350,31 +350,31 @@
|
|||
"preferences_quality_dash_label": "Chất lượng video DASH ưa thích ",
|
||||
"preferences_quality_dash_option_auto": "Tự động",
|
||||
"Subscriptions": "Thuê bao",
|
||||
"View YouTube comments": "Hiển thị bình luận trên YouTube",
|
||||
"View YouTube comments": "Hiển thị bình luận từ YouTube",
|
||||
"View more comments on Reddit": "Hiển thị thêm bình luận từ Reddit",
|
||||
"Music in this video": "Nhạc trong video này",
|
||||
"Artist: ": "Nghệ sĩ: ",
|
||||
"Premieres `x`": "Phát lần đầu `x`",
|
||||
"preferences_region_label": "Nội dung theo quốc gia ",
|
||||
"search_message_change_filters_or_query": "Thử mở rộng nội dung tìm kiếm hoặc thay đổi bộ lọc.",
|
||||
"preferences_quality_option_small": "Nhỏ",
|
||||
"preferences_quality_option_small": "Thấp",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"Import/export": "Xuất/nhập dữ liệu",
|
||||
"preferences_quality_dash_option_4320p": "4320p",
|
||||
"Import/export": "Nhập/Xuất",
|
||||
"preferences_quality_dash_option_4320p": "4320p (8K)",
|
||||
"preferences_quality_option_dash": "DASH (tự tối ưu chất lượng)",
|
||||
"generic_subscriptions_count_0": "{{count}} người đăng kí",
|
||||
"preferences_quality_dash_option_1440p": "1440p",
|
||||
"preferences_quality_dash_option_1440p": "1440p (2K)",
|
||||
"preferences_quality_dash_option_480p": "480p",
|
||||
"preferences_quality_dash_option_2160p": "2160p",
|
||||
"preferences_quality_dash_option_2160p": "2160p (4K)",
|
||||
"search_message_no_results": "Tìm kiếm không có kết quả.",
|
||||
"preferences_quality_dash_option_1080p": "1080p",
|
||||
"preferences_quality_dash_option_720p": "720p",
|
||||
"preferences_quality_option_medium": "Trung bình",
|
||||
"Load more": "Hiển thị thêm",
|
||||
"Load more": "Tải thêm",
|
||||
"comments_points_count_0": "{{count}} điểm",
|
||||
"Import YouTube playlist (.csv)": "Nhập danh sách phát YouTube (.csv)",
|
||||
"Import YouTube playlist (.csv)": "Nhập các danh sách phát từ YouTube (.csv)",
|
||||
"preferences_quality_dash_option_best": "Tốt nhất",
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} thông báo chưa đọc",
|
||||
|
@ -382,10 +382,93 @@
|
|||
"search_message_use_another_instance": " Bạn cũng có thể tìm kiếm <a href=\"`x`\"> ở một phiên bản khác</a>.",
|
||||
"Standard YouTube license": "Giấy phép YouTube thông thường",
|
||||
"Album: ": "Album: ",
|
||||
"preferences_save_player_pos_label": "Lưu vị trí xem cuối cùng ",
|
||||
"preferences_save_player_pos_label": "Lưu vị trí xem: ",
|
||||
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Xin chào! Có vẻ như bạn đã tắt JavaScript. Bấm vào đây để xem bình luận, lưu ý rằng thời gian tải có thể lâu hơn.",
|
||||
"Chinese (China)": "Tiếng Trung (Trung Quốc)",
|
||||
"generic_button_cancel": "Hủy",
|
||||
"Chinese": "Tiếng Trung",
|
||||
"generic_button_delete": "Xóa"
|
||||
"generic_button_delete": "Xóa",
|
||||
"Korean (auto-generated)": "Tiếng Hàn (được tạo tự động)",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"channel_tab_podcasts_label": "Podcast",
|
||||
"Spanish (Mexico)": "Tiếng Tây Ban Nha (Mexico)",
|
||||
"search_filters_apply_button": "Áp dụng các mục đã chọn",
|
||||
"Download is disabled": "Tải xuống đã bị vô hiệu hóa.",
|
||||
"next_steps_error_message_go_to_youtube": "Đi đến YouTube",
|
||||
"German (auto-generated)": "Tiếng Đức (được tạo tự động)",
|
||||
"Japanese (auto-generated)": "Tiếng Nhật (được tạo tự động)",
|
||||
"footer_donate_page": "Ủng hộ",
|
||||
"crash_page_before_reporting": "Trước khi báo cáo lỗi, hãy chắc chắn rằng bạn đã:",
|
||||
"Channel Sponsor": "Nhà tài trợ của kênh",
|
||||
"videoinfo_started_streaming_x_ago": "Đã bắt đầu phát sóng `x` trước",
|
||||
"videoinfo_youTube_embed_link": "Nhúng",
|
||||
"channel_tab_streams_label": "Phát trực tiếp",
|
||||
"playlist_button_add_items": "Thêm video",
|
||||
"generic_count_minutes_0": "{{count}} phút",
|
||||
"user_saved_playlists": "`x` danh sách phát đã lưu",
|
||||
"Spanish (Spain)": "Tiếng Tây Ban Nha (Tây Ban Nha)",
|
||||
"crash_page_refresh": "Đã thử <a href=\"`x`\">tải lại trang</a>",
|
||||
"Chinese (Hong Kong)": "Tiếng Trung (Hồng Kông)",
|
||||
"generic_count_months_0": "{{count}} tháng",
|
||||
"download_subtitles": "Phụ đề - `x` (.vtt)",
|
||||
"generic_button_save": "Lưu",
|
||||
"crash_page_search_issue": "Tìm <a href=\"`x`\">lỗi có sẵn trên GitHub</a>",
|
||||
"none": "không",
|
||||
"English (United States)": "Tiếng Anh (Mỹ)",
|
||||
"next_steps_error_message_refresh": "Tải lại",
|
||||
"Video unavailable": "Video không có sẵn",
|
||||
"footer_source_code": "Mã nguồn",
|
||||
"search_filters_duration_option_short": "Ngắn (< 4 phút)",
|
||||
"search_filters_duration_option_long": "Dài (> 20 phút)",
|
||||
"tokens_count_0": "{{count}} mã thông báo",
|
||||
"Italian (auto-generated)": "Tiếng Ý (được tạo tự động)",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"channel_tab_releases_label": "Mới tải lên",
|
||||
"`x` ago": "`x` trước",
|
||||
"Interlingue": "Tiếng Khoa học Quốc tế",
|
||||
"generic_channels_count_0": "{{count}} kênh",
|
||||
"Chinese (Taiwan)": "Tiếng Trung (Đài Loan)",
|
||||
"adminprefs_modified_source_code_url_label": "URL tới kho lưu trữ mã nguồn đã sửa đổi",
|
||||
"Turkish (auto-generated)": "Tiếng Thổ Nhĩ Kỳ (được tạo tự động)",
|
||||
"Indonesian (auto-generated)": "Tiếng Indonesia (được tạo tự động)",
|
||||
"Portuguese (auto-generated)": "Tiếng Bồ Đào Nha (được tạo tự động)",
|
||||
"generic_count_years_0": "{{count}} năm",
|
||||
"videoinfo_invidious_embed_link": "Liên kết nhúng",
|
||||
"Popular enabled: ": "Đã bật phổ biến: ",
|
||||
"Spanish (auto-generated)": "Tiếng Tây Ban Nha (được tạo tự động)",
|
||||
"English (United Kingdom)": "Tiếng Anh Anh",
|
||||
"channel_tab_playlists_label": "Danh sách phát",
|
||||
"generic_button_edit": "Sửa",
|
||||
"search_filters_features_option_purchased": "Đã mua",
|
||||
"search_filters_date_option_none": "Mọi thời điểm",
|
||||
"Cantonese (Hong Kong)": "Tiếng Quảng Châu (Hồng Kông)",
|
||||
"crash_page_report_issue": "Nếu các điều trên không giúp được, xin hãy <a href=\"`x`\">tạo vấn đề mới trên GitHub</a> (ưu tiên tiếng Anh) và đính kèm đoạn chữ sau trong nội dung (giữ nguyên KHÔNG dịch):",
|
||||
"crash_page_switch_instance": "Đã thử <a href=\"`x`\">dùng một phiên bản khác</a>",
|
||||
"generic_count_weeks_0": "{{count}} tuần",
|
||||
"videoinfo_watch_on_youTube": "Xem trên YouTube",
|
||||
"footer_modfied_source_code": "Mã nguồn đã chỉnh sửa",
|
||||
"generic_button_rss": "RSS",
|
||||
"generic_count_hours_0": "{{count}} giờ",
|
||||
"French (auto-generated)": "Tiếng Pháp (được tạo tự động)",
|
||||
"crash_page_read_the_faq": "Đọc <a href=\"`x`\">Hỏi đáp thường gặp (FAQ)</a>",
|
||||
"user_created_playlists": "`x` danh sách phát đã tạo",
|
||||
"channel_tab_channels_label": "Kênh",
|
||||
"search_filters_type_option_all": "Mọi thể loại",
|
||||
"Russian (auto-generated)": "Tiếng Nga (được tạo tự động)",
|
||||
"comments_view_x_replies_0": "Xem {{count}} lượt trả lời",
|
||||
"footer_original_source_code": "Mã nguồn gốc",
|
||||
"Portuguese (Brazil)": "Tiếng Bồ Đào Nha (Brazil)",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"error_video_not_in_playlist": "Video không tồn tại trong danh sách phát. <a href=\"`x`\">Bấm để trở về trang chủ của danh sách phát.</a>",
|
||||
"Dutch (auto-generated)": "Tiếng Hà Lan (được tạo tự động)",
|
||||
"generic_count_days_0": "{{count}} ngày",
|
||||
"Vietnamese (auto-generated)": "Tiếng Việt (được tạo tự động)",
|
||||
"search_filters_duration_option_none": "Mọi thời lượng",
|
||||
"footer_documentation": "Tài liệu",
|
||||
"next_steps_error_message": "Bạn có thể thử: ",
|
||||
"Import YouTube watch history (.json)": "Nhập lịch sử xem từ YouTube (.json)",
|
||||
"search_filters_duration_option_medium": "Trung bình (4 - 20 phút)",
|
||||
"generic_count_seconds_0": "{{count}} giây",
|
||||
"search_filters_date_label": "Ngày tải lên",
|
||||
"crash_page_you_found_a_bug": "Có vẻ như bạn đã tìm ra lỗi trong Indivious!"
|
||||
}
|
||||
|
|
|
@ -461,6 +461,7 @@
|
|||
"Standard YouTube license": "标准 YouTube 许可证",
|
||||
"Download is disabled": "已禁用下载",
|
||||
"Import YouTube playlist (.csv)": "导入 YouTube 播放列表(.csv)",
|
||||
"Import YouTube watch history (.json)": "导入 YouTube 观看历史(.json)",
|
||||
"generic_button_cancel": "取消",
|
||||
"playlist_button_add_items": "添加视频",
|
||||
"generic_button_delete": "删除",
|
||||
|
@ -468,5 +469,7 @@
|
|||
"generic_button_edit": "编辑",
|
||||
"generic_button_save": "保存",
|
||||
"generic_button_rss": "RSS",
|
||||
"channel_tab_releases_label": "公告"
|
||||
"channel_tab_releases_label": "公告",
|
||||
"generic_channels_count_0": "{{count}} 个频道",
|
||||
"toggle_theme": "切换主题"
|
||||
}
|
||||
|
|
|
@ -461,6 +461,7 @@
|
|||
"Standard YouTube license": "標準 YouTube 授權條款",
|
||||
"Download is disabled": "已停用下載",
|
||||
"Import YouTube playlist (.csv)": "匯入 YouTube 播放清單 (.csv)",
|
||||
"Import YouTube watch history (.json)": "匯入 YouTube 觀看歷史 (.json)",
|
||||
"generic_button_cancel": "取消",
|
||||
"generic_button_edit": "編輯",
|
||||
"generic_button_save": "儲存",
|
||||
|
@ -468,5 +469,7 @@
|
|||
"generic_button_delete": "刪除",
|
||||
"playlist_button_add_items": "新增影片",
|
||||
"channel_tab_podcasts_label": "Podcast",
|
||||
"channel_tab_releases_label": "發布"
|
||||
"channel_tab_releases_label": "發布",
|
||||
"generic_channels_count_0": "{{count}} 個頻道",
|
||||
"toggle_theme": "切換佈景主題"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
require "../../spec_helper.cr"
|
||||
|
||||
MockLines = ["Line 1", "Line 2"]
|
||||
MockLinesWithEscapableCharacter = ["<Line 1>", "&Line 2>", '\u200E' + "Line\u200F 3", "\u00A0Line 4"]
|
||||
|
||||
Spectator.describe "WebVTT::Builder" do
|
||||
it "correctly builds a vtt file" do
|
||||
result = WebVTT.build do |vtt|
|
||||
2.times do |i|
|
||||
vtt.cue(
|
||||
Time::Span.new(seconds: i),
|
||||
Time::Span.new(seconds: i + 1),
|
||||
MockLines[i]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
expect(result).to eq([
|
||||
"WEBVTT",
|
||||
"",
|
||||
"00:00:00.000 --> 00:00:01.000",
|
||||
"Line 1",
|
||||
"",
|
||||
"00:00:01.000 --> 00:00:02.000",
|
||||
"Line 2",
|
||||
"",
|
||||
"",
|
||||
].join('\n'))
|
||||
end
|
||||
|
||||
it "correctly builds a vtt file with setting fields" do
|
||||
setting_fields = {
|
||||
"Kind" => "captions",
|
||||
"Language" => "en",
|
||||
}
|
||||
|
||||
result = WebVTT.build(setting_fields) do |vtt|
|
||||
2.times do |i|
|
||||
vtt.cue(
|
||||
Time::Span.new(seconds: i),
|
||||
Time::Span.new(seconds: i + 1),
|
||||
MockLines[i]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
expect(result).to eq([
|
||||
"WEBVTT",
|
||||
"Kind: captions",
|
||||
"Language: en",
|
||||
"",
|
||||
"00:00:00.000 --> 00:00:01.000",
|
||||
"Line 1",
|
||||
"",
|
||||
"00:00:01.000 --> 00:00:02.000",
|
||||
"Line 2",
|
||||
"",
|
||||
"",
|
||||
].join('\n'))
|
||||
end
|
||||
|
||||
it "properly escapes characters" do
|
||||
result = WebVTT.build do |vtt|
|
||||
4.times do |i|
|
||||
vtt.cue(Time::Span.new(seconds: i), Time::Span.new(seconds: i + 1), MockLinesWithEscapableCharacter[i])
|
||||
end
|
||||
end
|
||||
|
||||
expect(result).to eq([
|
||||
"WEBVTT",
|
||||
"",
|
||||
"00:00:00.000 --> 00:00:01.000",
|
||||
"<Line 1>",
|
||||
"",
|
||||
"00:00:01.000 --> 00:00:02.000",
|
||||
"&Line 2>",
|
||||
"",
|
||||
"00:00:02.000 --> 00:00:03.000",
|
||||
"‎Line‏ 3",
|
||||
"",
|
||||
"00:00:03.000 --> 00:00:04.000",
|
||||
" Line 4",
|
||||
"",
|
||||
"",
|
||||
].join('\n'))
|
||||
end
|
||||
end
|
|
@ -15,12 +15,15 @@ FORM_TESTS = {
|
|||
"ar" => I18next::Plurals::PluralForms::Special_Arabic,
|
||||
"be" => I18next::Plurals::PluralForms::Dual_Slavic,
|
||||
"cy" => I18next::Plurals::PluralForms::Special_Welsh,
|
||||
"fr" => I18next::Plurals::PluralForms::Special_French_Portuguese,
|
||||
"en" => I18next::Plurals::PluralForms::Single_not_one,
|
||||
"fr" => I18next::Plurals::PluralForms::Single_gt_one,
|
||||
"es" => I18next::Plurals::PluralForms::Special_Spanish_Italian,
|
||||
"ga" => I18next::Plurals::PluralForms::Special_Irish,
|
||||
"gd" => I18next::Plurals::PluralForms::Special_Scottish_Gaelic,
|
||||
"he" => I18next::Plurals::PluralForms::Special_Hebrew,
|
||||
"hr" => I18next::Plurals::PluralForms::Special_Hungarian_Serbian,
|
||||
"is" => I18next::Plurals::PluralForms::Special_Icelandic,
|
||||
"it" => I18next::Plurals::PluralForms::Special_Spanish_Italian,
|
||||
"jv" => I18next::Plurals::PluralForms::Special_Javanese,
|
||||
"kw" => I18next::Plurals::PluralForms::Special_Cornish,
|
||||
"lt" => I18next::Plurals::PluralForms::Special_Lithuanian,
|
||||
|
@ -30,13 +33,14 @@ FORM_TESTS = {
|
|||
"mt" => I18next::Plurals::PluralForms::Special_Maltese,
|
||||
"or" => I18next::Plurals::PluralForms::Special_Odia,
|
||||
"pl" => I18next::Plurals::PluralForms::Special_Polish_Kashubian,
|
||||
"pt" => I18next::Plurals::PluralForms::Single_gt_one,
|
||||
"pt-PT" => I18next::Plurals::PluralForms::Single_not_one,
|
||||
"pt-BR" => I18next::Plurals::PluralForms::Single_gt_one,
|
||||
"pt" => I18next::Plurals::PluralForms::Special_French_Portuguese,
|
||||
"pt-PT" => I18next::Plurals::PluralForms::Single_gt_one,
|
||||
"pt-BR" => I18next::Plurals::PluralForms::Special_French_Portuguese,
|
||||
"ro" => I18next::Plurals::PluralForms::Special_Romanian,
|
||||
"su" => I18next::Plurals::PluralForms::None,
|
||||
"sk" => I18next::Plurals::PluralForms::Special_Czech_Slovak,
|
||||
"sl" => I18next::Plurals::PluralForms::Special_Slovenian,
|
||||
"su" => I18next::Plurals::PluralForms::None,
|
||||
"sr" => I18next::Plurals::PluralForms::Special_Hungarian_Serbian,
|
||||
}
|
||||
|
||||
SUFFIX_TESTS = {
|
||||
|
@ -73,10 +77,18 @@ SUFFIX_TESTS = {
|
|||
{num: 1, suffix: ""},
|
||||
{num: 10, suffix: "_plural"},
|
||||
],
|
||||
"es" => [
|
||||
{num: 0, suffix: "_2"},
|
||||
{num: 1, suffix: "_0"},
|
||||
{num: 10, suffix: "_2"},
|
||||
{num: 6_000_000, suffix: "_1"},
|
||||
],
|
||||
"fr" => [
|
||||
{num: 0, suffix: ""},
|
||||
{num: 1, suffix: ""},
|
||||
{num: 10, suffix: "_plural"},
|
||||
{num: 0, suffix: "_0"},
|
||||
{num: 1, suffix: "_0"},
|
||||
{num: 10, suffix: "_2"},
|
||||
{num: 4_000_000, suffix: "_1"},
|
||||
{num: 6_260_000, suffix: "_2"},
|
||||
],
|
||||
"ga" => [
|
||||
{num: 1, suffix: "_0"},
|
||||
|
@ -155,31 +167,24 @@ SUFFIX_TESTS = {
|
|||
{num: 1, suffix: "_0"},
|
||||
{num: 5, suffix: "_2"},
|
||||
],
|
||||
"pt" => [
|
||||
{num: 0, suffix: ""},
|
||||
{num: 1, suffix: ""},
|
||||
{num: 10, suffix: "_plural"},
|
||||
"pt-BR" => [
|
||||
{num: 0, suffix: "_0"},
|
||||
{num: 1, suffix: "_0"},
|
||||
{num: 10, suffix: "_2"},
|
||||
{num: 42, suffix: "_2"},
|
||||
{num: 9_000_000, suffix: "_1"},
|
||||
],
|
||||
"pt-PT" => [
|
||||
{num: 0, suffix: "_plural"},
|
||||
{num: 1, suffix: ""},
|
||||
{num: 10, suffix: "_plural"},
|
||||
],
|
||||
"pt-BR" => [
|
||||
{num: 0, suffix: ""},
|
||||
{num: 1, suffix: ""},
|
||||
{num: 10, suffix: "_plural"},
|
||||
{num: 9_000_000, suffix: "_plural"},
|
||||
],
|
||||
"ro" => [
|
||||
{num: 0, suffix: "_1"},
|
||||
{num: 1, suffix: "_0"},
|
||||
{num: 20, suffix: "_2"},
|
||||
],
|
||||
"su" => [
|
||||
{num: 0, suffix: "_0"},
|
||||
{num: 1, suffix: "_0"},
|
||||
{num: 10, suffix: "_0"},
|
||||
],
|
||||
"sk" => [
|
||||
{num: 0, suffix: "_2"},
|
||||
{num: 1, suffix: "_0"},
|
||||
|
@ -191,6 +196,18 @@ SUFFIX_TESTS = {
|
|||
{num: 2, suffix: "_2"},
|
||||
{num: 3, suffix: "_3"},
|
||||
],
|
||||
"su" => [
|
||||
{num: 0, suffix: "_0"},
|
||||
{num: 1, suffix: "_0"},
|
||||
{num: 10, suffix: "_0"},
|
||||
],
|
||||
"sr" => [
|
||||
{num: 1, suffix: "_0"},
|
||||
{num: 51, suffix: "_0"},
|
||||
{num: 32, suffix: "_1"},
|
||||
{num: 100, suffix: "_2"},
|
||||
{num: 100_000, suffix: "_2"},
|
||||
],
|
||||
}
|
||||
|
||||
Spectator.describe "i18next_Plural_Resolver" do
|
||||
|
|
|
@ -3,18 +3,6 @@ require "../spec_helper"
|
|||
CONFIG = Config.from_yaml(File.open("config/config.example.yml"))
|
||||
|
||||
Spectator.describe "Helper" do
|
||||
describe "#produce_channel_videos_url" do
|
||||
it "correctly produces url for requesting page `x` of a channel's videos" do
|
||||
# expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw")).to eq("/browse_ajax?continuation=4qmFsgI8EhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaIEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0V4&gl=US&hl=en")
|
||||
#
|
||||
# expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw", sort_by: "popular")).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0V4R0FFPQ%3D%3D&gl=US&hl=en")
|
||||
|
||||
# expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw", page: 20)).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0l5TUE9PQ%3D%3D&gl=US&hl=en")
|
||||
|
||||
# expect(produce_channel_videos_url(ucid: "UC-9-kyTW8ZkZNDHQJ6FgpwQ", page: 20, sort_by: "popular")).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQy05LWt5VFc4WmtaTkRIUUo2Rmdwd1EaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0l5TUJnQg%3D%3D&gl=US&hl=en")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#produce_channel_search_continuation" do
|
||||
it "correctly produces token for searching a specific channel" do
|
||||
expect(produce_channel_search_continuation("UCXuqSBlHAE6Xw-yeJA0Tunw", "", 100)).to eq("4qmFsgJqEhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaIEVnWnpaV0Z5WTJnd0FUZ0JZQUY2QkVkS2IxaTRBUUE9WgCaAilicm93c2UtZmVlZFVDWHVxU0JsSEFFNlh3LXllSkEwVHVud3NlYXJjaA%3D%3D")
|
||||
|
|
|
@ -12,45 +12,45 @@ end
|
|||
# page of Youtube with any browser devtools HTML inspector.
|
||||
|
||||
DATE_FILTERS = {
|
||||
Invidious::Search::Filters::Date::Hour => "EgIIAQ%3D%3D",
|
||||
Invidious::Search::Filters::Date::Today => "EgIIAg%3D%3D",
|
||||
Invidious::Search::Filters::Date::Week => "EgIIAw%3D%3D",
|
||||
Invidious::Search::Filters::Date::Month => "EgIIBA%3D%3D",
|
||||
Invidious::Search::Filters::Date::Year => "EgIIBQ%3D%3D",
|
||||
Invidious::Search::Filters::Date::Hour => "EgIIAfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Date::Today => "EgIIAvABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Date::Week => "EgIIA_ABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Date::Month => "EgIIBPABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Date::Year => "EgIIBfABAQ%3D%3D",
|
||||
}
|
||||
|
||||
TYPE_FILTERS = {
|
||||
Invidious::Search::Filters::Type::Video => "EgIQAQ%3D%3D",
|
||||
Invidious::Search::Filters::Type::Channel => "EgIQAg%3D%3D",
|
||||
Invidious::Search::Filters::Type::Playlist => "EgIQAw%3D%3D",
|
||||
Invidious::Search::Filters::Type::Movie => "EgIQBA%3D%3D",
|
||||
Invidious::Search::Filters::Type::Video => "EgIQAfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Type::Channel => "EgIQAvABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Type::Playlist => "EgIQA_ABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Type::Movie => "EgIQBPABAQ%3D%3D",
|
||||
}
|
||||
|
||||
DURATION_FILTERS = {
|
||||
Invidious::Search::Filters::Duration::Short => "EgIYAQ%3D%3D",
|
||||
Invidious::Search::Filters::Duration::Medium => "EgIYAw%3D%3D",
|
||||
Invidious::Search::Filters::Duration::Long => "EgIYAg%3D%3D",
|
||||
Invidious::Search::Filters::Duration::Short => "EgIYAfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Duration::Medium => "EgIYA_ABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Duration::Long => "EgIYAvABAQ%3D%3D",
|
||||
}
|
||||
|
||||
FEATURE_FILTERS = {
|
||||
Invidious::Search::Filters::Features::Live => "EgJAAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::FourK => "EgJwAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::HD => "EgIgAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::Subtitles => "EgIoAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::CCommons => "EgIwAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::ThreeSixty => "EgJ4AQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::VR180 => "EgPQAQE%3D",
|
||||
Invidious::Search::Filters::Features::ThreeD => "EgI4AQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::HDR => "EgPIAQE%3D",
|
||||
Invidious::Search::Filters::Features::Location => "EgO4AQE%3D",
|
||||
Invidious::Search::Filters::Features::Purchased => "EgJIAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::Live => "EgJAAfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::FourK => "EgJwAfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::HD => "EgIgAfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::Subtitles => "EgIoAfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::CCommons => "EgIwAfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::ThreeSixty => "EgJ4AfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::VR180 => "EgPQAQHwAQE%3D",
|
||||
Invidious::Search::Filters::Features::ThreeD => "EgI4AfABAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::HDR => "EgPIAQHwAQE%3D",
|
||||
Invidious::Search::Filters::Features::Location => "EgO4AQHwAQE%3D",
|
||||
Invidious::Search::Filters::Features::Purchased => "EgJIAfABAQ%3D%3D",
|
||||
}
|
||||
|
||||
SORT_FILTERS = {
|
||||
Invidious::Search::Filters::Sort::Relevance => "",
|
||||
Invidious::Search::Filters::Sort::Date => "CAI%3D",
|
||||
Invidious::Search::Filters::Sort::Views => "CAM%3D",
|
||||
Invidious::Search::Filters::Sort::Rating => "CAE%3D",
|
||||
Invidious::Search::Filters::Sort::Relevance => "8AEB",
|
||||
Invidious::Search::Filters::Sort::Date => "CALwAQE%3D",
|
||||
Invidious::Search::Filters::Sort::Views => "CAPwAQE%3D",
|
||||
Invidious::Search::Filters::Sort::Rating => "CAHwAQE%3D",
|
||||
}
|
||||
|
||||
Spectator.describe Invidious::Search::Filters do
|
||||
|
|
|
@ -18,8 +18,8 @@ record AboutChannel,
|
|||
|
||||
def get_about_info(ucid, locale) : AboutChannel
|
||||
begin
|
||||
# "EgVhYm91dA==" is the base64-encoded protobuf object {"2:string":"about"}
|
||||
initdata = YoutubeAPI.browse(browse_id: ucid, params: "EgVhYm91dA==")
|
||||
# Fetch channel information from channel home page
|
||||
initdata = YoutubeAPI.browse(browse_id: ucid, params: "")
|
||||
rescue
|
||||
raise InfoException.new("Could not get channel info.")
|
||||
end
|
||||
|
|
|
@ -93,7 +93,7 @@ struct ChannelVideo
|
|||
def to_tuple
|
||||
{% begin %}
|
||||
{
|
||||
{{*@type.instance_vars.map(&.name)}}
|
||||
{{@type.instance_vars.map(&.name).splat}}
|
||||
}
|
||||
{% end %}
|
||||
end
|
||||
|
|
|
@ -24,7 +24,33 @@ def fetch_channel_community(ucid, cursor, locale, format, thin_mode)
|
|||
return extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode)
|
||||
end
|
||||
|
||||
def extract_channel_community(items, *, ucid, locale, format, thin_mode)
|
||||
def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode)
|
||||
object = {
|
||||
"2:string" => "community",
|
||||
"25:embedded" => {
|
||||
"22:string" => post_id.to_s,
|
||||
},
|
||||
"45:embedded" => {
|
||||
"2:varint" => 1_i64,
|
||||
"3:varint" => 1_i64,
|
||||
},
|
||||
}
|
||||
params = object.try { |i| Protodec::Any.cast_json(i) }
|
||||
.try { |i| Protodec::Any.from_json(i) }
|
||||
.try { |i| Base64.urlsafe_encode(i) }
|
||||
.try { |i| URI.encode_www_form(i) }
|
||||
|
||||
initial_data = YoutubeAPI.browse(ucid, params: params)
|
||||
|
||||
items = [] of JSON::Any
|
||||
extract_items(initial_data) do |item|
|
||||
items << item
|
||||
end
|
||||
|
||||
return extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode, is_single_post: true)
|
||||
end
|
||||
|
||||
def extract_channel_community(items, *, ucid, locale, format, thin_mode, is_single_post : Bool = false)
|
||||
if message = items[0]["messageRenderer"]?
|
||||
error_message = (message["text"]["simpleText"]? ||
|
||||
message["text"]["runs"]?.try &.[0]?.try &.["text"]?)
|
||||
|
@ -39,6 +65,9 @@ def extract_channel_community(items, *, ucid, locale, format, thin_mode)
|
|||
response = JSON.build do |json|
|
||||
json.object do
|
||||
json.field "authorId", ucid
|
||||
if is_single_post
|
||||
json.field "singlePost", true
|
||||
end
|
||||
json.field "comments" do
|
||||
json.array do
|
||||
items.each do |post|
|
||||
|
@ -240,8 +269,10 @@ def extract_channel_community(items, *, ucid, locale, format, thin_mode)
|
|||
end
|
||||
end
|
||||
end
|
||||
if cont = items.dig?(-1, "continuationItemRenderer", "continuationEndpoint", "continuationCommand", "token")
|
||||
json.field "continuation", extract_channel_community_cursor(cont.as_s)
|
||||
if !is_single_post
|
||||
if cont = items.dig?(-1, "continuationItemRenderer", "continuationEndpoint", "continuationCommand", "token")
|
||||
json.field "continuation", extract_channel_community_cursor(cont.as_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -62,12 +62,6 @@ def produce_channel_videos_continuation(ucid, page = 1, auto_generated = nil, so
|
|||
return continuation
|
||||
end
|
||||
|
||||
# Used in bypass_captcha_job.cr
|
||||
def produce_channel_videos_url(ucid, page = 1, auto_generated = nil, sort_by = "newest", v2 = false)
|
||||
continuation = produce_channel_videos_continuation(ucid, page, auto_generated, sort_by, v2)
|
||||
return "/browse_ajax?continuation=#{continuation}&gl=US&hl=en"
|
||||
end
|
||||
|
||||
module Invidious::Channel::Tabs
|
||||
extend self
|
||||
|
||||
|
|
|
@ -13,6 +13,51 @@ module Invidious::Comments
|
|||
|
||||
client_config = YoutubeAPI::ClientConfig.new(region: region)
|
||||
response = YoutubeAPI.next(continuation: ctoken, client_config: client_config)
|
||||
return parse_youtube(id, response, format, locale, thin_mode, sort_by)
|
||||
end
|
||||
|
||||
def fetch_community_post_comments(ucid, post_id)
|
||||
object = {
|
||||
"2:string" => "community",
|
||||
"25:embedded" => {
|
||||
"22:string" => post_id,
|
||||
},
|
||||
"45:embedded" => {
|
||||
"2:varint" => 1_i64,
|
||||
"3:varint" => 1_i64,
|
||||
},
|
||||
"53:embedded" => {
|
||||
"4:embedded" => {
|
||||
"6:varint" => 0_i64,
|
||||
"27:varint" => 1_i64,
|
||||
"29:string" => post_id,
|
||||
"30:string" => ucid,
|
||||
},
|
||||
"8:string" => "comments-section",
|
||||
},
|
||||
}
|
||||
|
||||
object_parsed = object.try { |i| Protodec::Any.cast_json(i) }
|
||||
.try { |i| Protodec::Any.from_json(i) }
|
||||
.try { |i| Base64.urlsafe_encode(i) }
|
||||
|
||||
object2 = {
|
||||
"80226972:embedded" => {
|
||||
"2:string" => ucid,
|
||||
"3:string" => object_parsed,
|
||||
},
|
||||
}
|
||||
|
||||
continuation = object2.try { |i| Protodec::Any.cast_json(i) }
|
||||
.try { |i| Protodec::Any.from_json(i) }
|
||||
.try { |i| Base64.urlsafe_encode(i) }
|
||||
.try { |i| URI.encode_www_form(i) }
|
||||
|
||||
initial_data = YoutubeAPI.browse(continuation: continuation)
|
||||
return initial_data
|
||||
end
|
||||
|
||||
def parse_youtube(id, response, format, locale, thin_mode, sort_by = "top", isPost = false)
|
||||
contents = nil
|
||||
|
||||
if on_response_received_endpoints = response["onResponseReceivedEndpoints"]?
|
||||
|
@ -68,7 +113,11 @@ module Invidious::Comments
|
|||
json.field "commentCount", comment_count
|
||||
end
|
||||
|
||||
json.field "videoId", id
|
||||
if isPost
|
||||
json.field "postId", id
|
||||
else
|
||||
json.field "videoId", id
|
||||
end
|
||||
|
||||
json.field "comments" do
|
||||
json.array do
|
||||
|
|
|
@ -48,7 +48,7 @@ struct ConfigPreferences
|
|||
def to_tuple
|
||||
{% begin %}
|
||||
{
|
||||
{{*@type.instance_vars.map { |var| "#{var.name}: #{var.name}".id }}}
|
||||
{{(@type.instance_vars.map { |var| "#{var.name}: #{var.name}".id }).splat}}
|
||||
}
|
||||
{% end %}
|
||||
end
|
||||
|
@ -133,10 +133,6 @@ class Config
|
|||
# Saved cookies in "name1=value1; name2=value2..." format
|
||||
@[YAML::Field(converter: Preferences::StringToCookies)]
|
||||
property cookies : HTTP::Cookies = HTTP::Cookies.new
|
||||
# Key for Anti-Captcha
|
||||
property captcha_key : String? = nil
|
||||
# API URL for Anti-Captcha
|
||||
property captcha_api_url : String = "https://api.anti-captcha.com"
|
||||
|
||||
# Playlist length limit
|
||||
property playlist_length_limit : Int32 = 500
|
||||
|
|
|
@ -15,7 +15,7 @@ module Invidious::Database::Statistics
|
|||
PG_DB.query_one(request, as: Int64)
|
||||
end
|
||||
|
||||
def count_users_active_1m : Int64
|
||||
def count_users_active_6m : Int64
|
||||
request = <<-SQL
|
||||
SELECT count(*) FROM users
|
||||
WHERE CURRENT_TIMESTAMP - updated < '6 months'
|
||||
|
@ -24,7 +24,7 @@ module Invidious::Database::Statistics
|
|||
PG_DB.query_one(request, as: Int64)
|
||||
end
|
||||
|
||||
def count_users_active_6m : Int64
|
||||
def count_users_active_1m : Int64
|
||||
request = <<-SQL
|
||||
SELECT count(*) FROM users
|
||||
WHERE CURRENT_TIMESTAMP - updated < '1 month'
|
||||
|
|
|
@ -33,7 +33,7 @@ module Invidious::Frontend::Comments
|
|||
<a href="javascript:void(0)" data-onclick="toggle_parent">[ − ]</a>
|
||||
<b><a href="https://www.reddit.com/user/#{child.author}">#{child.author}</a></b>
|
||||
#{translate_count(locale, "comments_points_count", child.score, NumberFormatting::Separator)}
|
||||
<span title="#{child.created_utc.to_s(translate(locale, "%a %B %-d %T %Y UTC"))}">#{translate(locale, "`x` ago", recode_date(child.created_utc, locale))}</span>
|
||||
<span title="#{child.created_utc.to_s("%a %B %-d %T %Y UTC")}">#{translate(locale, "`x` ago", recode_date(child.created_utc, locale))}</span>
|
||||
<a href="https://www.reddit.com#{child.permalink}" title="#{translate(locale, "permalink")}">#{translate(locale, "permalink")}</a>
|
||||
</p>
|
||||
<div>
|
||||
|
|
|
@ -23,6 +23,24 @@ module Invidious::Frontend::Comments
|
|||
</div>
|
||||
</div>
|
||||
END_HTML
|
||||
elsif comments["authorId"]? && !comments["singlePost"]?
|
||||
# for posts we should display a link to the post
|
||||
replies_count_text = translate_count(locale,
|
||||
"comments_view_x_replies",
|
||||
child["replyCount"].as_i64 || 0,
|
||||
NumberFormatting::Separator
|
||||
)
|
||||
|
||||
replies_html = <<-END_HTML
|
||||
<div class="pure-g">
|
||||
<div class="pure-u-1-24"></div>
|
||||
<div class="pure-u-23-24">
|
||||
<p>
|
||||
<a href="/post/#{child["commentId"]}?ucid=#{comments["authorId"]}">#{replies_count_text}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
END_HTML
|
||||
end
|
||||
|
||||
if !thin_mode
|
||||
|
@ -89,6 +107,36 @@ module Invidious::Frontend::Comments
|
|||
</div>
|
||||
END_HTML
|
||||
end
|
||||
when "multiImage"
|
||||
html << <<-END_HTML
|
||||
<section class="carousel">
|
||||
<a class="skip-link" href="#skip-#{child["commentId"]}">#{translate(locale, "carousel_skip")}</a>
|
||||
<div class="slides">
|
||||
END_HTML
|
||||
image_array = attachment["images"].as_a
|
||||
|
||||
image_array.each_index do |i|
|
||||
html << <<-END_HTML
|
||||
<div class="slides-item slide-#{i + 1}" id="#{child["commentId"]}-slide-#{i + 1}" aria-label="#{translate(locale, "carousel_slide", {"current" => (i + 1).to_s, "total" => image_array.size.to_s})}" tabindex="0">
|
||||
<img loading="lazy" src="/ggpht#{URI.parse(image_array[i][1]["url"].as_s).request_target}" alt="" />
|
||||
</div>
|
||||
END_HTML
|
||||
end
|
||||
|
||||
html << <<-END_HTML
|
||||
</div>
|
||||
<div class="carousel__nav">
|
||||
END_HTML
|
||||
attachment["images"].as_a.each_index do |i|
|
||||
html << <<-END_HTML
|
||||
<a class="slider-nav" href="##{child["commentId"]}-slide-#{i + 1}" aria-label="#{translate(locale, "carousel_go_to", (i + 1).to_s)}" tabindex="-1" aria-hidden="true">#{i + 1}</a>
|
||||
END_HTML
|
||||
end
|
||||
html << <<-END_HTML
|
||||
</div>
|
||||
<div id="skip-#{child["commentId"]}"></div>
|
||||
</section>
|
||||
END_HTML
|
||||
else nil # Ignore
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,8 +42,7 @@ module Invidious::Frontend::WatchPage
|
|||
str << translate(locale, "Download as: ")
|
||||
str << "</label>\n"
|
||||
|
||||
# TODO: remove inline style
|
||||
str << "\t\t<select style=\"width:100%\" name='download_widget' id='download_widget'>\n"
|
||||
str << "\t\t<select name='download_widget' id='download_widget'>\n"
|
||||
|
||||
# Non-DASH videos (audio+video)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# -------------------
|
||||
|
||||
macro error_template(*args)
|
||||
error_template_helper(env, {{*args}})
|
||||
error_template_helper(env, {{args.splat}})
|
||||
end
|
||||
|
||||
def github_details(summary : String, content : String)
|
||||
|
@ -95,7 +95,7 @@ end
|
|||
# -------------------
|
||||
|
||||
macro error_atom(*args)
|
||||
error_atom_helper(env, {{*args}})
|
||||
error_atom_helper(env, {{args.splat}})
|
||||
end
|
||||
|
||||
def error_atom_helper(env : HTTP::Server::Context, status_code : Int32, exception : Exception)
|
||||
|
@ -121,7 +121,7 @@ end
|
|||
# -------------------
|
||||
|
||||
macro error_json(*args)
|
||||
error_json_helper(env, {{*args}})
|
||||
error_json_helper(env, {{args.splat}})
|
||||
end
|
||||
|
||||
def error_json_helper(
|
||||
|
|
|
@ -142,63 +142,8 @@ class APIHandler < Kemal::Handler
|
|||
exclude ["/api/v1/auth/notifications"], "POST"
|
||||
|
||||
def call(env)
|
||||
return call_next env unless only_match? env
|
||||
|
||||
env.response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
|
||||
# Since /api/v1/notifications is an event-stream, we don't want
|
||||
# to wrap the response
|
||||
return call_next env if exclude_match? env
|
||||
|
||||
# Here we swap out the socket IO so we can modify the response as needed
|
||||
output = env.response.output
|
||||
env.response.output = IO::Memory.new
|
||||
|
||||
begin
|
||||
call_next env
|
||||
|
||||
env.response.output.rewind
|
||||
|
||||
if env.response.output.as(IO::Memory).size != 0 &&
|
||||
env.response.headers.includes_word?("Content-Type", "application/json")
|
||||
response = JSON.parse(env.response.output)
|
||||
|
||||
if fields_text = env.params.query["fields"]?
|
||||
begin
|
||||
JSONFilter.filter(response, fields_text)
|
||||
rescue ex
|
||||
env.response.status_code = 400
|
||||
response = {"error" => ex.message}
|
||||
end
|
||||
end
|
||||
|
||||
if env.params.query["pretty"]?.try &.== "1"
|
||||
response = response.to_pretty_json
|
||||
else
|
||||
response = response.to_json
|
||||
end
|
||||
else
|
||||
response = env.response.output.gets_to_end
|
||||
end
|
||||
rescue ex
|
||||
env.response.content_type = "application/json" if env.response.headers.includes_word?("Content-Type", "text/html")
|
||||
env.response.status_code = 500
|
||||
|
||||
if env.response.headers.includes_word?("Content-Type", "application/json")
|
||||
response = {"error" => ex.message || "Unspecified error"}
|
||||
|
||||
if env.params.query["pretty"]?.try &.== "1"
|
||||
response = response.to_pretty_json
|
||||
else
|
||||
response = response.to_json
|
||||
end
|
||||
end
|
||||
ensure
|
||||
env.response.output = output
|
||||
env.response.print response
|
||||
|
||||
env.response.flush
|
||||
end
|
||||
env.response.headers["Access-Control-Allow-Origin"] = "*" if only_match?(env)
|
||||
call_next env
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -78,15 +78,6 @@ def create_notification_stream(env, topics, connection_channel)
|
|||
video.published = published
|
||||
response = JSON.parse(video.to_json(locale, nil))
|
||||
|
||||
if fields_text = env.params.query["fields"]?
|
||||
begin
|
||||
JSONFilter.filter(response, fields_text)
|
||||
rescue ex
|
||||
env.response.status_code = 400
|
||||
response = {"error" => ex.message}
|
||||
end
|
||||
end
|
||||
|
||||
env.response.puts "id: #{id}"
|
||||
env.response.puts "data: #{response.to_json}"
|
||||
env.response.puts
|
||||
|
@ -113,15 +104,6 @@ def create_notification_stream(env, topics, connection_channel)
|
|||
Invidious::Database::ChannelVideos.select_notfications(topic, since_unix).each do |video|
|
||||
response = JSON.parse(video.to_json(locale))
|
||||
|
||||
if fields_text = env.params.query["fields"]?
|
||||
begin
|
||||
JSONFilter.filter(response, fields_text)
|
||||
rescue ex
|
||||
env.response.status_code = 400
|
||||
response = {"error" => ex.message}
|
||||
end
|
||||
end
|
||||
|
||||
env.response.puts "id: #{id}"
|
||||
env.response.puts "data: #{response.to_json}"
|
||||
env.response.puts
|
||||
|
@ -155,15 +137,6 @@ def create_notification_stream(env, topics, connection_channel)
|
|||
video.published = Time.unix(published)
|
||||
response = JSON.parse(video.to_json(locale, nil))
|
||||
|
||||
if fields_text = env.params.query["fields"]?
|
||||
begin
|
||||
JSONFilter.filter(response, fields_text)
|
||||
rescue ex
|
||||
env.response.status_code = 400
|
||||
response = {"error" => ex.message}
|
||||
end
|
||||
end
|
||||
|
||||
env.response.puts "id: #{id}"
|
||||
env.response.puts "data: #{response.to_json}"
|
||||
env.response.puts
|
||||
|
@ -208,3 +181,20 @@ def proxy_file(response, env)
|
|||
IO.copy response.body_io, env.response
|
||||
end
|
||||
end
|
||||
|
||||
# Fetch the playback requests tracker from the statistics endpoint.
|
||||
#
|
||||
# Creates a new tracker when unavailable.
|
||||
def get_playback_statistic
|
||||
if (tracker = Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"]) && tracker.as(Hash).empty?
|
||||
tracker = {
|
||||
"totalRequests" => 0_i64,
|
||||
"successfulRequests" => 0_i64,
|
||||
"ratio" => 0_f64,
|
||||
}
|
||||
|
||||
Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"] = tracker
|
||||
end
|
||||
|
||||
return tracker.as(Hash(String, Int64 | Float64))
|
||||
end
|
||||
|
|
|
@ -78,7 +78,7 @@ def load_all_locales
|
|||
return locales
|
||||
end
|
||||
|
||||
def translate(locale : String?, key : String, text : String | Nil = nil) : String
|
||||
def translate(locale : String?, key : String, text : String | Hash(String, String) | Nil = nil) : String
|
||||
# Log a warning if "key" doesn't exist in en-US locale and return
|
||||
# that key as the text, so this is more or less transparent to the user.
|
||||
if !LOCALES["en-US"].has_key?(key)
|
||||
|
@ -101,10 +101,12 @@ def translate(locale : String?, key : String, text : String | Nil = nil) : Strin
|
|||
match_length = 0
|
||||
|
||||
raw_data.as_h.each do |hash_key, value|
|
||||
if md = text.try &.match(/#{hash_key}/)
|
||||
if md[0].size >= match_length
|
||||
translation = value.as_s
|
||||
match_length = md[0].size
|
||||
if text.is_a?(String)
|
||||
if md = text.try &.match(/#{hash_key}/)
|
||||
if md[0].size >= match_length
|
||||
translation = value.as_s
|
||||
match_length = md[0].size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -114,8 +116,13 @@ def translate(locale : String?, key : String, text : String | Nil = nil) : Strin
|
|||
raise "Invalid translation \"#{raw_data}\""
|
||||
end
|
||||
|
||||
if text
|
||||
if text.is_a?(String)
|
||||
translation = translation.gsub("`x`", text)
|
||||
elsif text.is_a?(Hash(String, String))
|
||||
# adds support for multi string interpolation. Based on i18next https://www.i18next.com/translation-function/interpolation#basic
|
||||
text.each_key do |hash_key|
|
||||
translation = translation.gsub("{{#{hash_key}}}", text[hash_key])
|
||||
end
|
||||
end
|
||||
|
||||
return translation
|
||||
|
|
|
@ -35,27 +35,35 @@ module I18next::Plurals
|
|||
Special_Slovenian = 21
|
||||
Special_Hebrew = 22
|
||||
Special_Odia = 23
|
||||
|
||||
# Mixed v3/v4 rules in Weblate
|
||||
# `es`, `pt` and `pt-PT` doesn't seem to have been refreshed
|
||||
# by weblate yet, but I suspect it will happen one day.
|
||||
# See: https://github.com/translate/translate/issues/4873
|
||||
Special_French_Portuguese
|
||||
Special_Hungarian_Serbian
|
||||
Special_Spanish_Italian
|
||||
end
|
||||
|
||||
private PLURAL_SETS = {
|
||||
PluralForms::Single_gt_one => [
|
||||
"ach", "ak", "am", "arn", "br", "fil", "fr", "gun", "ln", "mfe", "mg",
|
||||
"mi", "oc", "pt", "pt-BR", "tg", "tl", "ti", "tr", "uz", "wa",
|
||||
"ach", "ak", "am", "arn", "br", "fa", "fil", "gun", "ln", "mfe", "mg",
|
||||
"mi", "oc", "pt-PT", "tg", "tl", "ti", "tr", "uz", "wa",
|
||||
],
|
||||
PluralForms::Single_not_one => [
|
||||
"af", "an", "ast", "az", "bg", "bn", "ca", "da", "de", "dev", "el", "en",
|
||||
"eo", "es", "et", "eu", "fi", "fo", "fur", "fy", "gl", "gu", "ha", "hi",
|
||||
"hu", "hy", "ia", "it", "kk", "kn", "ku", "lb", "mai", "ml", "mn", "mr",
|
||||
"eo", "et", "eu", "fi", "fo", "fur", "fy", "gl", "gu", "ha", "hi",
|
||||
"hu", "hy", "ia", "kk", "kn", "ku", "lb", "mai", "ml", "mn", "mr",
|
||||
"nah", "nap", "nb", "ne", "nl", "nn", "no", "nso", "pa", "pap", "pms",
|
||||
"ps", "pt-PT", "rm", "sco", "se", "si", "so", "son", "sq", "sv", "sw",
|
||||
"ps", "rm", "sco", "se", "si", "so", "son", "sq", "sv", "sw",
|
||||
"ta", "te", "tk", "ur", "yo",
|
||||
],
|
||||
PluralForms::None => [
|
||||
"ay", "bo", "cgg", "fa", "ht", "id", "ja", "jbo", "ka", "km", "ko", "ky",
|
||||
"ay", "bo", "cgg", "ht", "id", "ja", "jbo", "ka", "km", "ko", "ky",
|
||||
"lo", "ms", "sah", "su", "th", "tt", "ug", "vi", "wo", "zh",
|
||||
],
|
||||
PluralForms::Dual_Slavic => [
|
||||
"be", "bs", "cnr", "dz", "hr", "ru", "sr", "uk",
|
||||
"be", "bs", "cnr", "dz", "ru", "uk",
|
||||
],
|
||||
}
|
||||
|
||||
|
@ -81,6 +89,14 @@ module I18next::Plurals
|
|||
"ro" => PluralForms::Special_Romanian,
|
||||
"sk" => PluralForms::Special_Czech_Slovak,
|
||||
"sl" => PluralForms::Special_Slovenian,
|
||||
# Mixed v3/v4 rules
|
||||
"es" => PluralForms::Special_Spanish_Italian,
|
||||
"fr" => PluralForms::Special_French_Portuguese,
|
||||
"hr" => PluralForms::Special_Hungarian_Serbian,
|
||||
"it" => PluralForms::Special_Spanish_Italian,
|
||||
"pt" => PluralForms::Special_French_Portuguese,
|
||||
"pt" => PluralForms::Special_French_Portuguese,
|
||||
"sr" => PluralForms::Special_Hungarian_Serbian,
|
||||
}
|
||||
|
||||
# These are the v1 and v2 compatible suffixes.
|
||||
|
@ -150,9 +166,8 @@ module I18next::Plurals
|
|||
end
|
||||
|
||||
def get_plural_form(locale : String) : PluralForms
|
||||
# Extract the ISO 639-1 or 639-2 code from an RFC 5646 language code,
|
||||
# except for pt-BR and pt-PT which needs to be kept as-is.
|
||||
if !locale.matches?(/^pt-(BR|PT)$/)
|
||||
# Extract the ISO 639-1 or 639-2 code from an RFC 5646 language code
|
||||
if !locale.matches?(/^pt-PT$/)
|
||||
locale = locale.split('-')[0]
|
||||
end
|
||||
|
||||
|
@ -246,6 +261,10 @@ module I18next::Plurals
|
|||
when .special_slovenian? then return special_slovenian(count)
|
||||
when .special_hebrew? then return special_hebrew(count)
|
||||
when .special_odia? then return special_odia(count)
|
||||
# Mixed v3/v4 forms
|
||||
when .special_spanish_italian? then return special_cldr_Spanish_Italian(count)
|
||||
when .special_french_portuguese? then return special_cldr_French_Portuguese(count)
|
||||
when .special_hungarian_serbian? then return special_cldr_Hungarian_Serbian(count)
|
||||
else
|
||||
# default, if nothing matched above
|
||||
return 0_u8
|
||||
|
@ -507,5 +526,42 @@ module I18next::Plurals
|
|||
def self.special_odia(count : Int) : UInt8
|
||||
return (count == 1) ? 0_u8 : 1_u8
|
||||
end
|
||||
|
||||
# -------------------
|
||||
# "v3.5" rules
|
||||
# -------------------
|
||||
|
||||
# Plural form for Spanish & Italian languages
|
||||
#
|
||||
# This rule is mostly compliant to CLDR v42
|
||||
#
|
||||
def self.special_cldr_Spanish_Italian(count : Int) : UInt8
|
||||
return 0_u8 if (count == 1) # one
|
||||
return 1_u8 if (count != 0 && count % 1_000_000 == 0) # many
|
||||
return 2_u8 # other
|
||||
end
|
||||
|
||||
# Plural form for French and Portuguese
|
||||
#
|
||||
# This rule is mostly compliant to CLDR v42
|
||||
#
|
||||
def self.special_cldr_French_Portuguese(count : Int) : UInt8
|
||||
return 0_u8 if (count == 0 || count == 1) # one
|
||||
return 1_u8 if (count % 1_000_000 == 0) # many
|
||||
return 2_u8 # other
|
||||
end
|
||||
|
||||
# Plural form for Hungarian and Serbian
|
||||
#
|
||||
# This rule is mostly compliant to CLDR v42
|
||||
#
|
||||
def self.special_cldr_Hungarian_Serbian(count : Int) : UInt8
|
||||
n_mod_10 = count % 10
|
||||
n_mod_100 = count % 100
|
||||
|
||||
return 0_u8 if (n_mod_10 == 1 && n_mod_100 != 11) # one
|
||||
return 1_u8 if (2 <= n_mod_10 <= 4 && (n_mod_100 < 12 || 14 < n_mod_100)) # few
|
||||
return 2_u8 # other
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,248 +0,0 @@
|
|||
module JSONFilter
|
||||
alias BracketIndex = Hash(Int64, Int64)
|
||||
|
||||
alias GroupedFieldsValue = String | Array(GroupedFieldsValue)
|
||||
alias GroupedFieldsList = Array(GroupedFieldsValue)
|
||||
|
||||
class FieldsParser
|
||||
class ParseError < Exception
|
||||
end
|
||||
|
||||
# Returns the `Regex` pattern used to match nest groups
|
||||
def self.nest_group_pattern : Regex
|
||||
# uses a '.' character to match json keys as they are allowed
|
||||
# to contain any unicode codepoint
|
||||
/(?:|,)(?<groupname>[^,\n]*?)\(/
|
||||
end
|
||||
|
||||
# Returns the `Regex` pattern used to check if there are any empty nest groups
|
||||
def self.unnamed_nest_group_pattern : Regex
|
||||
/^\(|\(\(|\/\(/
|
||||
end
|
||||
|
||||
def self.parse_fields(fields_text : String, &) : Nil
|
||||
if fields_text.empty?
|
||||
raise FieldsParser::ParseError.new "Fields is empty"
|
||||
end
|
||||
|
||||
opening_bracket_count = fields_text.count('(')
|
||||
closing_bracket_count = fields_text.count(')')
|
||||
|
||||
if opening_bracket_count != closing_bracket_count
|
||||
bracket_type = opening_bracket_count > closing_bracket_count ? "opening" : "closing"
|
||||
raise FieldsParser::ParseError.new "There are too many #{bracket_type} brackets (#{opening_bracket_count}:#{closing_bracket_count})"
|
||||
elsif match_result = unnamed_nest_group_pattern.match(fields_text)
|
||||
raise FieldsParser::ParseError.new "Unnamed nest group at position #{match_result.begin}"
|
||||
end
|
||||
|
||||
# first, handle top-level single nested properties: items/id, playlistItems/snippet, etc
|
||||
parse_single_nests(fields_text) { |nest_list| yield nest_list }
|
||||
|
||||
# next, handle nest groups: items(id, etag, etc)
|
||||
parse_nest_groups(fields_text) { |nest_list| yield nest_list }
|
||||
end
|
||||
|
||||
def self.parse_single_nests(fields_text : String, &) : Nil
|
||||
single_nests = remove_nest_groups(fields_text)
|
||||
|
||||
if !single_nests.empty?
|
||||
property_nests = single_nests.split(',')
|
||||
|
||||
property_nests.each do |nest|
|
||||
nest_list = nest.split('/')
|
||||
if nest_list.includes? ""
|
||||
raise FieldsParser::ParseError.new "Empty key in nest list: #{nest_list}"
|
||||
end
|
||||
yield nest_list
|
||||
end
|
||||
# else
|
||||
# raise FieldsParser::ParseError.new "Empty key in nest list 22: #{fields_text} | #{single_nests}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_nest_groups(fields_text : String, &) : Nil
|
||||
nest_stack = [] of NamedTuple(group_name: String, closing_bracket_index: Int64)
|
||||
bracket_pairs = get_bracket_pairs(fields_text, true)
|
||||
|
||||
text_index = 0
|
||||
regex_index = 0
|
||||
|
||||
while regex_result = self.nest_group_pattern.match(fields_text, regex_index)
|
||||
raw_match = regex_result[0]
|
||||
group_name = regex_result["groupname"]
|
||||
|
||||
text_index = regex_result.begin
|
||||
regex_index = regex_result.end
|
||||
|
||||
if text_index.nil? || regex_index.nil?
|
||||
raise FieldsParser::ParseError.new "Received invalid index while parsing nest groups: text_index: #{text_index} | regex_index: #{regex_index}"
|
||||
end
|
||||
|
||||
offset = raw_match.starts_with?(',') ? 1 : 0
|
||||
|
||||
opening_bracket_index = (text_index + group_name.size) + offset
|
||||
closing_bracket_index = bracket_pairs[opening_bracket_index]
|
||||
content_start = opening_bracket_index + 1
|
||||
|
||||
content = fields_text[content_start...closing_bracket_index]
|
||||
|
||||
if content.empty?
|
||||
raise FieldsParser::ParseError.new "Empty nest group at position #{content_start}"
|
||||
else
|
||||
content = remove_nest_groups(content)
|
||||
end
|
||||
|
||||
while nest_stack.size > 0 && closing_bracket_index > nest_stack[nest_stack.size - 1][:closing_bracket_index]
|
||||
if nest_stack.size
|
||||
nest_stack.pop
|
||||
end
|
||||
end
|
||||
|
||||
group_name.split('/').each do |name|
|
||||
nest_stack.push({
|
||||
group_name: name,
|
||||
closing_bracket_index: closing_bracket_index,
|
||||
})
|
||||
end
|
||||
|
||||
if !content.empty?
|
||||
properties = content.split(',')
|
||||
|
||||
properties.each do |prop|
|
||||
nest_list = nest_stack.map { |nest_prop| nest_prop[:group_name] }
|
||||
|
||||
if !prop.empty?
|
||||
if prop.includes?('/')
|
||||
parse_single_nests(prop) { |list| nest_list += list }
|
||||
else
|
||||
nest_list.push prop
|
||||
end
|
||||
else
|
||||
raise FieldsParser::ParseError.new "Empty key in nest list: #{nest_list << prop}"
|
||||
end
|
||||
|
||||
yield nest_list
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.remove_nest_groups(text : String) : String
|
||||
content_bracket_pairs = get_bracket_pairs(text, false)
|
||||
|
||||
content_bracket_pairs.each_key.to_a.reverse.each do |opening_bracket|
|
||||
closing_bracket = content_bracket_pairs[opening_bracket]
|
||||
last_comma = text.rindex(',', opening_bracket) || 0
|
||||
|
||||
text = text[0...last_comma] + text[closing_bracket + 1...text.size]
|
||||
end
|
||||
|
||||
return text.starts_with?(',') ? text[1...text.size] : text
|
||||
end
|
||||
|
||||
def self.get_bracket_pairs(text : String, recursive = true) : BracketIndex
|
||||
istart = [] of Int64
|
||||
bracket_index = BracketIndex.new
|
||||
|
||||
text.each_char_with_index do |char, index|
|
||||
if char == '('
|
||||
istart.push(index.to_i64)
|
||||
end
|
||||
|
||||
if char == ')'
|
||||
begin
|
||||
opening = istart.pop
|
||||
if recursive || (!recursive && istart.size == 0)
|
||||
bracket_index[opening] = index.to_i64
|
||||
end
|
||||
rescue
|
||||
raise FieldsParser::ParseError.new "No matching opening parenthesis at: #{index}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if istart.size != 0
|
||||
idx = istart.pop
|
||||
raise FieldsParser::ParseError.new "No matching closing parenthesis at: #{idx}"
|
||||
end
|
||||
|
||||
return bracket_index
|
||||
end
|
||||
end
|
||||
|
||||
class FieldsGrouper
|
||||
alias SkeletonValue = Hash(String, SkeletonValue)
|
||||
|
||||
def self.create_json_skeleton(fields_text : String) : SkeletonValue
|
||||
root_hash = {} of String => SkeletonValue
|
||||
|
||||
FieldsParser.parse_fields(fields_text) do |nest_list|
|
||||
current_item = root_hash
|
||||
nest_list.each do |key|
|
||||
if current_item[key]?
|
||||
current_item = current_item[key]
|
||||
else
|
||||
current_item[key] = {} of String => SkeletonValue
|
||||
current_item = current_item[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
root_hash
|
||||
end
|
||||
|
||||
def self.create_grouped_fields_list(json_skeleton : SkeletonValue) : GroupedFieldsList
|
||||
grouped_fields_list = GroupedFieldsList.new
|
||||
json_skeleton.each do |key, value|
|
||||
grouped_fields_list.push key
|
||||
|
||||
nested_keys = create_grouped_fields_list(value)
|
||||
grouped_fields_list.push nested_keys unless nested_keys.empty?
|
||||
end
|
||||
return grouped_fields_list
|
||||
end
|
||||
end
|
||||
|
||||
class FilterError < Exception
|
||||
end
|
||||
|
||||
def self.filter(item : JSON::Any, fields_text : String, in_place : Bool = true)
|
||||
skeleton = FieldsGrouper.create_json_skeleton(fields_text)
|
||||
grouped_fields_list = FieldsGrouper.create_grouped_fields_list(skeleton)
|
||||
filter(item, grouped_fields_list, in_place)
|
||||
end
|
||||
|
||||
def self.filter(item : JSON::Any, grouped_fields_list : GroupedFieldsList, in_place : Bool = true) : JSON::Any
|
||||
item = item.clone unless in_place
|
||||
|
||||
if !item.as_h? && !item.as_a?
|
||||
raise FilterError.new "Can't filter '#{item}' by #{grouped_fields_list}"
|
||||
end
|
||||
|
||||
top_level_keys = Array(String).new
|
||||
grouped_fields_list.each do |value|
|
||||
if value.is_a? String
|
||||
top_level_keys.push value
|
||||
elsif value.is_a? Array
|
||||
if !top_level_keys.empty?
|
||||
key_to_filter = top_level_keys.last
|
||||
|
||||
if item.as_h?
|
||||
filter(item[key_to_filter], value, in_place: true)
|
||||
elsif item.as_a?
|
||||
item.as_a.each { |arr_item| filter(arr_item[key_to_filter], value, in_place: true) }
|
||||
end
|
||||
else
|
||||
raise FilterError.new "Tried to filter while top level keys list is empty"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if item.as_h?
|
||||
item.as_h.select! top_level_keys
|
||||
elsif item.as_a?
|
||||
item.as_a.map { |value| filter(value, top_level_keys, in_place: true) }
|
||||
end
|
||||
|
||||
item
|
||||
end
|
||||
end
|
|
@ -186,6 +186,7 @@ struct SearchChannel
|
|||
property author_thumbnail : String
|
||||
property subscriber_count : Int32
|
||||
property video_count : Int32
|
||||
property channel_handle : String?
|
||||
property description_html : String
|
||||
property auto_generated : Bool
|
||||
property author_verified : Bool
|
||||
|
@ -214,6 +215,7 @@ struct SearchChannel
|
|||
json.field "autoGenerated", self.auto_generated
|
||||
json.field "subCount", self.subscriber_count
|
||||
json.field "videoCount", self.video_count
|
||||
json.field "channelHandle", self.channel_handle
|
||||
|
||||
json.field "description", html_to_content(self.description_html)
|
||||
json.field "descriptionHtml", self.description_html
|
||||
|
|
|
@ -262,7 +262,7 @@ def get_referer(env, fallback = "/", unroll = true)
|
|||
end
|
||||
|
||||
referer = referer.request_target
|
||||
referer = "/" + referer.gsub(/[^\/?@&%=\-_.:,0-9a-zA-Z]/, "").lstrip("/\\")
|
||||
referer = "/" + referer.gsub(/[^\/?@&%=\-_.:,*0-9a-zA-Z]/, "").lstrip("/\\")
|
||||
|
||||
if referer == env.request.path
|
||||
referer = fallback
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
# Namespace for logic relating to generating WebVTT files
|
||||
#
|
||||
# Probably not compliant to WebVTT's specs but it is enough for Invidious.
|
||||
module WebVTT
|
||||
# A WebVTT builder generates WebVTT files
|
||||
private class Builder
|
||||
# See https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API#cue_payload
|
||||
private ESCAPE_SUBSTITUTIONS = {
|
||||
'&' => "&",
|
||||
'<' => "<",
|
||||
'>' => ">",
|
||||
'\u200E' => "‎",
|
||||
'\u200F' => "‏",
|
||||
'\u00A0' => " ",
|
||||
}
|
||||
|
||||
def initialize(@io : IO)
|
||||
end
|
||||
|
||||
# Writes an vtt cue with the specified time stamp and contents
|
||||
def cue(start_time : Time::Span, end_time : Time::Span, text : String)
|
||||
timestamp(start_time, end_time)
|
||||
@io << self.escape(text)
|
||||
@io << "\n\n"
|
||||
end
|
||||
|
||||
private def timestamp(start_time : Time::Span, end_time : Time::Span)
|
||||
timestamp_component(start_time)
|
||||
@io << " --> "
|
||||
timestamp_component(end_time)
|
||||
|
||||
@io << '\n'
|
||||
end
|
||||
|
||||
private def timestamp_component(timestamp : Time::Span)
|
||||
@io << timestamp.hours.to_s.rjust(2, '0')
|
||||
@io << ':' << timestamp.minutes.to_s.rjust(2, '0')
|
||||
@io << ':' << timestamp.seconds.to_s.rjust(2, '0')
|
||||
@io << '.' << timestamp.milliseconds.to_s.rjust(3, '0')
|
||||
end
|
||||
|
||||
private def escape(text : String) : String
|
||||
return text.gsub(ESCAPE_SUBSTITUTIONS)
|
||||
end
|
||||
|
||||
def document(setting_fields : Hash(String, String)? = nil, &)
|
||||
@io << "WEBVTT\n"
|
||||
|
||||
if setting_fields
|
||||
setting_fields.each do |name, value|
|
||||
@io << name << ": " << value << '\n'
|
||||
end
|
||||
end
|
||||
|
||||
@io << '\n'
|
||||
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the resulting `String` of writing WebVTT to the yielded `WebVTT::Builder`
|
||||
#
|
||||
# ```
|
||||
# string = WebVTT.build do |vtt|
|
||||
# vtt.cue(Time::Span.new(seconds: 1), Time::Span.new(seconds: 2), "Line 1")
|
||||
# vtt.cue(Time::Span.new(seconds: 2), Time::Span.new(seconds: 3), "Line 2")
|
||||
# end
|
||||
#
|
||||
# string # => "WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nLine 1\n\n00:00:02.000 --> 00:00:03.000\nLine 2\n\n"
|
||||
# ```
|
||||
#
|
||||
# Accepts an optional settings fields hash to add settings attribute to the resulting vtt file.
|
||||
def self.build(setting_fields : Hash(String, String)? = nil, &)
|
||||
String.build do |str|
|
||||
builder = Builder.new(str)
|
||||
builder.document(setting_fields) do
|
||||
yield builder
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,135 +0,0 @@
|
|||
class Invidious::Jobs::BypassCaptchaJob < Invidious::Jobs::BaseJob
|
||||
def begin
|
||||
loop do
|
||||
begin
|
||||
random_video = PG_DB.query_one?("select id, ucid from (select id, ucid from channel_videos limit 1000) as s ORDER BY RANDOM() LIMIT 1", as: {id: String, ucid: String})
|
||||
if !random_video
|
||||
random_video = {id: "zj82_v2R6ts", ucid: "UCK87Lox575O_HCHBWaBSyGA"}
|
||||
end
|
||||
{"/watch?v=#{random_video["id"]}&gl=US&hl=en&has_verified=1&bpctr=9999999999", produce_channel_videos_url(ucid: random_video["ucid"])}.each do |path|
|
||||
response = YT_POOL.client &.get(path)
|
||||
if response.body.includes?("To continue with your YouTube experience, please fill out the form below.")
|
||||
html = XML.parse_html(response.body)
|
||||
form = html.xpath_node(%(//form[@action="/das_captcha"])).not_nil!
|
||||
site_key = form.xpath_node(%(.//div[@id="recaptcha"])).try &.["data-sitekey"]
|
||||
s_value = form.xpath_node(%(.//div[@id="recaptcha"])).try &.["data-s"]
|
||||
|
||||
inputs = {} of String => String
|
||||
form.xpath_nodes(%(.//input[@name])).map do |node|
|
||||
inputs[node["name"]] = node["value"]
|
||||
end
|
||||
|
||||
headers = response.cookies.add_request_headers(HTTP::Headers.new)
|
||||
|
||||
response = JSON.parse(HTTP::Client.post(CONFIG.captcha_api_url + "/createTask",
|
||||
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
||||
"clientKey" => CONFIG.captcha_key,
|
||||
"task" => {
|
||||
"type" => "NoCaptchaTaskProxyless",
|
||||
"websiteURL" => "https://www.youtube.com#{path}",
|
||||
"websiteKey" => site_key,
|
||||
"recaptchaDataSValue" => s_value,
|
||||
},
|
||||
}.to_json).body)
|
||||
|
||||
raise response["error"].as_s if response["error"]?
|
||||
task_id = response["taskId"].as_i
|
||||
|
||||
loop do
|
||||
sleep 10.seconds
|
||||
|
||||
response = JSON.parse(HTTP::Client.post(CONFIG.captcha_api_url + "/getTaskResult",
|
||||
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
||||
"clientKey" => CONFIG.captcha_key,
|
||||
"taskId" => task_id,
|
||||
}.to_json).body)
|
||||
|
||||
if response["status"]?.try &.== "ready"
|
||||
break
|
||||
elsif response["errorId"]?.try &.as_i != 0
|
||||
raise response["errorDescription"].as_s
|
||||
end
|
||||
end
|
||||
|
||||
inputs["g-recaptcha-response"] = response["solution"]["gRecaptchaResponse"].as_s
|
||||
headers["Cookies"] = response["solution"]["cookies"].as_h?.try &.map { |k, v| "#{k}=#{v}" }.join("; ") || ""
|
||||
response = YT_POOL.client &.post("/das_captcha", headers, form: inputs)
|
||||
|
||||
response.cookies
|
||||
.select { |cookie| cookie.name != "PREF" }
|
||||
.each { |cookie| CONFIG.cookies << cookie }
|
||||
|
||||
# Persist cookies between runs
|
||||
File.write("config/config.yml", CONFIG.to_yaml)
|
||||
elsif response.headers["Location"]?.try &.includes?("/sorry/index")
|
||||
location = response.headers["Location"].try { |u| URI.parse(u) }
|
||||
headers = HTTP::Headers{":authority" => location.host.not_nil!}
|
||||
response = YT_POOL.client &.get(location.request_target, headers)
|
||||
|
||||
html = XML.parse_html(response.body)
|
||||
form = html.xpath_node(%(//form[@action="index"])).not_nil!
|
||||
site_key = form.xpath_node(%(.//div[@id="recaptcha"])).try &.["data-sitekey"]
|
||||
s_value = form.xpath_node(%(.//div[@id="recaptcha"])).try &.["data-s"]
|
||||
|
||||
inputs = {} of String => String
|
||||
form.xpath_nodes(%(.//input[@name])).map do |node|
|
||||
inputs[node["name"]] = node["value"]
|
||||
end
|
||||
|
||||
captcha_client = HTTPClient.new(URI.parse(CONFIG.captcha_api_url))
|
||||
captcha_client.family = CONFIG.force_resolve || Socket::Family::INET
|
||||
response = JSON.parse(captcha_client.post("/createTask",
|
||||
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
||||
"clientKey" => CONFIG.captcha_key,
|
||||
"task" => {
|
||||
"type" => "NoCaptchaTaskProxyless",
|
||||
"websiteURL" => location.to_s,
|
||||
"websiteKey" => site_key,
|
||||
"recaptchaDataSValue" => s_value,
|
||||
},
|
||||
}.to_json).body)
|
||||
|
||||
captcha_client.close
|
||||
|
||||
raise response["error"].as_s if response["error"]?
|
||||
task_id = response["taskId"].as_i
|
||||
|
||||
loop do
|
||||
sleep 10.seconds
|
||||
|
||||
response = JSON.parse(captcha_client.post("/getTaskResult",
|
||||
headers: HTTP::Headers{"Content-Type" => "application/json"}, body: {
|
||||
"clientKey" => CONFIG.captcha_key,
|
||||
"taskId" => task_id,
|
||||
}.to_json).body)
|
||||
|
||||
if response["status"]?.try &.== "ready"
|
||||
break
|
||||
elsif response["errorId"]?.try &.as_i != 0
|
||||
raise response["errorDescription"].as_s
|
||||
end
|
||||
end
|
||||
|
||||
inputs["g-recaptcha-response"] = response["solution"]["gRecaptchaResponse"].as_s
|
||||
headers["Cookies"] = response["solution"]["cookies"].as_h?.try &.map { |k, v| "#{k}=#{v}" }.join("; ") || ""
|
||||
response = YT_POOL.client &.post("/sorry/index", headers: headers, form: inputs)
|
||||
headers = HTTP::Headers{
|
||||
"Cookie" => URI.parse(response.headers["location"]).query_params["google_abuse"].split(";")[0],
|
||||
}
|
||||
cookies = HTTP::Cookies.from_client_headers(headers)
|
||||
|
||||
cookies.each { |cookie| CONFIG.cookies << cookie }
|
||||
|
||||
# Persist cookies between runs
|
||||
File.write("config/config.yml", CONFIG.to_yaml)
|
||||
end
|
||||
end
|
||||
rescue ex
|
||||
LOGGER.error("BypassCaptchaJob: #{ex.message}")
|
||||
ensure
|
||||
sleep 1.minute
|
||||
Fiber.yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,6 +18,13 @@ class Invidious::Jobs::StatisticsRefreshJob < Invidious::Jobs::BaseJob
|
|||
"updatedAt" => Time.utc.to_unix,
|
||||
"lastChannelRefreshedAt" => 0_i64,
|
||||
},
|
||||
|
||||
#
|
||||
# "totalRequests" => 0_i64,
|
||||
# "successfulRequests" => 0_i64
|
||||
# "ratio" => 0_i64
|
||||
#
|
||||
"playback" => {} of String => Int64 | Float64,
|
||||
}
|
||||
|
||||
private getter db : DB::Database
|
||||
|
@ -30,7 +37,7 @@ class Invidious::Jobs::StatisticsRefreshJob < Invidious::Jobs::BaseJob
|
|||
|
||||
loop do
|
||||
refresh_stats
|
||||
sleep 1.minute
|
||||
sleep 10.minute
|
||||
Fiber.yield
|
||||
end
|
||||
end
|
||||
|
@ -49,12 +56,15 @@ class Invidious::Jobs::StatisticsRefreshJob < Invidious::Jobs::BaseJob
|
|||
users = STATISTICS.dig("usage", "users").as(Hash(String, Int64))
|
||||
|
||||
users["total"] = Invidious::Database::Statistics.count_users_total
|
||||
users["activeHalfyear"] = Invidious::Database::Statistics.count_users_active_1m
|
||||
users["activeMonth"] = Invidious::Database::Statistics.count_users_active_6m
|
||||
users["activeHalfyear"] = Invidious::Database::Statistics.count_users_active_6m
|
||||
users["activeMonth"] = Invidious::Database::Statistics.count_users_active_1m
|
||||
|
||||
STATISTICS["metadata"] = {
|
||||
"updatedAt" => Time.utc.to_unix,
|
||||
"lastChannelRefreshedAt" => Invidious::Database::Statistics.channel_last_update.try &.to_unix || 0_i64,
|
||||
}
|
||||
|
||||
# Reset playback requests tracker
|
||||
STATISTICS["playback"] = {} of String => Int64 | Float64
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,6 +39,7 @@ module Invidious::JSONify::APIv1
|
|||
json.field "author", video.author
|
||||
json.field "authorId", video.ucid
|
||||
json.field "authorUrl", "/channel/#{video.ucid}"
|
||||
json.field "authorVerified", video.author_verified
|
||||
|
||||
json.field "authorThumbnails" do
|
||||
json.array do
|
||||
|
|
|
@ -343,6 +343,59 @@ module Invidious::Routes::API::V1::Channels
|
|||
end
|
||||
end
|
||||
|
||||
def self.post(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
env.response.content_type = "application/json"
|
||||
id = env.params.url["id"].to_s
|
||||
ucid = env.params.query["ucid"]?
|
||||
|
||||
thin_mode = env.params.query["thin_mode"]?
|
||||
thin_mode = thin_mode == "true"
|
||||
|
||||
format = env.params.query["format"]?
|
||||
format ||= "json"
|
||||
|
||||
if ucid.nil?
|
||||
response = YoutubeAPI.resolve_url("https://www.youtube.com/post/#{id}")
|
||||
return error_json(400, "Invalid post ID") if response["error"]?
|
||||
ucid = response.dig("endpoint", "browseEndpoint", "browseId").as_s
|
||||
else
|
||||
ucid = ucid.to_s
|
||||
end
|
||||
|
||||
begin
|
||||
fetch_channel_community_post(ucid, id, locale, format, thin_mode)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
end
|
||||
end
|
||||
|
||||
def self.post_comments(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
env.response.content_type = "application/json"
|
||||
|
||||
id = env.params.url["id"]
|
||||
|
||||
thin_mode = env.params.query["thin_mode"]?
|
||||
thin_mode = thin_mode == "true"
|
||||
|
||||
format = env.params.query["format"]?
|
||||
format ||= "json"
|
||||
|
||||
continuation = env.params.query["continuation"]?
|
||||
|
||||
case continuation
|
||||
when nil, ""
|
||||
ucid = env.params.query["ucid"]
|
||||
comments = Comments.fetch_community_post_comments(ucid, id)
|
||||
else
|
||||
comments = YoutubeAPI.browse(continuation: continuation)
|
||||
end
|
||||
return Comments.parse_youtube(id, comments, format, locale, thin_mode, isPost: true)
|
||||
end
|
||||
|
||||
def self.channels(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
ucid = env.params.url["ucid"]
|
||||
|
|
|
@ -6,6 +6,22 @@ module Invidious::Routes::API::V1::Misc
|
|||
if !CONFIG.statistics_enabled
|
||||
return {"software" => SOFTWARE}.to_json
|
||||
else
|
||||
# Calculate playback success rate
|
||||
if (tracker = Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"]?)
|
||||
tracker = tracker.as(Hash(String, Int64 | Float64))
|
||||
|
||||
if !tracker.empty?
|
||||
total_requests = tracker["totalRequests"]
|
||||
success_count = tracker["successfulRequests"]
|
||||
|
||||
if total_requests.zero?
|
||||
tracker["ratio"] = 1_i64
|
||||
else
|
||||
tracker["ratio"] = (success_count / (total_requests)).round(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Invidious::Jobs::StatisticsRefreshJob::STATISTICS.to_json
|
||||
end
|
||||
end
|
||||
|
@ -162,17 +178,22 @@ module Invidious::Routes::API::V1::Misc
|
|||
resolved_url = YoutubeAPI.resolve_url(url.as(String))
|
||||
endpoint = resolved_url["endpoint"]
|
||||
pageType = endpoint.dig?("commandMetadata", "webCommandMetadata", "webPageType").try &.as_s || ""
|
||||
if resolved_ucid = endpoint.dig?("watchEndpoint", "videoId")
|
||||
elsif resolved_ucid = endpoint.dig?("browseEndpoint", "browseId")
|
||||
elsif pageType == "WEB_PAGE_TYPE_UNKNOWN"
|
||||
if pageType == "WEB_PAGE_TYPE_UNKNOWN"
|
||||
return error_json(400, "Unknown url")
|
||||
end
|
||||
|
||||
sub_endpoint = endpoint["watchEndpoint"]? || endpoint["browseEndpoint"]? || endpoint
|
||||
params = sub_endpoint.try &.dig?("params")
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
end
|
||||
JSON.build do |json|
|
||||
json.object do
|
||||
json.field "ucid", resolved_ucid.try &.as_s || ""
|
||||
json.field "ucid", sub_endpoint["browseId"].as_s if sub_endpoint["browseId"]?
|
||||
json.field "videoId", sub_endpoint["videoId"].as_s if sub_endpoint["videoId"]?
|
||||
json.field "playlistId", sub_endpoint["playlistId"].as_s if sub_endpoint["playlistId"]?
|
||||
json.field "startTimeSeconds", sub_endpoint["startTimeSeconds"].as_i if sub_endpoint["startTimeSeconds"]?
|
||||
json.field "params", params.try &.as_s
|
||||
json.field "pageType", pageType
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,11 +32,14 @@ module Invidious::Routes::API::V1::Search
|
|||
|
||||
begin
|
||||
client = HTTP::Client.new("suggestqueries-clients6.youtube.com")
|
||||
url = "/complete/search?client=youtube&hl=en&gl=#{region}&q=#{URI.encode_www_form(query)}&xssi=t&gs_ri=youtube&ds=yt"
|
||||
client.before_request { |r| add_yt_headers(r) }
|
||||
|
||||
url = "/complete/search?client=youtube&hl=en&gl=#{region}&q=#{URI.encode_www_form(query)}&gs_ri=youtube&ds=yt"
|
||||
|
||||
response = client.get(url).body
|
||||
client.close
|
||||
|
||||
body = JSON.parse(response[5..-1]).as_a
|
||||
body = JSON.parse(response[19..-2]).as_a
|
||||
suggestions = body[1].as_a[0..-2]
|
||||
|
||||
JSON.build do |json|
|
||||
|
|
|
@ -101,20 +101,17 @@ module Invidious::Routes::API::V1::Videos
|
|||
if caption.name.includes? "auto-generated"
|
||||
caption_xml = YT_POOL.client &.get(url).body
|
||||
|
||||
settings_field = {
|
||||
"Kind" => "captions",
|
||||
"Language" => "#{tlang || caption.language_code}",
|
||||
}
|
||||
|
||||
if caption_xml.starts_with?("<?xml")
|
||||
webvtt = caption.timedtext_to_vtt(caption_xml, tlang)
|
||||
else
|
||||
caption_xml = XML.parse(caption_xml)
|
||||
|
||||
webvtt = String.build do |str|
|
||||
str << <<-END_VTT
|
||||
WEBVTT
|
||||
Kind: captions
|
||||
Language: #{tlang || caption.language_code}
|
||||
|
||||
|
||||
END_VTT
|
||||
|
||||
webvtt = WebVTT.build(settings_field) do |webvtt|
|
||||
caption_nodes = caption_xml.xpath_nodes("//transcript/text")
|
||||
caption_nodes.each_with_index do |node, i|
|
||||
start_time = node["start"].to_f.seconds
|
||||
|
@ -127,9 +124,6 @@ module Invidious::Routes::API::V1::Videos
|
|||
end_time = start_time + duration
|
||||
end
|
||||
|
||||
start_time = "#{start_time.hours.to_s.rjust(2, '0')}:#{start_time.minutes.to_s.rjust(2, '0')}:#{start_time.seconds.to_s.rjust(2, '0')}.#{start_time.milliseconds.to_s.rjust(3, '0')}"
|
||||
end_time = "#{end_time.hours.to_s.rjust(2, '0')}:#{end_time.minutes.to_s.rjust(2, '0')}:#{end_time.seconds.to_s.rjust(2, '0')}.#{end_time.milliseconds.to_s.rjust(3, '0')}"
|
||||
|
||||
text = HTML.unescape(node.content)
|
||||
text = text.gsub(/<font color="#[a-fA-F0-9]{6}">/, "")
|
||||
text = text.gsub(/<\/font>/, "")
|
||||
|
@ -137,27 +131,22 @@ module Invidious::Routes::API::V1::Videos
|
|||
text = "<v #{md["name"]}>#{md["text"]}</v>"
|
||||
end
|
||||
|
||||
str << <<-END_CUE
|
||||
#{start_time} --> #{end_time}
|
||||
#{text}
|
||||
|
||||
|
||||
END_CUE
|
||||
webvtt.cue(start_time, end_time, text)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
# Some captions have "align:[start/end]" and "position:[num]%"
|
||||
# attributes. Those are causing issues with VideoJS, which is unable
|
||||
# to properly align the captions on the video, so we remove them.
|
||||
#
|
||||
# See: https://github.com/iv-org/invidious/issues/2391
|
||||
webvtt = YT_POOL.client &.get("#{url}&format=vtt").body
|
||||
webvtt = YT_POOL.client &.get("#{url}&fmt=vtt").body
|
||||
|
||||
if webvtt.starts_with?("<?xml")
|
||||
webvtt = caption.timedtext_to_vtt(webvtt)
|
||||
else
|
||||
webvtt = YT_POOL.client &.get("#{url}&format=vtt").body
|
||||
.gsub(/([0-9:.]{12} --> [0-9:.]{12}).+/, "\\1")
|
||||
# Some captions have "align:[start/end]" and "position:[num]%"
|
||||
# attributes. Those are causing issues with VideoJS, which is unable
|
||||
# to properly align the captions on the video, so we remove them.
|
||||
#
|
||||
# See: https://github.com/iv-org/invidious/issues/2391
|
||||
webvtt = webvtt.gsub(/([0-9:.]{12} --> [0-9:.]{12}).+/, "\\1")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -215,11 +204,7 @@ module Invidious::Routes::API::V1::Videos
|
|||
storyboard = storyboard[0]
|
||||
end
|
||||
|
||||
String.build do |str|
|
||||
str << <<-END_VTT
|
||||
WEBVTT
|
||||
END_VTT
|
||||
|
||||
WebVTT.build do |vtt|
|
||||
start_time = 0.milliseconds
|
||||
end_time = storyboard[:interval].milliseconds
|
||||
|
||||
|
@ -231,12 +216,8 @@ module Invidious::Routes::API::V1::Videos
|
|||
|
||||
storyboard[:storyboard_height].times do |j|
|
||||
storyboard[:storyboard_width].times do |k|
|
||||
str << <<-END_CUE
|
||||
#{start_time}.000 --> #{end_time}.000
|
||||
#{url}#xywh=#{storyboard[:width] * k},#{storyboard[:height] * j},#{storyboard[:width] - 2},#{storyboard[:height]}
|
||||
|
||||
|
||||
END_CUE
|
||||
current_cue_url = "#{url}#xywh=#{storyboard[:width] * k},#{storyboard[:height] * j},#{storyboard[:width] - 2},#{storyboard[:height]}"
|
||||
vtt.cue(start_time, end_time, current_cue_url)
|
||||
|
||||
start_time += storyboard[:interval].milliseconds
|
||||
end_time += storyboard[:interval].milliseconds
|
||||
|
@ -382,4 +363,47 @@ module Invidious::Routes::API::V1::Videos
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.clips(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
||||
env.response.content_type = "application/json"
|
||||
|
||||
clip_id = env.params.url["id"]
|
||||
region = env.params.query["region"]?
|
||||
proxy = {"1", "true"}.any? &.== env.params.query["local"]?
|
||||
|
||||
response = YoutubeAPI.resolve_url("https://www.youtube.com/clip/#{clip_id}")
|
||||
return error_json(400, "Invalid clip ID") if response["error"]?
|
||||
|
||||
video_id = response.dig?("endpoint", "watchEndpoint", "videoId").try &.as_s
|
||||
return error_json(400, "Invalid clip ID") if video_id.nil?
|
||||
|
||||
start_time = nil
|
||||
end_time = nil
|
||||
clip_title = nil
|
||||
|
||||
if params = response.dig?("endpoint", "watchEndpoint", "params").try &.as_s
|
||||
start_time, end_time, clip_title = parse_clip_parameters(params)
|
||||
end
|
||||
|
||||
begin
|
||||
video = get_video(video_id, region: region)
|
||||
rescue ex : NotFoundException
|
||||
return error_json(404, ex)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
end
|
||||
|
||||
return JSON.build do |json|
|
||||
json.object do
|
||||
json.field "startTime", start_time
|
||||
json.field "endTime", end_time
|
||||
json.field "clipTitle", clip_title
|
||||
json.field "video" do
|
||||
Invidious::JSONify::APIv1.video(video, json, locale: locale, proxy: proxy)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{% skip_file if flag?(:api_only) %}
|
||||
|
||||
module Invidious::Routes::Channels
|
||||
# Redirection for unsupported routes ("tabs")
|
||||
def self.redirect_home(env)
|
||||
ucid = env.params.url["ucid"]
|
||||
return env.redirect "/channel/#{URI.encode_www_form(ucid)}"
|
||||
end
|
||||
|
||||
def self.home(env)
|
||||
self.videos(env)
|
||||
end
|
||||
|
@ -159,6 +165,11 @@ module Invidious::Routes::Channels
|
|||
end
|
||||
locale, user, subscriptions, continuation, ucid, channel = data
|
||||
|
||||
# redirect to post page
|
||||
if lb = env.params.query["lb"]?
|
||||
env.redirect "/post/#{URI.encode_www_form(lb)}?ucid=#{URI.encode_www_form(ucid)}"
|
||||
end
|
||||
|
||||
thin_mode = env.params.query["thin_mode"]? || env.get("preferences").as(Preferences).thin_mode
|
||||
thin_mode = thin_mode == "true"
|
||||
|
||||
|
@ -187,6 +198,44 @@ module Invidious::Routes::Channels
|
|||
templated "community"
|
||||
end
|
||||
|
||||
def self.post(env)
|
||||
# /post/{postId}
|
||||
id = env.params.url["id"]
|
||||
ucid = env.params.query["ucid"]?
|
||||
|
||||
prefs = env.get("preferences").as(Preferences)
|
||||
|
||||
locale = prefs.locale
|
||||
|
||||
thin_mode = env.params.query["thin_mode"]? || prefs.thin_mode
|
||||
thin_mode = thin_mode == "true"
|
||||
|
||||
nojs = env.params.query["nojs"]?
|
||||
|
||||
nojs ||= "0"
|
||||
nojs = nojs == "1"
|
||||
|
||||
if !ucid.nil?
|
||||
ucid = ucid.to_s
|
||||
post_response = fetch_channel_community_post(ucid, id, locale, "json", thin_mode)
|
||||
else
|
||||
# resolve the url to get the author's UCID
|
||||
response = YoutubeAPI.resolve_url("https://www.youtube.com/post/#{id}")
|
||||
return error_template(400, "Invalid post ID") if response["error"]?
|
||||
|
||||
ucid = response.dig("endpoint", "browseEndpoint", "browseId").as_s
|
||||
post_response = fetch_channel_community_post(ucid, id, locale, "json", thin_mode)
|
||||
end
|
||||
|
||||
post_response = JSON.parse(post_response)
|
||||
|
||||
if nojs
|
||||
comments = Comments.fetch_community_post_comments(ucid, id)
|
||||
comment_html = JSON.parse(Comments.parse_youtube(id, comments, "html", locale, thin_mode, isPost: true))["contentHtml"]
|
||||
end
|
||||
templated "post"
|
||||
end
|
||||
|
||||
def self.channels(env)
|
||||
data = self.fetch_basic_information(env)
|
||||
return data if !data.is_a?(Tuple)
|
||||
|
@ -217,6 +266,11 @@ module Invidious::Routes::Channels
|
|||
env.redirect "/channel/#{ucid}"
|
||||
end
|
||||
|
||||
private KNOWN_TABS = {
|
||||
"home", "videos", "shorts", "streams", "podcasts",
|
||||
"releases", "playlists", "community", "channels", "about",
|
||||
}
|
||||
|
||||
# Redirects brand url channels to a normal /channel/:ucid route
|
||||
def self.brand_redirect(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
|
@ -227,7 +281,10 @@ module Invidious::Routes::Channels
|
|||
yt_url_params = URI::Params.encode(env.params.query.to_h.select(["a", "u", "user"]))
|
||||
|
||||
# Retrieves URL params that only Invidious uses
|
||||
invidious_url_params = URI::Params.encode(env.params.query.to_h.select!(["a", "u", "user"]))
|
||||
invidious_url_params = env.params.query.dup
|
||||
invidious_url_params.delete_all("a")
|
||||
invidious_url_params.delete_all("u")
|
||||
invidious_url_params.delete_all("user")
|
||||
|
||||
begin
|
||||
resolved_url = YoutubeAPI.resolve_url("https://youtube.com#{env.request.path}#{yt_url_params.size > 0 ? "?#{yt_url_params}" : ""}")
|
||||
|
@ -236,14 +293,17 @@ module Invidious::Routes::Channels
|
|||
return error_template(404, translate(locale, "This channel does not exist."))
|
||||
end
|
||||
|
||||
selected_tab = env.request.path.split("/")[-1]
|
||||
if {"home", "videos", "shorts", "streams", "playlists", "community", "channels", "about"}.includes? selected_tab
|
||||
selected_tab = env.params.url["tab"]?
|
||||
|
||||
if KNOWN_TABS.includes? selected_tab
|
||||
url = "/channel/#{ucid}/#{selected_tab}"
|
||||
else
|
||||
url = "/channel/#{ucid}"
|
||||
end
|
||||
|
||||
env.redirect url
|
||||
url += "?#{invidious_url_params}" if !invidious_url_params.empty?
|
||||
|
||||
return env.redirect url
|
||||
end
|
||||
|
||||
# Handles redirects for the /profile endpoint
|
||||
|
|
|
@ -407,14 +407,23 @@ module Invidious::Routes::Feeds
|
|||
end
|
||||
|
||||
spawn do
|
||||
rss = XML.parse_html(body)
|
||||
rss.xpath_nodes("//feed/entry").each do |entry|
|
||||
id = entry.xpath_node("videoid").not_nil!.content
|
||||
author = entry.xpath_node("author/name").not_nil!.content
|
||||
published = Time.parse_rfc3339(entry.xpath_node("published").not_nil!.content)
|
||||
updated = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content)
|
||||
# TODO: unify this with the other almost identical looking parts in this and channels.cr somehow?
|
||||
namespaces = {
|
||||
"yt" => "http://www.youtube.com/xml/schemas/2015",
|
||||
"default" => "http://www.w3.org/2005/Atom",
|
||||
}
|
||||
rss = XML.parse(body)
|
||||
rss.xpath_nodes("//default:feed/default:entry", namespaces).each do |entry|
|
||||
id = entry.xpath_node("yt:videoId", namespaces).not_nil!.content
|
||||
author = entry.xpath_node("default:author/default:name", namespaces).not_nil!.content
|
||||
published = Time.parse_rfc3339(entry.xpath_node("default:published", namespaces).not_nil!.content)
|
||||
updated = Time.parse_rfc3339(entry.xpath_node("default:updated", namespaces).not_nil!.content)
|
||||
|
||||
video = get_video(id, force_refresh: true)
|
||||
begin
|
||||
video = get_video(id, force_refresh: true)
|
||||
rescue
|
||||
next # skip this video since it raised an exception (e.g. it is a scheduled live event)
|
||||
end
|
||||
|
||||
if CONFIG.enable_user_notifications
|
||||
# Deliver notifications to `/api/v1/auth/notifications`
|
||||
|
|
|
@ -319,6 +319,15 @@ module Invidious::Routes::PreferencesRoute
|
|||
response: error_template(415, "Invalid playlist file uploaded")
|
||||
)
|
||||
end
|
||||
when "import_youtube_wh"
|
||||
filename = part.filename || ""
|
||||
success = Invidious::User::Import.from_youtube_wh(user, body, filename, type)
|
||||
|
||||
if !success
|
||||
haltf(env, status_code: 415,
|
||||
response: error_template(415, "Invalid watch history file uploaded")
|
||||
)
|
||||
end
|
||||
when "import_freetube"
|
||||
Invidious::User::Import.from_freetube(user, body)
|
||||
when "import_newpipe_subscriptions"
|
||||
|
|
|
@ -42,7 +42,7 @@ module Invidious::Routes::VideoPlayback
|
|||
headers["Range"] = "bytes=#{range_for_head}"
|
||||
end
|
||||
|
||||
client = make_client(URI.parse(host), region)
|
||||
client = make_client(URI.parse(host), region, force_resolve = true)
|
||||
response = HTTP::Client::Response.new(500)
|
||||
error = ""
|
||||
5.times do
|
||||
|
@ -57,7 +57,7 @@ module Invidious::Routes::VideoPlayback
|
|||
if new_host != host
|
||||
host = new_host
|
||||
client.close
|
||||
client = make_client(URI.parse(new_host), region)
|
||||
client = make_client(URI.parse(new_host), region, force_resolve = true)
|
||||
end
|
||||
|
||||
url = "#{location.request_target}&host=#{location.host}#{region ? "®ion=#{region}" : ""}"
|
||||
|
@ -71,7 +71,7 @@ module Invidious::Routes::VideoPlayback
|
|||
fvip = "3"
|
||||
|
||||
host = "https://r#{fvip}---#{mn}.googlevideo.com"
|
||||
client = make_client(URI.parse(host), region)
|
||||
client = make_client(URI.parse(host), region, force_resolve = true)
|
||||
rescue ex
|
||||
error = ex.message
|
||||
end
|
||||
|
@ -80,9 +80,14 @@ module Invidious::Routes::VideoPlayback
|
|||
# Remove the Range header added previously.
|
||||
headers.delete("Range") if range_header.nil?
|
||||
|
||||
playback_statistics = get_playback_statistic()
|
||||
playback_statistics["totalRequests"] += 1
|
||||
|
||||
if response.status_code >= 400
|
||||
env.response.content_type = "text/plain"
|
||||
haltf env, response.status_code
|
||||
else
|
||||
playback_statistics["successfulRequests"] += 1
|
||||
end
|
||||
|
||||
if url.includes? "&file=seg.ts"
|
||||
|
@ -191,7 +196,7 @@ module Invidious::Routes::VideoPlayback
|
|||
break
|
||||
else
|
||||
client.close
|
||||
client = make_client(URI.parse(host), region)
|
||||
client = make_client(URI.parse(host), region, force_resolve = true)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -30,14 +30,6 @@ module Invidious::Routes::Watch
|
|||
return env.redirect "/"
|
||||
end
|
||||
|
||||
embed_link = "/embed/#{id}"
|
||||
if env.params.query.size > 1
|
||||
embed_params = HTTP::Params.parse(env.params.query.to_s)
|
||||
embed_params.delete_all("v")
|
||||
embed_link += "?"
|
||||
embed_link += embed_params.to_s
|
||||
end
|
||||
|
||||
plid = env.params.query["list"]?.try &.gsub(/[^a-zA-Z0-9_-]/, "")
|
||||
continuation = process_continuation(env.params.query, plid, id)
|
||||
|
||||
|
@ -283,6 +275,12 @@ module Invidious::Routes::Watch
|
|||
return error_template(400, "Invalid clip ID") if response["error"]?
|
||||
|
||||
if video_id = response.dig?("endpoint", "watchEndpoint", "videoId")
|
||||
if params = response.dig?("endpoint", "watchEndpoint", "params").try &.as_s
|
||||
start_time, end_time, _ = parse_clip_parameters(params)
|
||||
env.params.query["start"] = start_time.to_s if start_time != nil
|
||||
env.params.query["end"] = end_time.to_s if end_time != nil
|
||||
end
|
||||
|
||||
return env.redirect "/watch?v=#{video_id}&#{env.params.query}"
|
||||
else
|
||||
return error_template(404, "The requested clip doesn't exist")
|
||||
|
|
|
@ -124,28 +124,42 @@ module Invidious::Routing
|
|||
get "/channel/:ucid/community", Routes::Channels, :community
|
||||
get "/channel/:ucid/channels", Routes::Channels, :channels
|
||||
get "/channel/:ucid/about", Routes::Channels, :about
|
||||
|
||||
get "/channel/:ucid/live", Routes::Channels, :live
|
||||
get "/user/:user/live", Routes::Channels, :live
|
||||
get "/c/:user/live", Routes::Channels, :live
|
||||
get "/post/:id", Routes::Channels, :post
|
||||
|
||||
{"", "/videos", "/shorts", "/streams", "/playlists", "/community", "/about"}.each do |path|
|
||||
# /c/LinusTechTips
|
||||
get "/c/:user#{path}", Routes::Channels, :brand_redirect
|
||||
# /user/linustechtips | Not always the same as /c/
|
||||
get "/user/:user#{path}", Routes::Channels, :brand_redirect
|
||||
# /@LinusTechTips | Handle
|
||||
get "/@:user#{path}", Routes::Channels, :brand_redirect
|
||||
# /attribution_link?a=anything&u=/channel/UCZYTClx2T1of7BRZ86-8fow
|
||||
get "/attribution_link#{path}", Routes::Channels, :brand_redirect
|
||||
# /profile?user=linustechtips
|
||||
get "/profile/#{path}", Routes::Channels, :profile
|
||||
end
|
||||
# Channel catch-all, to redirect future routes to the channel's home
|
||||
# NOTE: defined last in order to be processed after the other routes
|
||||
get "/channel/:ucid/*", Routes::Channels, :redirect_home
|
||||
|
||||
# /c/LinusTechTips
|
||||
get "/c/:user", Routes::Channels, :brand_redirect
|
||||
get "/c/:user/:tab", Routes::Channels, :brand_redirect
|
||||
|
||||
# /user/linustechtips (Not always the same as /c/)
|
||||
get "/user/:user", Routes::Channels, :brand_redirect
|
||||
get "/user/:user/:tab", Routes::Channels, :brand_redirect
|
||||
|
||||
# /@LinusTechTips (Handle)
|
||||
get "/@:user", Routes::Channels, :brand_redirect
|
||||
get "/@:user/:tab", Routes::Channels, :brand_redirect
|
||||
|
||||
# /attribution_link?a=anything&u=/channel/UCZYTClx2T1of7BRZ86-8fow
|
||||
get "/attribution_link", Routes::Channels, :brand_redirect
|
||||
get "/attribution_link/:tab", Routes::Channels, :brand_redirect
|
||||
|
||||
# /profile?user=linustechtips
|
||||
get "/profile", Routes::Channels, :profile
|
||||
get "/profile/*", Routes::Channels, :profile
|
||||
end
|
||||
|
||||
def register_watch_routes
|
||||
get "/watch", Routes::Watch, :handle
|
||||
post "/watch_ajax", Routes::Watch, :mark_watched
|
||||
get "/watch/:id", Routes::Watch, :redirect
|
||||
get "/live/:id", Routes::Watch, :redirect
|
||||
get "/shorts/:id", Routes::Watch, :redirect
|
||||
get "/clip/:clip", Routes::Watch, :clip
|
||||
get "/w/:id", Routes::Watch, :redirect
|
||||
|
@ -221,6 +235,7 @@ module Invidious::Routing
|
|||
get "/api/v1/captions/:id", {{namespace}}::Videos, :captions
|
||||
get "/api/v1/annotations/:id", {{namespace}}::Videos, :annotations
|
||||
get "/api/v1/comments/:id", {{namespace}}::Videos, :comments
|
||||
get "/api/v1/clips/:id", {{namespace}}::Videos, :clips
|
||||
|
||||
# Feeds
|
||||
get "/api/v1/trending", {{namespace}}::Feeds, :trending
|
||||
|
@ -240,6 +255,10 @@ module Invidious::Routing
|
|||
get "/api/v1/channels/:ucid/#{{{route}}}", {{namespace}}::Channels, :{{route}}
|
||||
{% end %}
|
||||
|
||||
# Posts
|
||||
get "/api/v1/post/:id", {{namespace}}::Channels, :post
|
||||
get "/api/v1/post/:id/comments", {{namespace}}::Channels, :post_comments
|
||||
|
||||
# 301 redirects to new /api/v1/channels/community/:ucid and /:ucid/community
|
||||
get "/api/v1/channels/comments/:ucid", {{namespace}}::Channels, :channel_comments_redirect
|
||||
get "/api/v1/channels/:ucid/comments", {{namespace}}::Channels, :channel_comments_redirect
|
||||
|
@ -249,6 +268,7 @@ module Invidious::Routing
|
|||
get "/api/v1/search/suggestions", {{namespace}}::Search, :search_suggestions
|
||||
get "/api/v1/hashtag/:hashtag", {{namespace}}::Search, :hashtag
|
||||
|
||||
|
||||
# Authenticated
|
||||
|
||||
# The notification APIs cannot be extracted yet! They require the *local* notifications constant defined in invidious.cr
|
||||
|
|
|
@ -300,9 +300,9 @@ module Invidious::Search
|
|||
object["9:varint"] = ((page - 1) * 20).to_i64
|
||||
end
|
||||
|
||||
# If the object is empty, return an empty string,
|
||||
# otherwise encode to protobuf then to base64
|
||||
return "" if object.empty?
|
||||
# Prevent censoring of self harm topics
|
||||
# See https://github.com/iv-org/invidious/issues/4398
|
||||
object["30:varint"] = 1.to_i64
|
||||
|
||||
return object
|
||||
.try { |i| Protodec::Any.cast_json(i) }
|
||||
|
|
|
@ -218,6 +218,26 @@ struct Invidious::User
|
|||
end
|
||||
end
|
||||
|
||||
def from_youtube_wh(user : User, body : String, filename : String, type : String) : Bool
|
||||
extension = filename.split(".").last
|
||||
|
||||
if extension == "json" || type == "application/json"
|
||||
data = JSON.parse(body)
|
||||
watched = data.as_a.compact_map do |item|
|
||||
next unless url = item["titleUrl"]?
|
||||
next unless match = url.as_s.match(/\?v=(?<video_id>[a-zA-Z0-9_-]+)$/)
|
||||
match["video_id"]
|
||||
end
|
||||
watched.reverse! # YouTube have newest first
|
||||
user.watched += watched
|
||||
user.watched.uniq!
|
||||
Invidious::Database::Users.update_watch_history(user)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------
|
||||
# Freetube
|
||||
# -------------------
|
||||
|
@ -228,8 +248,12 @@ struct Invidious::User
|
|||
subs = matches.map(&.["channel_id"])
|
||||
|
||||
if subs.empty?
|
||||
data = JSON.parse(body)["subscriptions"]
|
||||
subs = data.as_a.map(&.["id"].as_s)
|
||||
profiles = body.split('\n', remove_empty: true)
|
||||
profiles.each do |profile|
|
||||
if data = JSON.parse(profile)["subscriptions"]?
|
||||
subs += data.as_a.map(&.["id"].as_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
user.subscriptions += subs
|
||||
|
|
|
@ -227,8 +227,22 @@ struct Video
|
|||
info.dig?("streamingData", "hlsManifestUrl").try &.as_s
|
||||
end
|
||||
|
||||
def dash_manifest_url
|
||||
info.dig?("streamingData", "dashManifestUrl").try &.as_s
|
||||
def dash_manifest_url : String?
|
||||
raw_dash_url = info.dig?("streamingData", "dashManifestUrl").try &.as_s
|
||||
return nil if raw_dash_url.nil?
|
||||
|
||||
# Use manifest v5 parameter to reduce file size
|
||||
# See https://github.com/iv-org/invidious/issues/4186
|
||||
dash_url = URI.parse(raw_dash_url)
|
||||
dash_query = dash_url.query || ""
|
||||
|
||||
if dash_query.empty?
|
||||
dash_url.path = "#{dash_url.path}/mpd_version/5"
|
||||
else
|
||||
dash_url.query = "#{dash_query}&mpd_version=5"
|
||||
end
|
||||
|
||||
return dash_url.to_s
|
||||
end
|
||||
|
||||
def genre_url : String?
|
||||
|
|
|
@ -52,17 +52,13 @@ module Invidious::Videos
|
|||
break
|
||||
end
|
||||
end
|
||||
result = String.build do |result|
|
||||
result << <<-END_VTT
|
||||
WEBVTT
|
||||
Kind: captions
|
||||
Language: #{tlang || @language_code}
|
||||
|
||||
settings_field = {
|
||||
"Kind" => "captions",
|
||||
"Language" => "#{tlang || @language_code}",
|
||||
}
|
||||
|
||||
END_VTT
|
||||
|
||||
result << "\n\n"
|
||||
|
||||
result = WebVTT.build(settings_field) do |vtt|
|
||||
cues.each_with_index do |node, i|
|
||||
start_time = node["t"].to_f.milliseconds
|
||||
|
||||
|
@ -76,29 +72,16 @@ module Invidious::Videos
|
|||
end_time = start_time + duration
|
||||
end
|
||||
|
||||
# start_time
|
||||
result << start_time.hours.to_s.rjust(2, '0')
|
||||
result << ':' << start_time.minutes.to_s.rjust(2, '0')
|
||||
result << ':' << start_time.seconds.to_s.rjust(2, '0')
|
||||
result << '.' << start_time.milliseconds.to_s.rjust(3, '0')
|
||||
|
||||
result << " --> "
|
||||
|
||||
# end_time
|
||||
result << end_time.hours.to_s.rjust(2, '0')
|
||||
result << ':' << end_time.minutes.to_s.rjust(2, '0')
|
||||
result << ':' << end_time.seconds.to_s.rjust(2, '0')
|
||||
result << '.' << end_time.milliseconds.to_s.rjust(3, '0')
|
||||
|
||||
result << "\n"
|
||||
|
||||
node.children.each do |s|
|
||||
result << s.content
|
||||
text = String.build do |io|
|
||||
node.children.each do |s|
|
||||
io << s.content
|
||||
end
|
||||
end
|
||||
result << "\n"
|
||||
result << "\n"
|
||||
|
||||
vtt.cue(start_time, end_time, text)
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
require "json"
|
||||
|
||||
# returns start_time, end_time and clip_title
|
||||
def parse_clip_parameters(params) : {Float64?, Float64?, String?}
|
||||
decoded_protobuf = params.try { |i| URI.decode_www_form(i) }
|
||||
.try { |i| Base64.decode(i) }
|
||||
.try { |i| IO::Memory.new(i) }
|
||||
.try { |i| Protodec::Any.parse(i) }
|
||||
|
||||
start_time = decoded_protobuf
|
||||
.try(&.["50:0:embedded"]["2:1:varint"].as_i64)
|
||||
.try { |i| i/1000 }
|
||||
|
||||
end_time = decoded_protobuf
|
||||
.try(&.["50:0:embedded"]["3:2:varint"].as_i64)
|
||||
.try { |i| i/1000 }
|
||||
|
||||
clip_title = decoded_protobuf
|
||||
.try(&.["50:0:embedded"]["4:3:string"].as_s)
|
||||
|
||||
return start_time, end_time, clip_title
|
||||
end
|
|
@ -78,6 +78,11 @@ def extract_video_info(video_id : String, proxy_region : String? = nil)
|
|||
# YouTube may return a different video player response than expected.
|
||||
# See: https://github.com/TeamNewPipe/NewPipe/issues/8713
|
||||
# Line to be reverted if one day we solve the video not available issue.
|
||||
|
||||
# Although technically not a call to /videoplayback the fact that YouTube is returning the
|
||||
# wrong video means that we should count it as a failure.
|
||||
get_playback_statistic()["totalRequests"] += 1
|
||||
|
||||
return {
|
||||
"version" => JSON::Any.new(Video::SCHEMA_VERSION.to_i64),
|
||||
"reason" => JSON::Any.new("Can't load the video on this Invidious instance. YouTube is currently trying to block Invidious instances. <a href=\"https://github.com/iv-org/invidious/issues/3822\">Click here for more info about the issue.</a>"),
|
||||
|
@ -137,9 +142,9 @@ end
|
|||
|
||||
def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConfig) : Hash(String, JSON::Any)?
|
||||
LOGGER.debug("try_fetch_streaming_data: [#{id}] Using #{client_config.client_type} client.")
|
||||
# CgIQBg is a workaround for streaming URLs that returns a 403.
|
||||
# See https://github.com/iv-org/invidious/issues/4027#issuecomment-1666944520
|
||||
response = YoutubeAPI.player(video_id: id, params: "CgIQBg", client_config: client_config)
|
||||
# CgIIAdgDAQ%3D%3D is a workaround for streaming URLs that returns a 403.
|
||||
# https://github.com/LuanRT/YouTube.js/pull/624
|
||||
response = YoutubeAPI.player(video_id: id, params: "CgIIAdgDAQ%3D%3D", client_config: client_config)
|
||||
|
||||
playability_status = response["playabilityStatus"]["status"]
|
||||
LOGGER.debug("try_fetch_streaming_data: [#{id}] Got playabilityStatus == #{playability_status}.")
|
||||
|
|
|
@ -34,41 +34,15 @@ module Invidious::Videos
|
|||
# Convert into array of TranscriptLine
|
||||
lines = self.parse(initial_data)
|
||||
|
||||
settings_field = {
|
||||
"Kind" => "captions",
|
||||
"Language" => target_language,
|
||||
}
|
||||
|
||||
# Taken from Invidious::Videos::Captions::Metadata.timedtext_to_vtt()
|
||||
vtt = String.build do |vtt|
|
||||
vtt << <<-END_VTT
|
||||
WEBVTT
|
||||
Kind: captions
|
||||
Language: #{target_language}
|
||||
|
||||
|
||||
END_VTT
|
||||
|
||||
vtt << "\n\n"
|
||||
|
||||
vtt = WebVTT.build(settings_field) do |vtt|
|
||||
lines.each do |line|
|
||||
start_time = line.start_ms
|
||||
end_time = line.end_ms
|
||||
|
||||
# start_time
|
||||
vtt << start_time.hours.to_s.rjust(2, '0')
|
||||
vtt << ':' << start_time.minutes.to_s.rjust(2, '0')
|
||||
vtt << ':' << start_time.seconds.to_s.rjust(2, '0')
|
||||
vtt << '.' << start_time.milliseconds.to_s.rjust(3, '0')
|
||||
|
||||
vtt << " --> "
|
||||
|
||||
# end_time
|
||||
vtt << end_time.hours.to_s.rjust(2, '0')
|
||||
vtt << ':' << end_time.minutes.to_s.rjust(2, '0')
|
||||
vtt << ':' << end_time.seconds.to_s.rjust(2, '0')
|
||||
vtt << '.' << end_time.milliseconds.to_s.rjust(3, '0')
|
||||
|
||||
vtt << "\n"
|
||||
vtt << line.line
|
||||
|
||||
vtt << "\n"
|
||||
vtt << "\n"
|
||||
vtt.cue(line.start_ms, line.end_ms, line.line)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<p><%= error_message %></p>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="h-box pure-g" id="comments">
|
||||
<div class="h-box pure-g comments" id="comments">
|
||||
<%= IV::Frontend::Comments.template_youtube(items.not_nil!, locale, thin_mode) %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
@ -26,8 +26,9 @@
|
|||
</a></div>
|
||||
</div>
|
||||
|
||||
<% if !item.channel_handle.nil? %><p class="channel-name" dir="auto"><%= item.channel_handle %></p><% end %>
|
||||
<p><%= translate_count(locale, "generic_subscribers_count", item.subscriber_count, NumberFormatting::Separator) %></p>
|
||||
<% if !item.auto_generated %><p><%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %></p><% end %>
|
||||
<% if !item.auto_generated && item.channel_handle.nil? %><p><%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %></p><% end %>
|
||||
<h5><%= item.description_html %></h5>
|
||||
<% when SearchHashtag %>
|
||||
<% if !thin_mode %>
|
||||
|
@ -81,11 +82,19 @@
|
|||
</div>
|
||||
|
||||
<div class="video-card-row flexible">
|
||||
<div class="flex-left"><a href="/channel/<%= item.ucid %>">
|
||||
<p class="channel-name" dir="auto"><%= HTML.escape(item.author) %>
|
||||
<%- if author_verified %> <i class="icon ion ion-md-checkmark-circle"></i><% end -%>
|
||||
</p>
|
||||
</a></div>
|
||||
<div class="flex-left">
|
||||
<% if !item.ucid.to_s.empty? %>
|
||||
<a href="/channel/<%= item.ucid %>">
|
||||
<p class="channel-name" dir="auto"><%= HTML.escape(item.author) %>
|
||||
<%- if author_verified %> <i class="icon ion ion-md-checkmark-circle"></i><% end -%>
|
||||
</p>
|
||||
</a>
|
||||
<% else %>
|
||||
<p class="channel-name" dir="auto"><%= HTML.escape(item.author) %>
|
||||
<%- if author_verified %> <i class="icon ion ion-md-checkmark-circle"></i><% end -%>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% when Category %>
|
||||
<% else %>
|
||||
|
@ -159,11 +168,19 @@
|
|||
</div>
|
||||
|
||||
<div class="video-card-row flexible">
|
||||
<div class="flex-left"><a href="/channel/<%= item.ucid %>">
|
||||
<p class="channel-name" dir="auto"><%= HTML.escape(item.author) %>
|
||||
<%- if author_verified %> <i class="icon ion ion-md-checkmark-circle"></i><% end -%>
|
||||
</p>
|
||||
</a></div>
|
||||
<div class="flex-left">
|
||||
<% if !item.ucid.to_s.empty? %>
|
||||
<a href="/channel/<%= item.ucid %>">
|
||||
<p class="channel-name" dir="auto"><%= HTML.escape(item.author) %>
|
||||
<%- if author_verified %> <i class="icon ion ion-md-checkmark-circle"></i><% end -%>
|
||||
</p>
|
||||
</a>
|
||||
<% else %>
|
||||
<p class="channel-name" dir="auto"><%= HTML.escape(item.author) %>
|
||||
<%- if author_verified %> <i class="icon ion ion-md-checkmark-circle"></i><% end -%>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= rendered "components/video-context-buttons" %>
|
||||
</div>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue