Compare commits

..

1 Commits

Author SHA1 Message Date
Omar Roth 50bab26a3a
Add support for CONNECT proxy 5 years ago

@ -1,88 +0,0 @@
#
# Lint
#
# Exclude assigns for ECR files
Lint/UselessAssign:
Excluded:
- src/invidious.cr
- src/invidious/helpers/errors.cr
- src/invidious/routes/**/*.cr
# Ignore false negative (if !db.query_one?...)
Lint/UnreachableCode:
Excluded:
- src/invidious/database/base.cr
# Ignore shadowed variable `key` (it works for now, and that's
# a sensitive part of the code)
Lint/ShadowingOuterLocalVar:
Excluded:
- src/invidious/helpers/tokens.cr
#
# Style
#
Style/RedundantBegin:
Enabled: false
Style/RedundantReturn:
Enabled: false
#
# Metrics
#
# Ignore function complexity (number of if/else & case/when branches)
# For some functions that can hardly be simplified for now
Metrics/CyclomaticComplexity:
Excluded:
# get_about_info(ucid, locale) => [17/10]
- src/invidious/channels/about.cr
# fetch_channel_community(ucid, continuation, ...) => [34/10]
- src/invidious/channels/community.cr
# create_notification_stream(env, topics, connection_channel) => [14/10]
- src/invidious/helpers/helpers.cr:84:5
# get_index(plural_form, count) => [25/10]
- src/invidious/helpers/i18next.cr
# call(context) => [18/10]
- src/invidious/helpers/static_file_handler.cr
# show(env) => [38/10]
- src/invidious/routes/embed.cr
# get_video_playback(env) => [45/10]
- src/invidious/routes/video_playback.cr
# handle(env) => [40/10]
- src/invidious/routes/watch.cr
# playlist_ajax(env) => [24/10]
- src/invidious/routes/playlists.cr
# fetch_youtube_comments(id, cursor, ....) => [40/10]
# template_youtube_comments(comments, locale, ...) => [16/10]
# content_to_comment_html(content) => [14/10]
- src/invidious/comments.cr
# to_json(locale, json) => [21/10]
# extract_video_info(video_id, ...) => [44/10]
# process_video_params(query, preferences) => [20/10]
- src/invidious/videos.cr
#src/invidious/playlists.cr:327:5
#[C] Metrics/CyclomaticComplexity: Cyclomatic complexity too high [19/10]
# fetch_playlist(plid : String)
#src/invidious/playlists.cr:436:5
#[C] Metrics/CyclomaticComplexity: Cyclomatic complexity too high [11/10]
# extract_playlist_videos(initial_data : Hash(String, JSON::Any))

3
.gitattributes vendored

@ -1,3 +0,0 @@
# https://github.community/t/how-to-change-the-category/2261/3
videojs-*.js linguist-detectable=false
video.min.js linguist-detectable=false

18
.github/CODEOWNERS vendored

@ -1,18 +0,0 @@
# Default and lowest precedence. If none of the below matches, @iv-org/developers would be requested for review.
* @iv-org/developers
docker-compose.yml @unixfox
docker/ @unixfox
kubernetes/ @unixfox
README.md @thefrenchghosty
config/config.example.yml @thefrenchghosty @SamantazFox @unixfox
scripts/ @syeopite
shards.lock @syeopite
shards.yml @syeopite
locales/ @SamantazFox
src/invidious/helpers/i18n.cr @SamantazFox
src/invidious/helpers/youtube_api.cr @SamantazFox

@ -1 +0,0 @@
custom: https://invidious.io/donate/

@ -1,42 +0,0 @@
---
name: Bug report
about: Create a bug report to help us improve Invidious
title: '[Bug] '
labels: bug
assignees: ''
---
<!--
BEFORE TRYING TO REPORT A BUG:
* Read the FAQ!
* Use the search function to check if there is already an issue open for your problem!
If you want to suggest a new feature please use "Feature request" instead
If you want to suggest an enhancement to an existing feature please use "Enhancement" instead
-->
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**Steps to Reproduce**
<!-- Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->
**Logs**
<!-- If applicable, copy the log that appear in the browser page where the error is reported. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Additional context**
<!-- Add any other context about the problem here.
- Browser (if applicable):
- OS (if applicable):
-->

@ -1,24 +0,0 @@
---
name: Enhancement
about: Suggest an enhancement for an existing feature
title: '[Enhancement] '
labels: enhancement
assignees: ''
---
<!-- Please use the search function to check if the desired function has already been requested by someone else -->
<!-- If you want to suggest a new feature please use "Feature request" instead -->
<!-- If you want to report a bug, please use "Bug report" instead -->
**Is your enhancement request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the enhancement here. -->

@ -1,24 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: '[Feature request] '
labels: feature-request
assignees: ''
---
<!-- Please use the search function to check if the desired function has already been requested by someone else -->
<!-- If you want to suggest an enhancement to an existing feature please use "Enhancement" instead -->
<!-- If you want to report a bug, please use "Bug report" instead -->
**Is your feature request related to a problem? Please describe.**
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

@ -1,37 +0,0 @@
name: Close duplicates
on:
issues:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
permissions: write-all
steps:
- uses: iv-org/close-potential-duplicates@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Issue title filter work with anymatch https://www.npmjs.com/package/anymatch.
# Any matched issue will stop detection immediately.
# You can specify multi filters in each line.
filter: ''
# Exclude keywords in title before detecting.
exclude: ''
# Label to set, when potential duplicates are detected.
label: duplicate
# Get issues with state to compare. Supported state: 'all', 'closed', 'open'.
state: open
# If similarity is higher than this threshold([0,1]), issue will be marked as duplicate.
threshold: 0.9
# Reactions to be add to comment when potential duplicates are detected.
# Available reactions: "-1", "+1", "confused", "laugh", "heart", "hooray", "rocket", "eyes"
reactions: ''
close: true
# Comment to post when potential duplicates are detected.
comment: |
Hello, your issue is a duplicate of this/these issue(s): {{#issues}}
- #{{ number }} [accuracy: {{ accuracy }}%]
{{/issues}}
If this is a mistake please explain why and ping @\unixfox, @\SamantazFox and @\TheFrenchGhosty.
Please refrain from opening new issues, it won't help in solving your problem.

@ -1,128 +0,0 @@
name: Invidious CI
on:
schedule:
- cron: "0 0 * * *" # Every day at 00:00
push:
branches:
- "master"
- "api-only"
pull_request:
branches: "*"
paths-ignore:
- "*.md"
- LICENCE
- TRANSLATION
- invidious.service
- .git*
- .editorconfig
- screenshots/*
- assets/**
- locales/*
- config/**
- .github/ISSUE_TEMPLATE/*
- kubernetes/**
jobs:
build:
runs-on: ubuntu-latest
name: "build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }}"
continue-on-error: ${{ !matrix.stable }}
strategy:
fail-fast: false
matrix:
stable: [true]
crystal:
- 1.4.1
- 1.5.1
- 1.6.2
- 1.7.3
- 1.8.1
include:
- crystal: nightly
stable: false
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Install Crystal
uses: crystal-lang/install-crystal@v1.7.0
with:
crystal: ${{ matrix.crystal }}
- name: Cache Shards
uses: actions/cache@v3
with:
path: ./lib
key: shards-${{ hashFiles('shard.lock') }}
- name: Install Shards
run: |
if ! shards check; then
shards install
fi
- name: Run tests
run: crystal spec
- name: Run lint
run: |
if ! crystal tool format --check; then
crystal tool format
git diff
exit 1
fi
- name: Build
run: crystal build --warnings all --error-on-warnings --error-trace src/invidious.cr
build-docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker
run: docker-compose build --build-arg release=0
- name: Run Docker
run: docker-compose up -d
- name: Test Docker
run: while curl -Isf http://localhost:3000; do sleep 1; done
build-docker-arm64:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build Docker ARM64 image
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.arm64
platforms: linux/arm64/v8
build-args: release=0
- name: Test Docker
run: while curl -Isf http://localhost:3000; do sleep 1; done

@ -1,105 +0,0 @@
name: Build and release container
on:
push:
branches:
- "master"
paths-ignore:
- "*.md"
- LICENCE
- TRANSLATION
- invidious.service
- .git*
- .editorconfig
- screenshots/*
- .github/ISSUE_TEMPLATE/*
- kubernetes/**
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Crystal
uses: crystal-lang/install-crystal@v1.6.0
with:
crystal: 1.5.0
- name: Run lint
run: |
if ! crystal tool format --check; then
crystal tool format
git diff
exit 1
fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to registry
uses: docker/login-action@v2
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- name: Build and push Docker AMD64 image without QUIC for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }},quay.io/invidious/invidious:latest
build-args: |
"release=1"
"disable_quic=1"
- name: Build and push Docker ARM64 image without QUIC for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.arm64
platforms: linux/arm64/v8
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }}-arm64,quay.io/invidious/invidious:latest-arm64
build-args: |
"release=1"
"disable_quic=1"
- name: Build and push Docker AMD64 image with QUIC for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }}-quic,quay.io/invidious/invidious:latest-quic
build-args: release=1
- name: Build and push Docker ARM64 image with QUIC for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.arm64
platforms: linux/arm64/v8
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }}-arm64-quic,quay.io/invidious/invidious:latest-arm64-quic
build-args: release=1

@ -1,26 +0,0 @@
# Documentation: https://github.com/marketplace/actions/close-stale-issues
name: "Stale issue handler"
on:
workflow_dispatch:
schedule:
- cron: "0 */12 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 365
days-before-pr-stale: 45 # PRs should be active. Anything that hasn't had activity in more than 45 days should be considered abandoned.
days-before-close: 30
exempt-pr-labels: blocked
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"
stale-pr-label: "stale"
ascending: true
# Never mark feature requests/enhancements as stale
exempt-issue-labels: "feature-request,enhancement,exempt-stale"

4
.gitignore vendored

@ -1,4 +1,4 @@
/docs/
/doc/
/dev/
/lib/
/bin/
@ -6,4 +6,4 @@
/.vscode/
/invidious
/sentry
/config/config.yml
shard.lock

3
.gitmodules vendored

@ -1,3 +0,0 @@
[submodule "mocks"]
path = mocks
url = ../mocks

@ -0,0 +1,31 @@
dist: bionic
jobs:
include:
- stage: build
# TODO: Shallowly clone again once the .git folder is no longer required for building
git:
depth: false
language: crystal
crystal: latest
before_install:
- shards update
- shards install
install:
- crystal build --warnings all --error-on-warnings src/invidious.cr
script:
- crystal tool format --check
- crystal spec
- stage: build_docker
# TODO: Shallowly clone again once the .git folder is no longer required for building
git:
depth: false
language: minimal
services:
- docker
install:
- docker-compose build
script:
- docker-compose up -d
- while curl -Isf http://localhost:3000; do sleep 1; done

@ -1,5 +1,3 @@
# Note: This is no longer updated and links to omarroths repo, which doesn't exist anymore.
# 0.20.0 (2019-011-06)
# Version 0.20.0: Custom Playlists
@ -402,7 +400,7 @@ An `/api/v1/stats` endpoint has been added with [#356](https://github.com/omarro
## For Developers
`/api/v1/channels/:ucid` now provides an `autoGenerated` tag, which returns true for topic channels, and larger genre channels generated by YouTube. These channels don't have any videos of their own, so `latestVideos` will be empty. It is recommended instead to display a list of playlists generated by YouTube.
`/api/v1/channels/:ucid` now provides an `autoGenerated` tag, which returns true for [topic channels](https://www.youtube.com/channel/UCE80FOXpJydkkMo-BYoJdEg), and larger [genre channels](https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ) generated by YouTube. These channels don't have any videos of their own, so `latestVideos` will be empty. It is recommended instead to display a list of playlists generated by YouTube.
You can now pull a list of playlists from a channel with `/api/v1/channels/playlists/:ucid`. Supported options are documented in the [wiki](https://github.com/omarroth/invidious/wiki/API#get-apiv1channelsplaylistsucid-apiv1channelsucidplaylists). Pagination is handled with a `continuation` token, which is generated on each call. Of note is that auto-generated channels currently have one page of results, and subsequent calls will be empty.

@ -1,124 +0,0 @@
# -----------------------
# Compilation options
# -----------------------
RELEASE := 1
STATIC := 0
DISABLE_QUIC := 1
NO_DBG_SYMBOLS := 0
FLAGS ?=
ifeq ($(RELEASE), 1)
FLAGS += --release
endif
ifeq ($(STATIC), 1)
FLAGS += --static
endif
ifeq ($(NO_DBG_SYMBOLS), 1)
FLAGS += --no-debug
else
FLAGS += --debug
endif
ifeq ($(DISABLE_QUIC), 1)
FLAGS += -Ddisable_quic
endif
ifeq ($(API_ONLY), 1)
FLAGS += -Dapi_only
endif
# -----------------------
# Main
# -----------------------
all: invidious
get-libs:
shards install --production
# TODO: add support for ARM64 via cross-compilation
invidious: get-libs
crystal build src/invidious.cr $(FLAGS) --progress --stats --error-trace
run: invidious
./invidious
# -----------------------
# Development
# -----------------------
format:
crystal tool format
test:
crystal spec
verify:
crystal build src/invidious.cr -Dskip_videojs_download \
--no-codegen --progress --stats --error-trace
# -----------------------
# (Un)Install
# -----------------------
# TODO
# -----------------------
# Cleaning
# -----------------------
clean:
rm invidious
distclean: clean
rm -rf libs
# -----------------------
# Help page
# -----------------------
help:
@echo "Targets available in this Makefile:"
@echo ""
@echo " get-libs Fetch Crystal libraries"
@echo " invidious Build Invidious"
@echo " run Launch Invidious"
@echo ""
@echo " format Run the Crystal formatter"
@echo " test Run tests"
@echo " verify Just make sure that the code compiles, but without"
@echo " generating any binaries. Useful to search for errors"
@echo ""
@echo " clean Remove build artifacts"
@echo " distclean Remove build artifacts and libraries"
@echo ""
@echo ""
@echo "Build options available for this Makefile:"
@echo ""
@echo " RELEASE Make a release build (Default: 1)"
@echo " STATIC Link libraries statically (Default: 0)"
@echo ""
@echo " API_ONLY Build invidious without a GUI (Default: 0)"
@echo " DISABLE_QUIC Disable support for QUIC (Default: 0)"
@echo " NO_DBG_SYMBOLS Strip debug symbols (Default: 0)"
# No targets generates an output named after themselves
.PHONY: all get-libs build amd64 run
.PHONY: format test verify clean distclean help

@ -1,181 +1,255 @@
<div align="center">
<img src="assets/invidious-colored-vector.svg" width="192" height="192" alt="Invidious logo">
<h1>Invidious</h1>
<a href="https://www.gnu.org/licenses/agpl-3.0.en.html">
<img alt="License: AGPLv3" src="https://shields.io/badge/License-AGPL%20v3-blue.svg">
</a>
<a href="https://github.com/iv-org/invidious/actions">
<img alt="Build Status" src="https://github.com/iv-org/invidious/workflows/Invidious%20CI/badge.svg">
</a>
<a href="https://github.com/iv-org/invidious/commits/master">
<img alt="GitHub commits" src="https://img.shields.io/github/commit-activity/y/iv-org/invidious?color=red&label=commits">
</a>
<a href="https://github.com/iv-org/invidious/issues">
<img alt="GitHub issues" src="https://img.shields.io/github/issues/iv-org/invidious?color=important">
</a>
<a href="https://github.com/iv-org/invidious/pulls">
<img alt="GitHub pull requests" src="https://img.shields.io/github/issues-pr/iv-org/invidious?color=blueviolet">
</a>
<a href="https://hosted.weblate.org/engage/invidious/">
<img alt="Translation Status" src="https://hosted.weblate.org/widgets/invidious/-/translations/svg-badge.svg">
</a>
<a href="https://github.com/humanetech-community/awesome-humane-tech">
<img alt="Awesome Humane Tech" src="https://raw.githubusercontent.com/humanetech-community/awesome-humane-tech/main/humane-tech-badge.svg?sanitize=true">
</a>
<h3>An open source alternative front-end to YouTube</h3>
<a href="https://invidious.io/">Website</a>
&nbsp;&nbsp;
<a href="https://instances.invidious.io/">Instances list</a>
&nbsp;&nbsp;
<a href="https://docs.invidious.io/faq/">FAQ</a>
&nbsp;&nbsp;
<a href="https://docs.invidious.io/">Documentation</a>
&nbsp;&nbsp;
<a href="#contribute">Contribute</a>
&nbsp;&nbsp;
<a href="https://invidious.io/donate/">Donate</a>
<h5>Chat with us:</h5>
<a href="https://matrix.to/#/#invidious:matrix.org">
<img alt="Matrix" src="https://img.shields.io/matrix/invidious:matrix.org?label=Matrix&color=darkgreen">
</a>
<a href="https://web.libera.chat/?channel=#invidious">
<img alt="Libera.chat (IRC)" src="https://img.shields.io/badge/IRC%20%28Libera.chat%29-%23invidious-darkgreen">
</a>
<br>
<a rel="me" href="https://social.tchncs.de/@invidious">
<img alt="Fediverse: @invidious@social.tchncs.de" src="https://img.shields.io/badge/Fediverse-%40invidious%40social.tchncs.de-darkgreen">
</a>
<br>
<a href="https://invidious.io/contact/">
<img alt="E-mail" src="https://img.shields.io/badge/E%2d%2dmail-darkgreen">
</a>
</div>
# Invidious
[![Build Status](https://travis-ci.org/omarroth/invidious.svg?branch=master)](https://travis-ci.org/omarroth/invidious)
## Screenshots
| Player | Preferences | Subscriptions |
|-------------------------------------|-------------------------------------|---------------------------------------|
| ![](screenshots/01_player.png) | ![](screenshots/02_preferences.png) | ![](screenshots/03_subscriptions.png) |
| ![](screenshots/04_description.png) | ![](screenshots/05_preferences.png) | ![](screenshots/06_subscriptions.png) |
## Invidious is an alternative front-end to YouTube
## Features
**User features**
- Lightweight
- Audio-only mode (and no need to keep window open on mobile)
- [Free software](https://github.com/omarroth/invidious) (AGPLv3 licensed)
- No ads
- No tracking
- No JavaScript required
- Light/Dark themes
- Customizable homepage
- Subscriptions independent from Google
- Notifications for all subscribed channels
- Audio-only mode (with background play on mobile)
- Support for Reddit comments
- [Available in many languages](locales/), thanks to [our translators](#contribute)
**Data import/export**
- Import subscriptions from YouTube, NewPipe and Freetube
- Import watch history from NewPipe
- Export subscriptions to NewPipe and Freetube
- Import/Export Invidious user data
**Technical features**
- Embedded video support
- [Developer API](https://docs.invidious.io/api/)
- Does not use official YouTube APIs
- No Contributor License Agreement (CLA)
## Quick start
**Using invidious:**
- [Select a public instance from the list](https://instances.invidious.io) and start watching videos right now!
**Hosting invidious:**
- No need to create a Google account to save subscriptions
- Lightweight (homepage is ~4 KB compressed)
- Tools for managing subscriptions:
- Only show unseen videos
- Only show latest (or latest unseen) video from each channel
- Delivers notifications from all subscribed channels
- Automatically redirect homepage to feed
- Import subscriptions from YouTube
- Dark mode
- Embed support
- Set default player options (speed, quality, autoplay, loop)
- Does not require JS to play videos
- Support for Reddit comments in place of YT comments
- Import/Export subscriptions, watch history, preferences
- Does not use any of the official YouTube APIs
- Developer [API](https://github.com/omarroth/invidious/wiki/API)
Liberapay: https://liberapay.com/omarroth
BTC: 356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY
BCH: qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk
## Invidious Instances
See [Invidious Instances](https://github.com/omarroth/invidious/wiki/Invidious-Instances) for a full list of publicly available instances.
### Official Instances
- [invidio.us](https://invidio.us) 🇺🇸
Issuer: Let's Encrypt, [SSLLabs Verification](https://www.ssllabs.com/ssltest/analyze.html?d=invidio.us)
- [kgg2m7yk5aybusll.onion](http://kgg2m7yk5aybusll.onion)
- [axqzx4s6s54s32yentfqojs3x5i7faxza6xo3ehd4bzzsg2ii4fv2iid.onion](http://axqzx4s6s54s32yentfqojs3x5i7faxza6xo3ehd4bzzsg2ii4fv2iid.onion)
- [Follow the installation instructions](https://docs.invidious.io/installation/)
## Screenshots
| Player | Preferences | Subscriptions |
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [<img src="screenshots/01_player.png?raw=true" height="140" width="280">](screenshots/01_player.png?raw=true) | [<img src="screenshots/02_preferences.png?raw=true" height="140" width="280">](screenshots/02_preferences.png?raw=true) | [<img src="screenshots/03_subscriptions.png?raw=true" height="140" width="280">](screenshots/03_subscriptions.png?raw=true) |
| [<img src="screenshots/04_description.png?raw=true" height="140" width="280">](screenshots/04_description.png?raw=true) | [<img src="screenshots/05_preferences.png?raw=true" height="140" width="280">](screenshots/05_preferences.png?raw=true) | [<img src="screenshots/06_subscriptions.png?raw=true" height="140" width="280">](screenshots/06_subscriptions.png?raw=true) |
## Installation
See [Invidious-Updater](https://github.com/tmiland/Invidious-Updater) for a self-contained script that can automatically install and update Invidious.
### Docker:
#### Build and start cluster:
```bash
$ docker-compose up
```
And visit `localhost:3000` in your browser.
#### Rebuild cluster:
```bash
$ docker-compose build
```
#### Delete data and rebuild:
```bash
$ docker volume rm invidious_postgresdata
$ docker-compose build
```
### Linux:
#### Install dependencies
```bash
# Arch Linux
$ sudo pacman -S base-devel shards crystal librsvg postgresql
# Ubuntu or Debian
# First you have to add the repository to your APT configuration. For easy setup just run in your command line:
$ curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash
# That will add the signing key and the repository configuration. If you prefer to do it manually, execute the following commands:
$ curl -sL "https://keybase.io/crystal/pgp_keys.asc" | sudo apt-key add -
$ echo "deb https://dist.crystal-lang.org/apt crystal main" | sudo tee /etc/apt/sources.list.d/crystal.list
$ sudo apt-get update
$ sudo apt install crystal libssl-dev libxml2-dev libyaml-dev libgmp-dev libreadline-dev postgresql librsvg2-bin libsqlite3-dev
```
#### Add invidious user and clone repository
```bash
$ useradd -m invidious
$ sudo -i -u invidious
$ git clone https://github.com/omarroth/invidious
$ exit
```
#### Setup PostgresSQL
```bash
$ sudo systemctl enable postgresql
$ sudo systemctl start postgresql
$ sudo -i -u postgres
$ psql -c "CREATE USER kemal WITH PASSWORD 'kemal';" # Change 'kemal' here to a stronger password, and update `password` in config/config.yml
$ createdb -O kemal invidious
$ psql invidious kemal < /home/invidious/invidious/config/sql/channels.sql
$ psql invidious kemal < /home/invidious/invidious/config/sql/videos.sql
$ psql invidious kemal < /home/invidious/invidious/config/sql/channel_videos.sql
$ psql invidious kemal < /home/invidious/invidious/config/sql/users.sql
$ psql invidious kemal < /home/invidious/invidious/config/sql/session_ids.sql
$ psql invidious kemal < /home/invidious/invidious/config/sql/nonces.sql
$ psql invidious kemal < /home/invidious/invidious/config/sql/annotations.sql
$ psql invidious kemal < /home/invidious/invidious/config/sql/playlists.sql
$ psql invidious kemal < /home/invidious/invidious/config/sql/playlist_videos.sql
$ exit
```
#### Setup Invidious
```bash
$ sudo -i -u invidious
$ cd invidious
$ shards update && shards install
$ crystal build src/invidious.cr --release
# test compiled binary
$ ./invidious # stop with ctrl c
$ exit
```
#### systemd service
```bash
$ sudo cp /home/invidious/invidious/invidious.service /etc/systemd/system/invidious.service
$ sudo systemctl enable invidious.service
$ sudo systemctl start invidious.service
```
#### Logrotate
```bash
$ sudo echo "/home/invidious/invidious/invidious.log {
rotate 4
weekly
notifempty
missingok
compress
minsize 1048576
}" | tee /etc/logrotate.d/invidious.logrotate
$ sudo chmod 0644 /etc/logrotate.d/invidious.logrotate
```
### OSX:
```bash
# Install dependencies
$ brew update
$ brew install shards crystal postgres imagemagick librsvg
# Clone repository and setup postgres database
$ git clone https://github.com/omarroth/invidious
$ cd invidious
$ brew services start postgresql
$ psql -c "CREATE ROLE kemal WITH PASSWORD 'kemal';" # Change 'kemal' here to a stronger password, and update `password` in config/config.yml
$ createdb -O kemal invidious
$ psql invidious kemal < config/sql/channels.sql
$ psql invidious kemal < config/sql/videos.sql
$ psql invidious kemal < config/sql/channel_videos.sql
$ psql invidious kemal < config/sql/users.sql
$ psql invidious kemal < config/sql/session_ids.sql
$ psql invidious kemal < config/sql/nonces.sql
$ psql invidious kemal < config/sql/annotations.sql
$ psql invidious kemal < config/sql/privacy.sql
$ psql invidious kemal < config/sql/playlists.sql
$ psql invidious kemal < config/sql/playlist_videos.sql
# Setup Invidious
$ shards update && shards install
$ crystal build src/invidious.cr --release
```
## Update Invidious
You can see how to update Invidious [here](https://github.com/omarroth/invidious/wiki/Updating).
## Usage:
```bash
$ ./invidious -h
Usage: invidious [arguments]
-b HOST, --bind HOST Host to bind (defaults to 0.0.0.0)
-p PORT, --port PORT Port to listen for connections (defaults to 3000)
-s, --ssl Enables SSL
--ssl-key-file FILE SSL key file
--ssl-cert-file FILE SSL certificate file
-h, --help Shows this help
-c THREADS, --channel-threads=THREADS
Number of threads for refreshing channels (default: 1)
-f THREADS, --feed-threads=THREADS
Number of threads for refreshing feeds (default: 1)
-o OUTPUT, --output=OUTPUT Redirect output (default: STDOUT)
-v, --version Print version
```
Or for development:
```bash
$ curl -fsSLo- https://raw.githubusercontent.com/samueleaton/sentry/master/install.cr | crystal eval
$ ./sentry
🤖 Your SentryBot is vigilant. beep-boop...
```
## Documentation
The full documentation can be accessed online at https://docs.invidious.io/
The documentation's source code is available in this repository:
https://github.com/iv-org/documentation
### Extensions
We highly recommend the use of [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect#get),
a browser extension that automatically redirects Youtube URLs to any Invidious instance and replaces
embedded youtube videos on other websites with invidious.
The documentation contains a list of browser extensions that we recommended to use along with Invidious.
You can read more here: https://docs.invidious.io/applications/
## Contribute
### Code
1. Fork it ( https://github.com/iv-org/invidious/fork ).
1. Create your feature branch (`git checkout -b my-new-feature`).
1. Stage your files (`git add .`).
1. Commit your changes (`git commit -am 'Add some feature'`).
1. Push to the branch (`git push origin my-new-feature`).
1. Create a new pull request ( https://github.com/iv-org/invidious/compare ).
[Documentation](https://github.com/omarroth/invidious/wiki) can be found in the wiki.
### Translations
## Extensions
We use [Weblate](https://weblate.org) to manage Invidious translations.
[Extensions](https://github.com/omarroth/invidious/wiki/Extensions) can be found in the wiki, as well as documentation for integrating it into other projects.
You can suggest new translations and/or correction here: https://hosted.weblate.org/engage/invidious/.
## Made with Invidious
Creating an account is not required, but recommended, especially if you want to contribute regularly.
Weblate also allows you to log-in with major SSO providers like Github, Gitlab, BitBucket, Google, ...
- [FreeTube](https://github.com/FreeTubeApp/FreeTube): An Open Source YouTube app for privacy.
- [CloudTube](https://cadence.moe/cloudtube/subscriptions): A JS-rich alternate YouTube player
- [PeerTubeify](https://gitlab.com/Ealhad/peertubeify): On YouTube, displays a link to the same video on PeerTube, if it exists.
- [MusicPiped](https://github.com/deep-gaurav/MusicPiped): A materialistic music player that streams music from YouTube.
- [LapisTube](https://github.com/blubbll/lapis-tube): A fancy and advanced (experimental) YouTube frontend. Combined streams & custom YT features.
## Contributing
## Projects using Invidious
1. Fork it ( https://github.com/omarroth/invidious/fork )
2. Create your feature branch (git checkout -b my-new-feature)
3. Commit your changes (git commit -am 'Add some feature')
4. Push to the branch (git push origin my-new-feature)
5. Create a new Pull Request
- [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.
## Contact
Feel free to send an email to omarroth@protonmail.com or join our [Matrix Server](https://riot.im/app/#/room/#invidious:matrix.org), or #invidious on Freenode.
## Liability
You can also view release notes on the [releases](https://github.com/omarroth/invidious/releases) page or in the CHANGELOG.md included in the repository.
We take no responsibility for the use of our tool, or external instances
provided by third parties. We strongly recommend you abide by the valid
official regulations in your country. Furthermore, we refuse liability
for any inappropriate use of Invidious, such as illegal downloading.
This tool is provided to you in the spirit of free, open software.
## License
You may view the LICENSE in which this software is provided to you [here](./LICENSE).
[![GNU AGPLv3 Image](https://www.gnu.org/graphics/agplv3-155x51.png)](http://www.gnu.org/licenses/agpl-3.0.en.html)
> 16. Limitation of Liability.
>
> IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
Invidious is Free Software: You can use, study share and improve it at your
will. Specifically you can redistribute and/or modify it under the terms of the
[GNU Affero General Public License](https://www.gnu.org/licenses/agpl.html) as
published by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

@ -1 +0,0 @@
https://hosted.weblate.org/projects/invidious/

@ -0,0 +1,37 @@
a:hover,
a:active {
color: rgb(0, 182, 240);
}
a {
color: #a0a0a0;
text-decoration: none;
}
body {
background-color: rgba(35, 35, 35, 1);
color: #f0f0f0;
}
.pure-form legend {
color: #f0f0f0;
}
.pure-menu-heading {
color: #f0f0f0;
}
input,
select,
textarea {
color: rgba(35, 35, 35, 1);
}
.pure-form input[type="file"] {
color: #f0f0f0;
}
.navbar > .searchbar input {
background-color: inherit;
color: inherit;
}

@ -5,26 +5,14 @@ body {
Arial, sans-serif;
}
#contents {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.deleted {
background-color: rgb(255, 0, 0, 0.5);
}
.underlined {
border-bottom: 1px solid;
margin-bottom: 20px;
}
.channel-profile > * {
font-size: 1.17em;
font-weight: bold;
vertical-align: middle;
border-radius: 50%;
}
.channel-profile > img {
@ -32,7 +20,7 @@ body {
height: auto;
}
body a.channel-owner {
.channel-owner {
background-color: #008bec;
color: #fff;
border-radius: 9px;
@ -46,7 +34,6 @@ body a.channel-owner {
}
.creator-heart {
display: inline-block;
position: relative;
width: 16px;
height: 16px;
@ -67,29 +54,12 @@ body a.channel-owner {
}
.creator-heart-small-container {
display: block;
position: relative;
width: 13px;
height: 13px;
color: rgb(255, 0, 0);
}
.feed-menu {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.feed-menu-item {
text-align: center;
}
@media screen and (max-width: 640px) {
.feed-menu-item {
flex: 0 0 40%;
}
}
.h-box {
padding-left: 1em;
padding-right: 1em;
@ -115,22 +85,15 @@ div {
padding-right: 10px;
}
body a.pure-button {
color: rgba(0,0,0,.8);
}
button.pure-button-primary,
body a.pure-button-primary,
.channel-owner:hover,
.channel-owner:focus {
a.pure-button-primary,
.channel-owner:hover {
background-color: #a0a0a0;
color: rgba(35, 35, 35, 1);
}
button.pure-button-primary:hover,
body a.pure-button-primary:hover,
button.pure-button-primary:focus,
body a.pure-button-primary:focus {
a.pure-button-primary:hover {
background-color: rgba(0, 182, 240, 1);
color: #fff;
}
@ -150,24 +113,6 @@ img.thumbnail {
object-fit: cover;
}
div.watched-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255,255,255,.4);
}
div.watched-indicator {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
width: 100%;
background-color: red;
}
.length {
z-index: 100;
position: absolute;
@ -207,7 +152,7 @@ div.watched-indicator {
flex: 1;
}
.searchbar {
.navbar > .searchbar {
flex-grow: 2; /* take double the space of the other items */
}
@ -220,27 +165,20 @@ div.watched-indicator {
display: inline;
}
.searchbar .pure-form fieldset { padding: 0; }
.navbar > .searchbar .pure-form input[type="search"] {
margin-bottom: 1px;
.searchbar input[type="search"] {
width: 100%;
margin: 1px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #ccc;
border-radius: 0;
border: 1px solid;
border-color: rgba(0,0,0,0);
border-bottom-color: #CCC;
border-radius: 0;
padding: initial 0;
box-shadow: none;
appearance: none;
-webkit-appearance: none;
}
box-shadow: none;
.searchbar input[type="search"]:focus {
margin: 0;
border: 2px solid;
border-color: rgba(0,0,0,0);
border-bottom-color: #FED;
-webkit-appearance: none;
}
/* https://stackoverflow.com/a/55170420 */
@ -252,6 +190,16 @@ input[type="search"]::-webkit-search-cancel-button {
background-size: 14px;
}
.navbar > .searchbar .pure-form fieldset {
padding: 0;
}
/* attract focus to the searchbar by adding a subtle transition */
.navbar > .searchbar .pure-form input[type="search"]:focus {
margin-bottom: 0px;
border-bottom: 2px solid #aaa;
}
.user-field {
display: flex;
flex-direction: row;
@ -260,7 +208,7 @@ input[type="search"]::-webkit-search-cancel-button {
}
.user-field div {
width: auto;
width: initial;
}
.user-field div:not(:last-child) {
@ -308,67 +256,21 @@ input[type="search"]::-webkit-search-cancel-button {
}
}
/*
* Video "cards" (results/playlist/channel videos)
*/
.video-card-row { margin: 15px 0; }
.flexible { display: flex; }
.flex-left { flex: 1 1 100%; flex-wrap: wrap; }
.flex-right { flex: 1 0 auto; flex-wrap: nowrap; }
p.channel-name { margin: 0; }
p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
/*
* Comments & community posts
*/
#comments {
max-width: 800px;
margin: auto;
}
.video-iframe-wrapper {
position: relative;
height: 0;
padding-bottom: 56.25%;
}
.video-iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
}
/*
* Footer
*/
footer {
color: #919191;
margin-top: auto;
padding: 1.5em 0;
.footer {
color: #666666;
margin: 2em 0;
text-align: center;
max-height: 30vh;
}
footer a {
color: #919191 !important;
.footer a {
color: inherit;
text-decoration: underline;
}
footer span {
margin: 4px 0;
display: block;
}
/* keyframes */
@keyframes spin {
@ -380,252 +282,190 @@ footer span {
}
}
fieldset > select,
span > select {
color: rgba(49, 49, 51, 1);
/* Control Bar */
@media screen and (max-width: 640px) {
.video-js .vjs-control-bar,
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
overflow-x: scroll;
}
}
.pure-control-group label {
word-wrap: normal;
ul.vjs-menu-content::-webkit-scrollbar {
display: none;
}
/*
* Light theme
*/
.light-theme a:hover,
.light-theme a:active,
.light-theme summary:hover,
.light-theme a:focus,
.light-theme summary:focus {
color: #075A9E !important;
.vjs-user-inactive {
cursor: none;
}
.light-theme a.pure-button-primary:hover,
.light-theme a.pure-button-primary:focus {
color: #fff !important;
.video-js .vjs-text-track-display > div > div > div {
background-color: rgba(0, 0, 0, 0.75) !important;
border-radius: 9px !important;
padding: 5px !important;
}
.light-theme a {
color: #335d7a;
text-decoration: none;
.vjs-play-control,
.vjs-volume-panel,
.vjs-current-time,
.vjs-time-control,
.vjs-duration,
.vjs-progress-control,
.vjs-remaining-time {
order: 1;
}
/* All links that do not fit with the default color goes here */
.light-theme a:not([data-id]) > .icon,
.light-theme .pure-u-lg-1-5 > .h-box > a[href^="/watch?"],
.light-theme .playlist-restricted > ol > li > a {
color: #303030;
.vjs-captions-button {
order: 2;
}
.light-theme .pure-menu-heading {
color: #565d64;
.vjs-quality-selector,
.video-js .vjs-http-source-selector {
order: 3;
}
@media (prefers-color-scheme: light) {
.no-theme a:hover,
.no-theme a:active,
.no-theme summary:hover,
.no-theme a:focus,
.no-theme summary:focus {
color: #075A9E !important;
}
.no-theme a.pure-button-primary:hover,
.no-theme a.pure-button-primary:focus {
color: #fff !important;
}
.no-theme a {
color: #335d7a;
text-decoration: none;
}
/* All links that do not fit with the default color goes here */
.no-theme a:not([data-id]) > .icon,
.no-theme .pure-u-lg-1-5 > .h-box > a[href^="/watch?"],
.no-theme .playlist-restricted > ol > li > a {
color: #303030;
}
.light-theme .pure-menu-heading {
color: #565d64;
}
.vjs-playback-rate {
order: 4;
}
/*
* Dark theme
*/
.dark-theme a:hover,
.dark-theme a:active,
.dark-theme summary:hover,
.dark-theme a:focus,
.dark-theme summary:focus {
color: rgb(0, 182, 240);
.vjs-share-control {
order: 5;
}
.dark-theme a {
color: #a0a0a0;
text-decoration: none;
.vjs-fullscreen-control {
order: 6;
}
body.dark-theme {
background-color: rgba(35, 35, 35, 1);
color: #f0f0f0;
.vjs-playback-rate > .vjs-menu {
width: 50px;
}
.dark-theme .pure-form legend {
color: #f0f0f0;
.vjs-control-bar {
display: flex;
flex-direction: row;
scrollbar-width: none;
}
.dark-theme .pure-menu-heading {
color: #f0f0f0;
.vjs-control-bar::-webkit-scrollbar {
display: none;
}
.dark-theme input,
.dark-theme select,
.dark-theme textarea {
color: rgba(35, 35, 35, 1);
.video-js .vjs-icon-cog {
font-size: 18px;
}
.dark-theme .pure-form input[type="file"] {
color: #f0f0f0;
.video-js .vjs-control-bar,
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
background-color: rgba(35, 35, 35, 0.75);
}
.dark-theme .searchbar input {
background-color: inherit;
color: inherit;
.vjs-menu li.vjs-menu-item:focus,
.vjs-menu li.vjs-menu-item:hover {
background-color: rgba(255, 255, 255, 0.75);
color: rgba(49, 49, 51, 0.75);
}
@media (prefers-color-scheme: dark) {
.no-theme a:hover,
.no-theme a:active,
.no-theme a:focus {
color: rgb(0, 182, 240);
}
.no-theme a {
color: #a0a0a0;
text-decoration: none;
}
body.no-theme {
background-color: rgba(35, 35, 35, 1);
color: #f0f0f0;
}
.no-theme .pure-form legend {
color: #f0f0f0;
}
.no-theme .pure-menu-heading {
color: #f0f0f0;
}
.no-theme input,
.no-theme select,
.no-theme textarea {
color: rgba(35, 35, 35, 1);
}
.no-theme .pure-form input[type="file"] {
color: #f0f0f0;
}
.no-theme .searchbar input {
background-color: inherit;
color: inherit;
}
.vjs-menu li.vjs-selected,
.vjs-menu li.vjs-selected:focus,
.vjs-menu li.vjs-selected:hover {
background-color: rgba(0, 182, 240, 0.75);
}
/*With commit d9528f5 all contents of the page is now within a flexbox. However,
the hr element is rendered improperly within one.
See https://stackoverflow.com/a/34372979 for more info */
hr {
margin: 10px 0 10px 0;
/* Progress Bar */
.video-js .vjs-slider {
background-color: rgba(15, 15, 15, 0.5);
}
/* Description Expansion Styling*/
#descexpansionbutton,
#music-desc-expansion {
display: none;
fieldset > select,
span > select {
color: rgba(49, 49, 51, 1);
}
#descexpansionbutton ~ div {
overflow: hidden;
.video-js .vjs-load-progress,
.video-js .vjs-load-progress div {
background: rgba(87, 87, 88, 1);
}
#descexpansionbutton:not(:checked) ~ div {
max-height: 8.3em;
.video-js .vjs-slider:hover,
.video-js button:hover {
color: rgba(0, 182, 240, 1);
}
#descexpansionbutton:checked ~ div {
overflow: unset;
height: 100%;
.video-js .vjs-play-progress {
background-color: rgba(0, 182, 240, 1);
}
#descexpansionbutton ~ label {
order: 1;
margin-top: 20px;
/* Overlay */
.video-js .vjs-overlay {
background-color: rgba(35, 35, 35, 0.75);
color: rgba(255, 255, 255, 1);
}
label[for="descexpansionbutton"]:hover,
label[for="music-desc-expansion"]:hover {
cursor: pointer;
/* ProgressBar marker */
.vjs-marker {
background-color: rgba(255, 255, 255, 1);
z-index: 0;
}
/* Bidi (bidirectional text) support */
h1,
h2,
h3,
h4,
h5,
p,
#descriptionWrapper,
#description-box,
#music-description-box {
unicode-bidi: plaintext;
text-align: start;
/* Big "Play" Button */
.video-js .vjs-big-play-button {
background-color: rgba(35, 35, 35, 0.5);
}
#descriptionWrapper {
max-width: 600px;
white-space: pre-wrap;
.video-js:hover .vjs-big-play-button {
background-color: rgba(35, 35, 35, 0.75);
}
#music-description-box {
display: none;
.video-js .vjs-current-time,
.video-js .vjs-time-divider,
.video-js .vjs-duration {
display: block;
}
#music-desc-expansion:checked ~ #music-description-box {
display: block;
.video-js .vjs-time-divider {
min-width: 0px;
padding-left: 0px;
padding-right: 0px;
}
#music-desc-expansion ~ label > h3 > .ion-ios-arrow-up,
#music-desc-expansion:checked ~ label > h3 > .ion-ios-arrow-down {
display: none;
.video-js .vjs-poster {
background-size: cover;
object-fit: cover;
}
#music-desc-expansion:checked ~ label > h3 > .ion-ios-arrow-up,
#music-desc-expansion ~ label > h3 > .ion-ios-arrow-down {
display: inline;
.player-dimensions.vjs-fluid {
padding-top: 82vh;
}
/* Select all the music items except the first one */
.music-item + .music-item {
border-top: 1px solid #ffffff;
video.video-js {
position: absolute;
height: 100%;
}
/* Center the "invidious" logo on the search page */
#logo > h1 { text-align: center; }
#player-container {
position: relative;
padding-bottom: 82vh;
height: 0;
}
/* IE11 fixes */
:-ms-input-placeholder { color: #888; }
.pure-control-group label {
word-wrap: normal;
}
/* Wider settings name to less word wrap */
.pure-form-aligned .pure-control-group label { width: 19em; }
.video-js.player-style-invidious {
/* This is already the default */
}
.channel-emoji {
margin: 0 2px;
.video-js.player-style-youtube .vjs-control-bar {
display: flex;
flex-direction: row;
}
.video-js.player-style-youtube .vjs-big-play-button {
/*
Styles copied from video-js.min.css, definition of
.vjs-big-play-centered .vjs-big-play-button
*/
top: 50%;
left: 50%;
margin-top: -0.81666em;
margin-left: -1.5em;
}

@ -8,20 +8,3 @@
height: auto;
z-index: -100;
}
.watch-on-invidious {
font-size: 1.3em !important;
font-weight: bold;
white-space: nowrap;
margin: 0 1em 0 1em !important;
order: 3;
}
.watch-on-invidious > a {
color: white;
}
.watch-on-invidious > a:hover,
.watch-on-invidious > a:focus {
color: rgba(0, 182, 240, 1);;
}

@ -1,16 +0,0 @@
#search-widget {
text-align: center;
margin: 20vh 0 50px 0;
}
#logo > h1 {
font-size: 3.5em;
margin: 0;
padding: 0;
}
@media screen and (max-width: 1500px) and (max-height: 1000px) {
#logo > h1 {
font-size: 10vmin;
}
}

@ -0,0 +1,16 @@
a:hover,
a:active {
color: #167ac6 !important;
}
a {
color: #61809b;
text-decoration: none;
}
/* All links that do not fit with the default color goes here */
a:not([data-id]) > .icon,
.pure-u-lg-1-5 > .h-box > a[href^="/watch?"],
.playlist-restricted > ol > li > a {
color: #303030;
}

@ -1,264 +0,0 @@
/* Youtube player style */
.video-js.player-style-youtube .vjs-progress-control {
height: 0;
}
.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {
position: absolute;
right: 0;
left: 0;
width: 100%;
margin: 0;
}
.video-js.player-style-youtube .vjs-control-bar {
background: linear-gradient(rgba(0,0,0,0.1), rgba(0, 0, 0,0.5));
}
.video-js.player-style-youtube .vjs-slider {
background-color: rgba(255,255,255,0.2);
}
.video-js.player-style-youtube .vjs-load-progress > div {
background-color: rgba(255,255,255,0.5);
}
.video-js.player-style-youtube .vjs-play-progress {
background-color: red;
}
.video-js.player-style-youtube .vjs-progress-control:hover .vjs-progress-holder {
font-size: 15px;
}
.video-js.player-style-youtube .vjs-control-bar > .vjs-spacer {
flex: 1;
order: 2;
}
.video-js.player-style-youtube .vjs-play-progress .vjs-time-tooltip {
display: none;
}
.video-js.player-style-youtube .vjs-play-progress::before {
color: red;
font-size: 0.85em;
display: none;
}
.video-js.player-style-youtube .vjs-progress-holder:hover .vjs-play-progress::before {
display: unset;
}
.video-js.player-style-youtube .vjs-control-bar {
display: flex;
flex-direction: row;
}
.video-js.player-style-youtube .vjs-big-play-button {
/*
Styles copied from video-js.min.css, definition of
.vjs-big-play-centered .vjs-big-play-button
*/
top: 50%;
left: 50%;
margin-top: -0.81666em;
margin-left: -1.5em;
}
.video-js.player-style-youtube .vjs-menu-button-popup .vjs-menu {
margin-bottom: 2em;
}
.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {height: 5px;
margin-bottom: 10px;}
ul.vjs-menu-content::-webkit-scrollbar {
display: none;
}
.vjs-user-inactive {
cursor: none;
}
.video-js .vjs-text-track-display > div > div > div {
background-color: rgba(0, 0, 0, 0.75) !important;
border-radius: 9px !important;
padding: 5px !important;
}
.vjs-play-control,
.vjs-volume-panel,
.vjs-current-time,
.vjs-time-control,
.vjs-duration,
.vjs-progress-control,
.vjs-remaining-time {
order: 1;
}
.vjs-captions-button {
order: 2;
}
.vjs-audio-button {
order: 3;
}
.vjs-quality-selector,
.video-js .vjs-http-source-selector {
order: 4;
}
.vjs-playback-rate {
order: 5;
}
.vjs-share-control {
order: 6;
}
.vjs-fullscreen-control {
order: 7;
}
.vjs-playback-rate > .vjs-menu {
width: 50px;
}
.vjs-control-bar {
display: flex;
flex-direction: row;
scrollbar-width: none;
}
.vjs-control-bar::-webkit-scrollbar {
display: none;
}
.video-js .vjs-icon-cog {
font-size: 18px;
}
.video-js .vjs-control-bar,
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
background-color: rgba(35, 35, 35, 0.75);
}
.vjs-menu li.vjs-menu-item:focus,
.vjs-menu li.vjs-menu-item:hover {
background-color: rgba(255, 255, 255, 0.75);
color: rgba(49, 49, 51, 0.75);
}
.vjs-menu li.vjs-selected,
.vjs-menu li.vjs-selected:focus,
.vjs-menu li.vjs-selected:hover {
background-color: rgba(0, 182, 240, 0.75);
}
/* Progress Bar */
.video-js .vjs-slider {
background-color: rgba(15, 15, 15, 0.5);
}
.video-js .vjs-load-progress,
.video-js .vjs-load-progress div {
background: rgba(87, 87, 88, 1);
}
.video-js .vjs-slider:hover,
.video-js button:hover {
color: rgba(0, 182, 240, 1);
}
.video-js.player-style-invidious .vjs-play-progress {
background-color: rgba(0, 182, 240, 1);
}
/* Overlay */
.video-js .vjs-overlay {
background-color: rgba(35, 35, 35, 0.75) !important;
}
.video-js .vjs-overlay * {
color: rgba(255, 255, 255, 1) !important;
text-align: center;
}
/* ProgressBar marker */
.vjs-marker {
background-color: rgba(255, 255, 255, 1);
z-index: 0;
}
/* Big "Play" Button */
.video-js .vjs-big-play-button {
background-color: rgba(35, 35, 35, 0.5);
}
.video-js:hover .vjs-big-play-button {
background-color: rgba(35, 35, 35, 0.75);
}
.video-js .vjs-current-time,
.video-js .vjs-time-divider,
.video-js .vjs-duration {
display: block;
}
.video-js .vjs-time-divider {
min-width: 0px;
padding-left: 0px;
padding-right: 0px;
}
.video-js .vjs-poster {
background-size: cover;
object-fit: cover;
}
.player-dimensions.vjs-fluid {
padding-top: 82vh;
}
video.video-js {
position: absolute;
height: 100%;
}
#player-container {
position: relative;
padding-left: 0;
padding-right: 0;
margin-left: 1em;
margin-right: 1em;
padding-bottom: 82vh;
height: 0;
}
.mobile-operations-bar {
display: flex;
position: absolute;
top: 0;
right: 1px !important;
left: initial !important;
width: initial !important;
}
.mobile-operations-bar ul {
position: absolute !important;
bottom: unset !important;
top: 1.5em;
}
@media screen and (max-width: 700px) {
.video-js .vjs-share {
justify-content: unset;
}
}
@media screen and (max-width: 650px) {
.vjs-modal-dialog-content {
overflow-x: hidden;
}
}

@ -1,121 +0,0 @@
summary {
/* This should hide the marker */
display: block;
font-size: 1.17em;
font-weight: bold;
margin: 0 auto 10px auto;
cursor: pointer;
}
summary::-webkit-details-marker,
summary::marker { display: none; }
summary:before {
border-radius: 5px;
content: "[ + ]";
margin: -2px 10px 0 10px;
padding: 1px 0 3px 0;
text-align: center;
width: 40px;
}
details[open] > summary:before { content: "[ ]"; }
#filters-box {
padding: 10px 20px 20px 10px;
margin: 10px 15px;
}
#filters-flex {
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: flex-start;
align-content: flex-start;
justify-content: flex-start;
}
fieldset, legend {
display: contents !important;
border: none !important;
margin: 0 !important;
padding: 0 !important;
}
.filter-column {
display: inline-block;
display: inline-flex;
width: max-content;
min-width: max-content;
max-width: 16em;
margin: 15px;
flex-grow: 2;
flex-basis: auto;
flex-direction: column;
}
.filter-name, .filter-options {
display: block;
padding: 5px 10px;
margin: 0;
text-align: start;
}
.filter-options div { margin: 6px 0; }
.filter-options div * { vertical-align: middle; }
.filter-options label { margin: 0 10px; }
#filters-apply {
text-align: right; /* IE11 only */
text-align: end; /* Override for compatible browsers */
}
/* Error message */
.no-results-error {
text-align: center;
line-height: 180%;
font-size: 110%;
padding: 15px 15px 125px 15px;
}
/* Responsive rules */
@media only screen and (max-width: 800px) {
summary { font-size: 1.30em; }
#filters-box {
margin: 10px 0 0 0;
padding: 0;
}
#filters-apply {
text-align: center;
padding: 15px;
}
}
/* Light theme */
.light-theme #filters-box {
background: #dfdfdf;
}
@media (prefers-color-scheme: light) {
.no-theme #filters-box {
background: #dfdfdf;
}
}
/* Dark theme */
.dark-theme #filters-box {
background: #373737;
}
@media (prefers-color-scheme: dark) {
.no-theme #filters-box {
background: #373737;
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,7 @@
/**
* videojs-http-source-selector
* @version 1.1.6
* @copyright 2019 Justin Fujita <Justin@pivotshare.com>
* @license MIT
*/
.video-js.vjs-http-source-selector{display:block}

@ -0,0 +1 @@
.video-js .vjs-overlay{color:#fff;position:absolute;text-align:center}.video-js .vjs-overlay-no-background{max-width:33%}.video-js .vjs-overlay-background{background-color:#646464;background-color:rgba(255,255,255,0.4);border-radius:3px;padding:10px;width:33%}.video-js .vjs-overlay-top-left{top:5px;left:5px}.video-js .vjs-overlay-top{left:50%;margin-left:-16.5%;top:5px}.video-js .vjs-overlay-top-right{right:5px;top:5px}.video-js .vjs-overlay-right{right:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-bottom-right{bottom:3.5em;right:5px}.video-js .vjs-overlay-bottom{bottom:3.5em;left:50%;margin-left:-16.5%}.video-js .vjs-overlay-bottom-left{bottom:3.5em;left:5px}.video-js .vjs-overlay-left{left:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-center{left:50%;margin-left:-16.5%;top:50%;transform:translateY(-50%)}.video-js .vjs-no-flex .vjs-overlay-left,.video-js .vjs-no-flex .vjs-overlay-center,.video-js .vjs-no-flex .vjs-overlay-right{margin-top:-15px}

@ -0,0 +1,7 @@
/**
* videojs-share
* @version 3.2.1
* @copyright 2019 Mikhail Khazov <mkhazov.work@gmail.com>
* @license MIT
*/
.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-modal-dialog-content{display:flex;align-items:center;padding:0;background-image:linear-gradient(to bottom, rgba(0,0,0,0.77), rgba(0,0,0,0.75))}.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button{position:absolute;right:0;top:5px;width:30px;height:30px;color:#fff;cursor:pointer;opacity:0.9;transition:opacity 0.25s ease-out}.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button:before{content:'×';font-size:20px;line-height:15px}.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button:hover{opacity:1}.video-js .vjs-share{display:flex;flex-direction:column;justify-content:space-around;align-items:center;width:100%;height:100%;max-height:400px}.video-js .vjs-share__top,.video-js .vjs-share__middle,.video-js .vjs-share__bottom{display:flex}.video-js .vjs-share__top,.video-js .vjs-share__middle{flex-direction:column;justify-content:space-between}.video-js .vjs-share__middle{padding:0 25px}.video-js .vjs-share__title{align-self:center;font-size:22px;color:#fff}.video-js .vjs-share__subtitle{width:100%;margin:0 auto 12px;font-size:16px;color:#fff;opacity:0.7}.video-js .vjs-share__short-link-wrapper{position:relative;display:block;width:100%;height:40px;margin:0 auto;margin-bottom:15px;border:0;color:rgba(255,255,255,0.65);background-color:#363636;outline:none;overflow:hidden;flex-shrink:0}.video-js .vjs-share__short-link{display:block;width:100%;height:100%;padding:0 40px 0 15px;border:0;color:rgba(255,255,255,0.65);background-color:#363636;outline:none}.video-js .vjs-share__btn{position:absolute;right:0;bottom:0;height:40px;width:40px;display:flex;align-items:center;padding:0 11px;border:0;color:#fff;background-color:#2e2e2e;background-size:18px 19px;background-position:center;background-repeat:no-repeat;cursor:pointer;outline:none;transition:width 0.3s ease-out, padding 0.3s ease-out}.video-js .vjs-share__btn svg{flex-shrink:0}.video-js .vjs-share__btn span{position:relative;padding-left:10px;opacity:0;transition:opacity 0.3s ease-out}.video-js .vjs-share__btn:hover{justify-content:center;width:100%;padding:0 40px;background-image:none}.video-js .vjs-share__btn:hover span{opacity:1}.video-js .vjs-share__socials{display:flex;flex-wrap:wrap;justify-content:center;align-content:flex-start;transition:width 0.3s ease-out, height 0.3s ease-out}.video-js .vjs-share__social{display:flex;justify-content:center;align-items:center;flex-shrink:0;width:32px;height:32px;margin-right:6px;margin-bottom:6px;cursor:pointer;font-size:8px;transition:transform 0.3s ease-out, filter 0.2s ease-out;border:none;outline:none}.video-js .vjs-share__social:hover{filter:brightness(115%)}.video-js .vjs-share__social svg{overflow:visible;max-height:24px}.video-js .vjs-share__social_vk{background-color:#5d7294}.video-js .vjs-share__social_ok{background-color:#ed7c20}.video-js .vjs-share__social_mail,.video-js .vjs-share__social_email{background-color:#134785}.video-js .vjs-share__social_tw{background-color:#76aaeb}.video-js .vjs-share__social_reddit{background-color:#ff4500}.video-js .vjs-share__social_fbFeed{background-color:#475995}.video-js .vjs-share__social_messenger{background-color:#0084ff}.video-js .vjs-share__social_gp{background-color:#d53f35}.video-js .vjs-share__social_linkedin{background-color:#0077b5}.video-js .vjs-share__social_viber{background-color:#766db5}.video-js .vjs-share__social_telegram{background-color:#4bb0e2}.video-js .vjs-share__social_whatsapp{background-color:#78c870}.video-js .vjs-share__bottom{justify-content:center}@media (max-height: 220px){.video-js .vjs-share .hidden-xs{display:none}}@media (max-height: 350px){.video-js .vjs-share .hidden-sm{display:none}}@media (min-height: 400px){.video-js .vjs-share__title{margin-bottom:15px}.video-js .vjs-share__short-link-wrapper{margin-bottom:30px}}@media (min-width: 320px){.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button{right:5px;top:10px}}@media (min-width: 660px){.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button{right:20px;top:20px}.video-js .vjs-share__social{width:40px;height:40px}}

@ -0,0 +1,7 @@
/**
* videojs-vtt-thumbnails
* @version 0.0.13
* @copyright 2019 Chris Boustead <chris@forgemotion.com>
* @license MIT
*/
.video-js.vjs-vtt-thumbnails{display:block}.video-js .vjs-vtt-thumbnail-display{position:absolute;bottom:85%;pointer-events:none;box-shadow:0 0 7px rgba(0,0,0,0.6)}

@ -0,0 +1 @@
.vjs-marker{position:absolute;left:0;bottom:0;opacity:1;height:100%;transition:opacity .2s ease;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;z-index:100}.vjs-marker:hover{cursor:pointer;-webkit-transform:scale(1.3,1.3);-moz-transform:scale(1.3,1.3);-o-transform:scale(1.3,1.3);-ms-transform:scale(1.3,1.3);transform:scale(1.3,1.3)}.vjs-tip{visibility:hidden;display:block;opacity:.8;padding:5px;font-size:10px;position:absolute;bottom:14px;z-index:100000}.vjs-tip .vjs-tip-arrow{background:url(data:image/gif;base64,R0lGODlhCQAJAIABAAAAAAAAACH5BAEAAAEALAAAAAAJAAkAAAIRjAOnwIrcDJxvwkplPtchVQAAOw==) no-repeat top left;bottom:0;left:50%;margin-left:-4px;background-position:bottom left;position:absolute;width:9px;height:5px}.vjs-tip .vjs-tip-inner{border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;padding:5px 8px 4px 8px;background-color:#000;color:#fff;max-width:200px;text-align:center}.vjs-break-overlay{visibility:hidden;position:absolute;z-index:100000;top:0}.vjs-break-overlay .vjs-break-overlay-text{padding:9px;text-align:center}

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="512pt" height="512pt" version="1.0" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><g><rect x="-.0072516" y=".00056299" width="512.01" height="512.02" fill="#575757" stroke-width=".063019"/><path d="m247.17 455.95c-19.792-0.78921-38.719-4.2564-57.154-10.47-60.968-20.55-108.68-68.579-127-127.86-7.8955-25.538-10.062-53.943-6.2586-82.067 3.7105-27.439 13.603-53.515 29.342-77.344 12.069-18.273 29.138-36.277 47.228-49.816 36.891-27.61 85.944-42.49 132.38-40.157 25.88 1.3001 49.939 6.765 73.106 16.606 8.1948 3.481 20.024 9.6845 27.696 14.525 14.15 8.9272 22.367 15.498 34.482 27.573 13.254 13.211 22.128 24.276 30.398 37.906 7.2081 11.879 14.099 27.15 18.229 40.397 1.5996 5.1305 4.442 16.456 5.6852 22.653 2.3908 11.917 2.6998 15.722 2.7049 33.312 6e-3 18.515-0.46256 24.413-2.9166 36.758-9.3274 46.92-35.58 88.167-74.872 117.64-22.814 17.112-50.027 29.535-78.547 35.858-16.714 3.7059-35.421 5.2453-54.498 4.4846zm-35.1-78.786c-5.3e-4 -0.52647-0.0741-2.0564-0.16311-3.3999l-0.16178-2.4427-4.7018-0.26271c-4.0477-0.22614-4.7968-0.33363-5.3847-0.77253-2.0235-1.5108-1.4679-6.0695 2.2494-18.457 0.8637-2.8781 3.3371-11.321 5.4966-18.762 2.1594-7.4409 5.2002-17.836 6.7573-23.101 1.5571-5.2648 4.1948-14.282 5.8615-20.038 1.6667-5.7562 3.6145-12.4 4.3284-14.764 0.71391-2.3641 3.2583-11.037 5.6542-19.272 4.9475-17.007 8.1626-27.723 8.9438-29.811 0.51852-1.3858 0.54785-1.4139 0.99761-0.95317 0.25486 0.26106 3.8462 7.3667 7.9807 15.79 4.1345 8.4236 13.089 26.573 19.898 40.331 17.188 34.73 37.849 76.578 43.261 87.622l4.5356 9.257 11.359-0.0895c6.2475-0.0492 11.615-0.19623 11.929-0.32672 0.5614-0.23385 0.54167-0.2959-1.3723-4.3176-1.068-2.2442-8.1436-16.601-15.724-31.904-48.687-98.293-61.22-123.86-67.889-138.48-4.7022-10.309-6.9031-14.807-7.7139-15.762-0.82931-0.97742-1.6319-1.0638-2.3704-0.25525-1.1993 1.313-4.1046 10.063-9.3869 28.27-2.0569 7.0899-6.5372 22.425-9.9562 34.077-6.6396 22.629-8.5182 29.037-14.33 48.883-2.0354 6.9495-4.7977 16.369-6.1385 20.931-1.3408 4.5628-4.033 13.81-5.9826 20.549-4.304 14.877-6.136 20.889-7.3886 24.25-2.1371 5.7334-2.5723 6.3292-4.9216 6.7384-0.88855 0.15472-2.4102 0.28196-3.3815 0.28275-2.1993 3e-3 -3.5494 0.36339-4.0558 1.0863-0.42176 0.60215-0.56421 4.8802-0.18251 5.4812 0.20573 0.32388 2.4672 0.37414 23.34 0.51873l8.6151 0.0597-7e-4 -0.95723zm36.751-205.59c4.3282-0.92335 8.4607-4.943 9.4374-9.1796 0.36569-1.5862 0.32543-4.9758-0.077-6.4799-0.85108-3.1813-3.2688-6.291-6.039-7.7675-3.8111-2.0313-9.456-2.0295-13.272 5e-3 -5.9828 3.1888-8.1556 11.089-4.7878 17.408 2.6995 5.0648 8.3611 7.3754 14.738 6.015z" fill="#f0f0f0" stroke-width=".025526"/></g><g transform="matrix(.069892 0 0 -.069892 44.236 474.48)"><path d="m2787 4669c-124-65-123-255 3-319 86-44 196-16 247 62 58 87 26 211-67 258-51 26-132 26-183-1z" fill="#00b6f0" stroke="#00b6f0" stroke-width="4.25"/><path d="m2882 4108c-12-16-63-166-102-303-30-104-101-350-165-565-20-69-58-199-85-290-26-91-64-221-85-290-20-69-58-199-85-290-26-91-64-221-85-290-20-69-57-195-81-280-59-207-93-299-115-310-10-6-35-10-56-10-73 0-84-8-81-54l3-41 228-3 228-2-3 47-3 48-73 3c-66 3-74 5-84 27-13 28 0 104 37 225 13 41 47 156 75 255s66 230 85 290c18 61 56 191 85 290 28 99 66 230 85 290 18 61 56 191 85 290 85 297 123 419 131 429 5 5 17-11 28-35 10-24 192-393 403-819s447-902 523-1058l139-282h168c92 0 168 4 168 8s-75 158-166 342c-588 1183-969 1958-1033 2100-29 63-69 151-89 195-44 95-58 110-80 83z" fill="#575757"/></g></svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

@ -1,254 +0,0 @@
'use strict';
// Contains only auxiliary methods
// May be included and executed unlimited number of times without any consequences
// Polyfills for IE11
Array.prototype.find = Array.prototype.find || function (condition) {
return this.filter(condition)[0];
};
Array.from = Array.from || function (source) {
return Array.prototype.slice.call(source);
};
NodeList.prototype.forEach = NodeList.prototype.forEach || function (callback) {
Array.from(this).forEach(callback);
};
String.prototype.includes = String.prototype.includes || function (searchString) {
return this.indexOf(searchString) >= 0;
};
String.prototype.startsWith = String.prototype.startsWith || function (prefix) {
return this.substr(0, prefix.length) === prefix;
};
Math.sign = Math.sign || function(x) {
x = +x;
if (!x) return x; // 0 and NaN
return x > 0 ? 1 : -1;
};
if (!window.hasOwnProperty('HTMLDetailsElement') && !window.hasOwnProperty('mockHTMLDetailsElement')) {
window.mockHTMLDetailsElement = true;
const style = 'details:not([open]) > :not(summary) {display: none}';
document.head.appendChild(document.createElement('style')).textContent = style;
addEventListener('click', function (e) {
if (e.target.nodeName !== 'SUMMARY') return;
const details = e.target.parentElement;
if (details.hasAttribute('open'))
details.removeAttribute('open');
else
details.setAttribute('open', '');
});
}
// Monstrous global variable for handy code
// Includes: clamp, xhr, storage.{get,set,remove}
window.helpers = window.helpers || {
/**
* https://en.wikipedia.org/wiki/Clamping_(graphics)
* @param {Number} num Source number
* @param {Number} min Low border
* @param {Number} max High border
* @returns {Number} Clamped value
*/
clamp: function (num, min, max) {
if (max < min) {
var t = max; max = min; min = t; // swap max and min
}
if (max < num)
return max;
if (min > num)
return min;
return num;
},
/** @private */
_xhr: function (method, url, options, callbacks) {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
// Default options
xhr.responseType = 'json';
xhr.timeout = 10000;
// Default options redefining
if (options.responseType)
xhr.responseType = options.responseType;
if (options.timeout)
xhr.timeout = options.timeout;
if (method === 'POST')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// better than onreadystatechange because of 404 codes https://stackoverflow.com/a/36182963
xhr.onloadend = function () {
if (xhr.status === 200) {
if (callbacks.on200) {
// fix for IE11. It doesn't convert response to JSON
if (xhr.responseType === '' && typeof(xhr.response) === 'string')
callbacks.on200(JSON.parse(xhr.response));
else
callbacks.on200(xhr.response);
}
} else {
// handled by onerror
if (xhr.status === 0) return;
if (callbacks.onNon200)
callbacks.onNon200(xhr);
}
};
xhr.ontimeout = function () {
if (callbacks.onTimeout)
callbacks.onTimeout(xhr);
};
xhr.onerror = function () {
if (callbacks.onError)
callbacks.onError(xhr);
};
if (options.payload)
xhr.send(options.payload);
else
xhr.send();
},
/** @private */
_xhrRetry: function(method, url, options, callbacks) {
if (options.retries <= 0) {
console.warn('Failed to pull', options.entity_name);
if (callbacks.onTotalFail)
callbacks.onTotalFail();
return;
}
helpers._xhr(method, url, options, callbacks);
},
/**
* @callback callbackXhrOn200
* @param {Object} response - xhr.response
*/
/**
* @callback callbackXhrError
* @param {XMLHttpRequest} xhr
*/
/**
* @param {'GET'|'POST'} method - 'GET' or 'POST'
* @param {String} url - URL to send request to
* @param {Object} options - other XHR options
* @param {XMLHttpRequestBodyInit} [options.payload=null] - payload for POST-requests
* @param {'arraybuffer'|'blob'|'document'|'json'|'text'} [options.responseType=json]
* @param {Number} [options.timeout=10000]
* @param {Number} [options.retries=1]
* @param {String} [options.entity_name='unknown'] - string to log
* @param {Number} [options.retry_timeout=1000]
* @param {Object} callbacks - functions to execute on events fired
* @param {callbackXhrOn200} [callbacks.on200]
* @param {callbackXhrError} [callbacks.onNon200]
* @param {callbackXhrError} [callbacks.onTimeout]
* @param {callbackXhrError} [callbacks.onError]
* @param {callbackXhrError} [callbacks.onTotalFail] - if failed after all retries
*/
xhr: function(method, url, options, callbacks) {
if (!options.retries || options.retries <= 1) {
helpers._xhr(method, url, options, callbacks);
return;
}
if (!options.entity_name) options.entity_name = 'unknown';
if (!options.retry_timeout) options.retry_timeout = 1000;
const retries_total = options.retries;
let currentTry = 1;
const retry = function () {
console.warn('Pulling ' + options.entity_name + ' failed... ' + (currentTry++) + '/' + retries_total);
setTimeout(function () {
options.retries--;
helpers._xhrRetry(method, url, options, callbacks);
}, options.retry_timeout);
};
// Pack retry() call into error handlers
callbacks._onError = callbacks.onError;
callbacks.onError = function (xhr) {
if (callbacks._onError)
callbacks._onError(xhr);
retry();
};
callbacks._onTimeout = callbacks.onTimeout;
callbacks.onTimeout = function (xhr) {
if (callbacks._onTimeout)
callbacks._onTimeout(xhr);
retry();
};
helpers._xhrRetry(method, url, options, callbacks);
},
/**
* @typedef {Object} invidiousStorage
* @property {(key:String) => Object} get
* @property {(key:String, value:Object)} set
* @property {(key:String)} remove
*/
/**
* Universal storage, stores and returns JS objects. Uses inside localStorage or cookies
* @type {invidiousStorage}
*/
storage: (function () {
// access to localStorage throws exception in Tor Browser, so try is needed
let localStorageIsUsable = false;
try{localStorageIsUsable = !!localStorage.setItem;}catch(e){}
if (localStorageIsUsable) {
return {
get: function (key) {
let storageItem = localStorage.getItem(key)
if (!storageItem) return;
try {
return JSON.parse(decodeURIComponent(storageItem));
} catch(e) {
// Erase non parsable value
helpers.storage.remove(key);
}
},
set: function (key, value) {
let encoded_value = encodeURIComponent(JSON.stringify(value))
localStorage.setItem(key, encoded_value);
},
remove: function (key) { localStorage.removeItem(key); }
};
}
// TODO: fire 'storage' event for cookies
console.info('Storage: localStorage is disabled or unaccessible. Cookies used as fallback');
return {
get: function (key) {
const cookiePrefix = key + '=';
function findCallback(cookie) {return cookie.startsWith(cookiePrefix);}
const matchedCookie = document.cookie.split('; ').find(findCallback);
if (matchedCookie) {
const cookieBody = matchedCookie.replace(cookiePrefix, '');
if (cookieBody.length === 0) return;
try {
return JSON.parse(decodeURIComponent(cookieBody));
} catch(e) {
// Erase non parsable value
helpers.storage.remove(key);
}
}
},
set: function (key, value) {
const cookie_data = encodeURIComponent(JSON.stringify(value));
// Set expiration in 2 year
const date = new Date();
date.setFullYear(date.getFullYear()+2);
document.cookie = key + '=' + cookie_data + '; expires=' + date.toGMTString();
},
remove: function (key) {
document.cookie = key + '=; Max-Age=0';
}
};
})()
};

@ -1,13 +1,19 @@
'use strict';
var community_data = JSON.parse(document.getElementById('community_data').textContent);
var community_data = JSON.parse(document.getElementById('community_data').innerHTML);
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 hide_youtube_replies(event) {
var target = event.target;
var sub_text = target.getAttribute('data-inner-text');
var inner_text = target.getAttribute('data-sub-text');
sub_text = target.getAttribute('data-inner-text');
inner_text = target.getAttribute('data-sub-text');
var body = target.parentNode.parentNode.children[1];
body = target.parentNode.parentNode.children[1];
body.style.display = 'none';
target.innerHTML = sub_text;
@ -19,10 +25,10 @@ function hide_youtube_replies(event) {
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');
sub_text = target.getAttribute('data-inner-text');
inner_text = target.getAttribute('data-sub-text');
var body = target.parentNode.parentNode.children[1];
body = target.parentNode.parentNode.children[1];
body.style.display = '';
target.innerHTML = sub_text;
@ -31,6 +37,13 @@ function show_youtube_replies(event) {
target.setAttribute('data-sub-text', sub_text);
}
function number_with_separator(val) {
while (/(\d+)(\d{3})/.test(val.toString())) {
val = val.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
}
return val;
}
function get_youtube_replies(target, load_more) {
var continuation = target.getAttribute('data-continuation');
@ -44,39 +57,47 @@ function get_youtube_replies(target, load_more) {
'&hl=' + community_data.preferences.locale +
'&thin_mode=' + community_data.preferences.thin_mode +
'&continuation=' + continuation;
helpers.xhr('GET', url, {}, {
on200: function (response) {
if (load_more) {
body = body.parentNode.parentNode;
body.removeChild(body.lastElementChild);
body.innerHTML += response.contentHtml;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
if (load_more) {
body = body.parentNode.parentNode;
body.removeChild(body.lastElementChild);
body.innerHTML += xhr.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', community_data.hide_replies_text);
a.setAttribute('data-inner-text', community_data.show_replies_text);
a.innerText = community_data.hide_replies_text;
var div = document.createElement('div');
div.innerHTML = xhr.response.contentHtml;
body.appendChild(p);
body.appendChild(div);
}
} 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', community_data.hide_replies_text);
a.setAttribute('data-inner-text', community_data.show_replies_text);
a.textContent = community_data.hide_replies_text;
var div = document.createElement('div');
div.innerHTML = response.contentHtml;
body.appendChild(p);
body.appendChild(div);
body.innerHTML = fallback;
}
},
onNon200: function (xhr) {
body.innerHTML = fallback;
},
onTimeout: function (xhr) {
console.warn('Pulling comments failed');
body.innerHTML = fallback;
}
});
}
xhr.ontimeout = function () {
console.log('Pulling comments failed.');
body.innerHTML = fallback;
}
xhr.send();
}

@ -1,62 +1,102 @@
'use strict';
var video_data = JSON.parse(document.getElementById('video_data').textContent);
var video_data = JSON.parse(document.getElementById('video_data').innerHTML);
function get_playlist(plid, retries) {
if (retries == undefined) retries = 5;
if (retries <= 0) {
console.log('Failed to pull playlist');
return;
}
function get_playlist(plid) {
var plid_url;
if (plid.startsWith('RD')) {
plid_url = '/api/v1/mixes/' + plid +
var plid_url = '/api/v1/mixes/' + plid +
'?continuation=' + video_data.id +
'&format=html&hl=' + video_data.preferences.locale;
} else {
plid_url = '/api/v1/playlists/' + plid +
var plid_url = '/api/v1/playlists/' + plid +
'?index=' + video_data.index +
'&continuation' + video_data.id +
'&format=html&hl=' + video_data.preferences.locale;
}
helpers.xhr('GET', plid_url, {retries: 5, entity_name: 'playlist'}, {
on200: function (response) {
if (!response.nextVideo)
return;
player.on('ended', function () {
var url = new URL('https://example.com/embed/' + response.nextVideo);
url.searchParams.set('list', plid);
if (!plid.startsWith('RD'))
url.searchParams.set('index', response.index);
if (video_data.params.autoplay || video_data.params.continue_autoplay)
url.searchParams.set('autoplay', '1');
if (video_data.params.listen !== video_data.preferences.listen)
url.searchParams.set('listen', video_data.params.listen);
if (video_data.params.speed !== video_data.preferences.speed)
url.searchParams.set('speed', video_data.params.speed);
if (video_data.params.local !== video_data.preferences.local)
url.searchParams.set('local', video_data.params.local);
location.assign(url.pathname + url.search);
});
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', plid_url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
if (xhr.response.nextVideo) {
player.on('ended', function () {
var url = new URL('https://example.com/embed/' + xhr.response.nextVideo);
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
url.searchParams.set('autoplay', '1');
}
if (video_data.params.listen !== video_data.preferences.listen) {
url.searchParams.set('listen', video_data.params.listen);
}
if (video_data.params.speed !== video_data.preferences.speed) {
url.searchParams.set('speed', video_data.params.speed);
}
if (video_data.params.local !== video_data.preferences.local) {
url.searchParams.set('local', video_data.params.local);
}
url.searchParams.set('list', plid);
if (!plid.startsWith('RD')) {
url.searchParams.set('index', xhr.response.index);
}
location.assign(url.pathname + url.search);
});
}
}
}
});
}
xhr.onerror = function () {
console.log('Pulling playlist failed... ' + retries + '/5');
setTimeout(function () { get_playlist(plid, retries - 1) }, 1000);
}
xhr.ontimeout = function () {
console.log('Pulling playlist failed... ' + retries + '/5');
get_playlist(plid, retries - 1);
}
xhr.send();
}
addEventListener('load', function (e) {
window.addEventListener('load', function (e) {
if (video_data.plid) {
get_playlist(video_data.plid);
} else if (video_data.video_series) {
player.on('ended', function () {
var url = new URL('https://example.com/embed/' + video_data.video_series.shift());
if (video_data.params.autoplay || video_data.params.continue_autoplay)
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
url.searchParams.set('autoplay', '1');
if (video_data.params.listen !== video_data.preferences.listen)
}
if (video_data.params.listen !== video_data.preferences.listen) {
url.searchParams.set('listen', video_data.params.listen);
if (video_data.params.speed !== video_data.preferences.speed)
}
if (video_data.params.speed !== video_data.preferences.speed) {
url.searchParams.set('speed', video_data.params.speed);
if (video_data.params.local !== video_data.preferences.local)
}
if (video_data.params.local !== video_data.preferences.local) {
url.searchParams.set('local', video_data.params.local);
if (video_data.video_series.length !== 0)
url.searchParams.set('playlist', video_data.video_series.join(','));
}
if (video_data.video_series.length !== 0) {
url.searchParams.set('playlist', video_data.video_series.join(','))
}
location.assign(url.pathname + url.search);
});

@ -0,0 +1,3 @@
// Disable Web Workers. Fixes Video.js CSP violation (created by `new Worker(objURL)`):
// Refused to create a worker from 'blob:http://host/id' because it violates the following Content Security Policy directive: "worker-src 'self'".
window.Worker = undefined;

@ -1,6 +1,8 @@
'use strict';
(function () {
var n2a = function (n) { return Array.prototype.slice.call(n); };
var video_player = document.getElementById('player_html5_api');
if (video_player) {
video_player.onmouseenter = function () { video_player['data-title'] = video_player['title']; video_player['title'] = ''; };
@ -9,141 +11,134 @@
}
// For dynamically inserted elements
addEventListener('click', function (e) {
if (!e || !e.target) return;
var t = e.target;
var handler_name = t.getAttribute('data-onclick');
document.addEventListener('click', function (e) {
if (!e || !e.target) { return; }
e = e.target;
var handler_name = e.getAttribute('data-onclick');
switch (handler_name) {
case 'jump_to_time':
e.preventDefault();
var time = t.getAttribute('data-jump-time');
var time = e.getAttribute('data-jump-time');
player.currentTime(time);
break;
case 'get_youtube_replies':
var load_more = t.getAttribute('data-load-more') !== null;
var load_replies = t.getAttribute('data-load-replies') !== null;
get_youtube_replies(t, load_more, load_replies);
var load_more = e.getAttribute('data-load-more') !== null;
get_youtube_replies(e, load_more);
break;
case 'toggle_parent':
e.preventDefault();
toggle_parent(t);
toggle_parent(e);
break;
default:
break;
}
});
document.querySelectorAll('[data-mouse="switch_classes"]').forEach(function (el) {
var classes = el.getAttribute('data-switch-classes').split(',');
var classOnEnter = classes[0];
var classOnLeave = classes[1];
function toggle_classes(toAdd, toRemove) {
el.classList.add(toAdd);
el.classList.remove(toRemove);
}
el.onmouseenter = function () { toggle_classes(classOnEnter, classOnLeave); };
el.onmouseleave = function () { toggle_classes(classOnLeave, classOnEnter); };
n2a(document.querySelectorAll('[data-mouse="switch_classes"]')).forEach(function (e) {
var classes = e.getAttribute('data-switch-classes').split(',');
var ec = classes[0];
var lc = classes[1];
var onoff = function (on, off) {
var cs = e.getAttribute('class');
cs = cs.split(off).join(on);
e.setAttribute('class', cs);
};
e.onmouseenter = function () { onoff(ec, lc); };
e.onmouseleave = function () { onoff(lc, ec); };
});
document.querySelectorAll('[data-onsubmit="return_false"]').forEach(function (el) {
el.onsubmit = function () { return false; };
n2a(document.querySelectorAll('[data-onsubmit="return_false"]')).forEach(function (e) {
e.onsubmit = function () { return false; };
});
document.querySelectorAll('[data-onclick="mark_watched"]').forEach(function (el) {
el.onclick = function () { mark_watched(el); };
n2a(document.querySelectorAll('[data-onclick="mark_watched"]')).forEach(function (e) {
e.onclick = function () { mark_watched(e); };
});
document.querySelectorAll('[data-onclick="mark_unwatched"]').forEach(function (el) {
el.onclick = function () { mark_unwatched(el); };
n2a(document.querySelectorAll('[data-onclick="mark_unwatched"]')).forEach(function (e) {
e.onclick = function () { mark_unwatched(e); };
});
document.querySelectorAll('[data-onclick="add_playlist_video"]').forEach(function (el) {
el.onclick = function () { add_playlist_video(el); };
n2a(document.querySelectorAll('[data-onclick="add_playlist_video"]')).forEach(function (e) {
e.onclick = function () { add_playlist_video(e); };
});
document.querySelectorAll('[data-onclick="add_playlist_item"]').forEach(function (el) {
el.onclick = function () { add_playlist_item(el); };
n2a(document.querySelectorAll('[data-onclick="add_playlist_item"]')).forEach(function (e) {
e.onclick = function () { add_playlist_item(e); };
});
document.querySelectorAll('[data-onclick="remove_playlist_item"]').forEach(function (el) {
el.onclick = function () { remove_playlist_item(el); };
n2a(document.querySelectorAll('[data-onclick="remove_playlist_item"]')).forEach(function (e) {
e.onclick = function () { remove_playlist_item(e); };
});
document.querySelectorAll('[data-onclick="revoke_token"]').forEach(function (el) {
el.onclick = function () { revoke_token(el); };
n2a(document.querySelectorAll('[data-onclick="revoke_token"]')).forEach(function (e) {
e.onclick = function () { revoke_token(e); };
});
document.querySelectorAll('[data-onclick="remove_subscription"]').forEach(function (el) {
el.onclick = function () { remove_subscription(el); };
n2a(document.querySelectorAll('[data-onclick="remove_subscription"]')).forEach(function (e) {
e.onclick = function () { remove_subscription(e); };
});
document.querySelectorAll('[data-onclick="notification_requestPermission"]').forEach(function (el) {
el.onclick = function () { Notification.requestPermission(); };
n2a(document.querySelectorAll('[data-onclick="notification_requestPermission"]')).forEach(function (e) {
e.onclick = function () { Notification.requestPermission(); };
});
document.querySelectorAll('[data-onrange="update_volume_value"]').forEach(function (el) {
function update_volume_value() {
document.getElementById('volume-value').textContent = el.value;
}
el.oninput = update_volume_value;
el.onchange = update_volume_value;
n2a(document.querySelectorAll('[data-onrange="update_volume_value"]')).forEach(function (e) {
var cb = function () { update_volume_value(e); }
e.oninput = cb;
e.onchange = cb;
});
function update_volume_value(element) {
document.getElementById('volume-value').innerText = element.value;
}
function revoke_token(target) {
var row = target.parentNode.parentNode.parentNode.parentNode.parentNode;
row.style.display = 'none';
var count = document.getElementById('count');
count.textContent--;
count.innerText = count.innerText - 1;
var referer = window.encodeURIComponent(document.location.href);
var url = '/token_ajax?action_revoke_token=1&redirect=false' +
'&referer=' + encodeURIComponent(location.href) +
'&referer=' + referer +
'&session=' + target.getAttribute('data-session');
var payload = 'csrf_token=' + target.parentNode.querySelector('input[name="csrf_token"]').value;
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
count.textContent++;
row.style.display = '';
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
count.innerText = parseInt(count.innerText) + 1;
row.style.display = '';
}
}
});
}
var csrf_token = target.parentNode.querySelector('input[name="csrf_token"]').value;
xhr.send('csrf_token=' + csrf_token);
}
function remove_subscription(target) {
var row = target.parentNode.parentNode.parentNode.parentNode.parentNode;
row.style.display = 'none';
var count = document.getElementById('count');
count.textContent--;
count.innerText = count.innerText - 1;
var referer = window.encodeURIComponent(document.location.href);
var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
'&referer=' + encodeURIComponent(location.href) +
'&referer=' + referer +
'&c=' + target.getAttribute('data-ucid');
var payload = 'csrf_token=' + target.parentNode.querySelector('input[name="csrf_token"]').value;
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
count.textContent++;
row.style.display = '';
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
count.innerText = parseInt(count.innerText) + 1;
row.style.display = '';
}
}
});
}
// Handle keypresses
addEventListener('keydown', function (event) {
// Ignore modifier keys
if (event.ctrlKey || event.metaKey) return;
// Ignore shortcuts if any text input is focused
let focused_tag = document.activeElement.tagName.toLowerCase();
const allowed = /^(button|checkbox|file|radio|submit)$/;
if (focused_tag === 'textarea') return;
if (focused_tag === 'input') {
let focused_type = document.activeElement.type.toLowerCase();
if (!allowed.test(focused_type)) return;
}
// Focus search bar on '/'
if (event.key === '/') {
document.getElementById('searchbox').focus();
event.preventDefault();
}
});
var csrf_token = target.parentNode.querySelector('input[name="csrf_token"]').value;
xhr.send('csrf_token=' + csrf_token);
}
})();

@ -1,30 +1,46 @@
'use strict';
var notification_data = JSON.parse(document.getElementById('notification_data').textContent);
/** Boolean meaning 'some tab have stream' */
const STORAGE_KEY_STREAM = 'stream';
/** Number of notifications. May be increased or reset */
const STORAGE_KEY_NOTIF_COUNT = 'notification_count';
var notification_data = JSON.parse(document.getElementById('notification_data').innerHTML);
var notifications, delivered;
var notifications_mock = { close: function () { } };
function get_subscriptions() {
helpers.xhr('GET', '/api/v1/auth/subscriptions?fields=authorId', {
retries: 5,
entity_name: 'subscriptions'
}, {
on200: create_notification_stream
});
function get_subscriptions(callback, retries) {
if (retries == undefined) retries = 5;
if (retries <= 0) {
return;
}
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', '/api/v1/auth/subscriptions?fields=authorId', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
subscriptions = xhr.response;
callback(subscriptions);
}
}
}
xhr.onerror = function () {
console.log('Pulling subscriptions failed... ' + retries + '/5');
setTimeout(function () { get_subscriptions(callback, retries - 1) }, 1000);
}
xhr.ontimeout = function () {
console.log('Pulling subscriptions failed... ' + retries + '/5');
get_subscriptions(callback, retries - 1);
}
xhr.send();
}
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', {
withCredentials: true,
payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId; }).join(','),
payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId }).join(','),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
delivered = [];
@ -32,100 +48,96 @@ function create_notification_stream(subscriptions) {
var start_time = Math.round(new Date() / 1000);
notifications.onmessage = function (event) {
if (!event.id) return;
if (!event.id) {
return;
}
var notification = JSON.parse(event.data);
console.info('Got notification:', notification);
// Ignore not actual and delivered notifications
if (start_time > notification.published || delivered.includes(notification.videoId)) return;
delivered.push(notification.videoId);
let notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
notification_count++;
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
update_ticker_count();
// permission for notifications handled on settings page. JS handler is in handlers.js
if (window.Notification && Notification.permission === 'granted') {
var notification_text = notification.liveNow ? notification_data.live_now_text : notification_data.upload_text;
notification_text = notification_text.replace('`x`', notification.author);
var system_notification = new Notification(notification_text, {
body: notification.title,
icon: '/ggpht' + new URL(notification.authorThumbnails[2].url).pathname,
img: '/ggpht' + new URL(notification.authorThumbnails[4].url).pathname
});
system_notification.onclick = function (e) {
open('/watch?v=' + notification.videoId, '_blank');
};
console.log('Got notification:', notification);
if (start_time < notification.published && !delivered.includes(notification.videoId)) {
if (Notification.permission === 'granted') {
var system_notification =
new Notification((notification.liveNow ? notification_data.live_now_text : notification_data.upload_text).replace('`x`', notification.author), {
body: notification.title,
icon: '/ggpht' + new URL(notification.authorThumbnails[2].url).pathname,
img: '/ggpht' + new URL(notification.authorThumbnails[4].url).pathname,
tag: notification.videoId
});
system_notification.onclick = function (event) {
window.open('/watch?v=' + event.currentTarget.tag, '_blank');
}
}
delivered.push(notification.videoId);
localStorage.setItem('notification_count', parseInt(localStorage.getItem('notification_count') || '0') + 1);
var notification_ticker = document.getElementById('notification_ticker');
if (parseInt(localStorage.getItem('notification_count')) > 0) {
notification_ticker.innerHTML =
'<span id="notification_count">' + localStorage.getItem('notification_count') + '</span> <i class="icon ion-ios-notifications"></i>';
} else {
notification_ticker.innerHTML =
'<i class="icon ion-ios-notifications-outline"></i>';
}
}
};
notifications.addEventListener('error', function (e) {
console.warn('Something went wrong with notifications, trying to reconnect...');
notifications = notifications_mock;
setTimeout(get_subscriptions, 1000);
});
}
notifications.addEventListener('error', handle_notification_error);
notifications.stream();
}
function update_ticker_count() {
var notification_ticker = document.getElementById('notification_ticker');
const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
if (notification_count > 0) {
notification_ticker.innerHTML =
'<span id="notification_count">' + notification_count + '</span> <i class="icon ion-ios-notifications"></i>';
} else {
notification_ticker.innerHTML =
'<i class="icon ion-ios-notifications-outline"></i>';
}
function handle_notification_error(event) {
console.log('Something went wrong with notifications, trying to reconnect...');
notifications = { close: function () { } };
setTimeout(function () { get_subscriptions(create_notification_stream) }, 1000);
}
function start_stream_if_needed() {
// random wait for other tabs set 'stream' flag
setTimeout(function () {
if (!helpers.storage.get(STORAGE_KEY_STREAM)) {
// if no one set 'stream', set it by yourself and start stream
helpers.storage.set(STORAGE_KEY_STREAM, true);
notifications = notifications_mock;
get_subscriptions();
}
}, Math.random() * 1000 + 50); // [0.050 .. 1.050) second
}
window.addEventListener('load', function (e) {
localStorage.setItem('notification_count', document.getElementById('notification_count') ? document.getElementById('notification_count').innerText : '0');
addEventListener('storage', function (e) {
if (e.key === STORAGE_KEY_NOTIF_COUNT)
update_ticker_count();
// if 'stream' key was removed
if (e.key === STORAGE_KEY_STREAM && !helpers.storage.get(STORAGE_KEY_STREAM)) {
if (notifications) {
// restore it if we have active stream
helpers.storage.set(STORAGE_KEY_STREAM, true);
} else {
start_stream_if_needed();
}
if (localStorage.getItem('stream')) {
localStorage.removeItem('stream');
} else {
setTimeout(function () {
if (!localStorage.getItem('stream')) {
notifications = { close: function () { } };
localStorage.setItem('stream', true);
get_subscriptions(create_notification_stream);
}
}, Math.random() * 1000 + 50);
}
});
addEventListener('load', function () {
var notification_count_el = document.getElementById('notification_count');
var notification_count = notification_count_el ? parseInt(notification_count_el.textContent) : 0;
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
if (helpers.storage.get(STORAGE_KEY_STREAM))
helpers.storage.remove(STORAGE_KEY_STREAM);
start_stream_if_needed();
window.addEventListener('storage', function (e) {
if (e.key === 'stream' && !e.newValue) {
if (notifications) {
localStorage.setItem('stream', true);
} else {
setTimeout(function () {
if (!localStorage.getItem('stream')) {
notifications = { close: function () { } };
localStorage.setItem('stream', true);
get_subscriptions(create_notification_stream);
}
}, Math.random() * 1000 + 50);
}
} else if (e.key === 'notification_count') {
var notification_ticker = document.getElementById('notification_ticker');
if (parseInt(e.newValue) > 0) {
notification_ticker.innerHTML =
'<span id="notification_count">' + e.newValue + '</span> <i class="icon ion-ios-notifications"></i>';
} else {
notification_ticker.innerHTML =
'<i class="icon ion-ios-notifications-outline"></i>';
}
}
});
});
addEventListener('unload', function () {
// let chance to other tabs to be a streamer via firing 'storage' event
if (notifications) helpers.storage.remove(STORAGE_KEY_STREAM);
window.addEventListener('unload', function (e) {
if (notifications) {
localStorage.removeItem('stream');
}
});

@ -1,6 +1,5 @@
'use strict';
var player_data = JSON.parse(document.getElementById('player_data').textContent);
var video_data = JSON.parse(document.getElementById('video_data').textContent);
var player_data = JSON.parse(document.getElementById('player_data').innerHTML);
var video_data = JSON.parse(document.getElementById('video_data').innerHTML);
var options = {
preload: 'auto',
@ -15,21 +14,13 @@ var options = {
'durationDisplay',
'progressControl',
'remainingTimeDisplay',
'Spacer',
'captionsButton',
'audioTrackButton',
'qualitySelector',
'playbackRateMenuButton',
'fullscreenToggle'
]
},
html5: {
preloadTextTracks: false,
vhs: {
overrideNative: true
}
}
};
}
if (player_data.aspect_ratio) {
options.aspectRatio = player_data.aspect_ratio;
@ -37,165 +28,61 @@ if (player_data.aspect_ratio) {
var embed_url = new URL(location);
embed_url.searchParams.delete('v');
var short_url = location.origin + '/' + video_data.id + embed_url.search;
short_url = location.origin + '/' + video_data.id + embed_url.search;
embed_url = location.origin + '/embed/' + video_data.id + embed_url.search;
var save_player_pos_key = 'save_player_pos';
videojs.Vhs.xhr.beforeRequest = function(options) {
// set local if requested not videoplayback
if (!options.uri.includes('videoplayback')) {
if (!options.uri.includes('local=true'))
options.uri += '?local=true';
}
return options;
};
var player = videojs('player', options);
player.on('error', function () {
if (video_data.params.quality === 'dash') return;
var localNotDisabled = (
!player.currentSrc().includes('local=true') && !video_data.local_disabled
);
var reloadMakesSense = (
player.error().code === MediaError.MEDIA_ERR_NETWORK ||
player.error().code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED
);
if (localNotDisabled) {
// add local=true to all current sources
player.src(player.currentSources().map(function (source) {
source.src += '&local=true';
return source;
}));
} else if (reloadMakesSense) {
setTimeout(function () {
console.warn('An error occurred in the player, reloading...');
// After load() all parameters are reset. Save them
var currentTime = player.currentTime();
var playbackRate = player.playbackRate();
var paused = player.paused();
player.load();
if (currentTime > 0.5) currentTime -= 0.5;
player.currentTime(currentTime);
player.playbackRate(playbackRate);
if (!paused) player.play();
}, 5000);
}
});
if (video_data.params.quality === 'dash') {
player.reloadSourceOnError({
errorInterval: 10
});
}
/**
* Function for add time argument to url
* @param {String} url
* @returns {URL} urlWithTimeArg
*/
function addCurrentTimeToURL(url) {
var urlUsed = new URL(url);
urlUsed.searchParams.delete('start');
var currentTime = Math.ceil(player.currentTime());
if (currentTime > 0)
urlUsed.searchParams.set('t', currentTime);
else if (urlUsed.searchParams.has('t'))
urlUsed.searchParams.delete('t');
return urlUsed;
}
var shareOptions = {
socials: ['fbFeed', 'tw', 'reddit', 'email'],
get url() {
return addCurrentTimeToURL(short_url);
},
url: short_url,
title: player_data.title,
description: player_data.description,
image: player_data.thumbnail,
get embedCode() {
// Single quotes inside here required. HTML inserted as is into value attribute of input
return "<iframe id='ivplayer' width='640' height='360' src='" +
addCurrentTimeToURL(embed_url) + "' style='border:none;'></iframe>";
}
};
embedCode: "<iframe id='ivplayer' width='640' height='360' src='" + embed_url + "' style='border:none;'></iframe>"
}
var player = videojs('player', options);
if (location.pathname.startsWith('/embed/')) {
var overlay_content = '<h1><a rel="noopener" target="_blank" href="' + location.origin + '/watch?v=' + video_data.id + '">' + player_data.title + '</a></h1>';
player.overlay({
overlays: [
{ start: 'loadstart', content: overlay_content, end: 'playing', align: 'top'},
{ start: 'pause', content: overlay_content, end: 'playing', align: 'top'}
]
overlays: [{
start: 'loadstart',
content: '<h1><a rel="noopener" target="_blank" href="' + location.origin + '/watch?v=' + video_data.id + '">' + player_data.title + '</a></h1>',
end: 'playing',
align: 'top'
}, {
start: 'pause',
content: '<h1><a rel="noopener" target="_blank" href="' + location.origin + '/watch?v=' + video_data.id + '">' + player_data.title + '</a></h1>',
end: 'playing',
align: 'top'
}]
});
}
// Detect mobile users and initialize mobileUi for better UX
// Detection code taken from https://stackoverflow.com/a/20293441
player.on('error', function (event) {
if (player.error().code === 2 || player.error().code === 4) {
setInterval(setTimeout(function (event) {
console.log('An error occured in the player, reloading...');
function isMobile() {
try{ document.createEvent('TouchEvent'); return true; }
catch(e){ return false; }
}
if (isMobile()) {
player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } });
var buttons = ['playToggle', 'volumePanel', 'captionsButton'];
if (!video_data.params.listen && video_data.params.quality === 'dash') buttons.push('audioTrackButton');
if (video_data.params.listen || video_data.params.quality !== 'dash') buttons.push('qualitySelector');
// Create new control bar object for operation buttons
const ControlBar = videojs.getComponent('controlBar');
let operations_bar = new ControlBar(player, {
children: [],
playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
});
buttons.slice(1).forEach(function (child) {operations_bar.addChild(child);});
// Remove operation buttons from primary control bar
var primary_control_bar = player.getChild('controlBar');
buttons.forEach(function (child) {primary_control_bar.removeChild(child);});
var operations_bar_element = operations_bar.el();
operations_bar_element.classList.add('mobile-operations-bar');
player.addChild(operations_bar);
var currentTime = player.currentTime();
var playbackRate = player.playbackRate();
var paused = player.paused();
// Playback menu doesn't work when it's initialized outside of the primary control bar
var playback_element = document.getElementsByClassName('vjs-playback-rate')[0];
operations_bar_element.append(playback_element);
player.load();
// The share and http source selector element can't be fetched till the players ready.
player.one('playing', function () {
var share_element = document.getElementsByClassName('vjs-share-control')[0];
operations_bar_element.append(share_element);
if (currentTime > 0.5) {
currentTime -= 0.5;
}
if (!video_data.params.listen && video_data.params.quality === 'dash') {
var http_source_selector = document.getElementsByClassName('vjs-http-source-selector vjs-menu-button')[0];
operations_bar_element.append(http_source_selector);
}
});
}
player.currentTime(currentTime);
player.playbackRate(playbackRate);
// Enable VR video support
if (!video_data.params.listen && video_data.vr && video_data.params.vr_mode) {
player.crossOrigin('anonymous');
switch (video_data.projection_type) {
case 'EQUIRECTANGULAR':
player.vr({projection: 'equirectangular'});
default: // Should only be 'MESH' but we'll use this as a fallback.
player.vr({projection: 'EAC'});
if (!paused) {
player.play();
}
}, 5000), 5000);
}
}
});
// Add markers
if (video_data.params.video_start > 0 || video_data.params.video_end > 0) {
@ -209,8 +96,13 @@ if (video_data.params.video_start > 0 || video_data.params.video_end > 0) {
player.markers({
onMarkerReached: function (marker) {
if (marker.text === 'End')
player.loop() ? player.markers.prev('Start') : player.pause();
if (marker.text === 'End') {
if (player.loop()) {
player.markers.prev('Start');
} else {
player.pause();
}
}
},
markers: markers
});
@ -221,76 +113,9 @@ if (video_data.params.video_start > 0 || video_data.params.video_end > 0) {
player.volume(video_data.params.volume / 100);
player.playbackRate(video_data.params.speed);
/**
* Method for getting the contents of a cookie
*
* @param {String} name Name of cookie
* @returns {String|null} cookieValue
*/
function getCookieValue(name) {
var cookiePrefix = name + '=';
var matchedCookie = document.cookie.split(';').find(function (item) {return item.includes(cookiePrefix);});
if (matchedCookie)
return matchedCookie.replace(cookiePrefix, '');
return null;
}
/**
* Method for updating the 'PREFS' cookie (or creating it if missing)
*
* @param {number} newVolume New volume defined (null if unchanged)
* @param {number} newSpeed New speed defined (null if unchanged)
*/
function updateCookie(newVolume, newSpeed) {
var volumeValue = newVolume !== null ? newVolume : video_data.params.volume;
var speedValue = newSpeed !== null ? newSpeed : video_data.params.speed;
var cookieValue = getCookieValue('PREFS');
var cookieData;
if (cookieValue !== null) {
var cookieJson = JSON.parse(decodeURIComponent(cookieValue));
cookieJson.volume = volumeValue;
cookieJson.speed = speedValue;
cookieData = encodeURIComponent(JSON.stringify(cookieJson));
} else {
cookieData = encodeURIComponent(JSON.stringify({ 'volume': volumeValue, 'speed': speedValue }));
}
// Set expiration in 2 year
var date = new Date();
date.setFullYear(date.getFullYear() + 2);
var ipRegex = /^((\d+\.){3}\d+|[\dA-Fa-f]*:[\d:A-Fa-f]*:[\d:A-Fa-f]+)$/;
var domainUsed = location.hostname;
// Fix for a bug in FF where the leading dot in the FQDN is not ignored
if (domainUsed.charAt(0) !== '.' && !ipRegex.test(domainUsed) && domainUsed !== 'localhost')
domainUsed = '.' + location.hostname;
var secure = location.protocol.startsWith("https") ? " Secure;" : "";
document.cookie = 'PREFS=' + cookieData + '; SameSite=Lax; path=/; domain=' +
domainUsed + '; expires=' + date.toGMTString() + ';' + secure;
video_data.params.volume = volumeValue;
video_data.params.speed = speedValue;
}
player.on('ratechange', function () {
updateCookie(null, player.playbackRate());
if (isMobile()) {
player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } });
}
});
player.on('volumechange', function () {
updateCookie(Math.ceil(player.volume() * 100), null);
});
player.on('waiting', function () {
if (player.playbackRate() > 1 && player.liveTracker.isLive() && player.liveTracker.atLiveEdge()) {
console.info('Player has caught up to source, resetting playbackRate');
console.log('Player has caught up to source, resetting playbackRate.')
player.playbackRate(1);
}
});
@ -299,39 +124,19 @@ if (video_data.premiere_timestamp && Math.round(new Date() / 1000) < video_data.
player.getChild('bigPlayButton').hide();
}
if (video_data.params.save_player_pos) {
const url = new URL(location);
const hasTimeParam = url.searchParams.has('t');
const rememberedTime = get_video_time();
let lastUpdated = 0;
if(!hasTimeParam) set_seconds_after_start(rememberedTime);
player.on('timeupdate', function () {
const raw = player.currentTime();
const time = Math.floor(raw);
if(lastUpdated !== time && raw <= video_data.length_seconds - 15) {
save_video_time(time);
lastUpdated = time;
}
});
}
else remove_all_video_times();
if (video_data.params.autoplay) {
var bpb = player.getChild('bigPlayButton');
bpb.hide();
player.ready(function () {
new Promise(function (resolve, reject) {
setTimeout(function () {resolve(1);}, 1);
setTimeout(() => resolve(1), 1);
}).then(function (result) {
var promise = player.play();
if (promise !== undefined) {
promise.then(function () {
}).catch(function (error) {
promise.then(_ => {
}).catch(error => {
bpb.show();
});
}
@ -341,48 +146,38 @@ if (video_data.params.autoplay) {
if (!video_data.params.listen && video_data.params.quality === 'dash') {
player.httpSourceSelector();
if (video_data.params.quality_dash !== 'auto') {
player.ready(function () {
player.on('loadedmetadata', function () {
const qualityLevels = Array.from(player.qualityLevels()).sort(function (a, b) {return a.height - b.height;});
let targetQualityLevel;
switch (video_data.params.quality_dash) {
case 'best':
targetQualityLevel = qualityLevels.length - 1;
break;
case 'worst':
targetQualityLevel = 0;
break;
default:
const targetHeight = parseInt(video_data.params.quality_dash);
for (let i = 0; i < qualityLevels.length; i++) {
if (qualityLevels[i].height <= targetHeight)
targetQualityLevel = i;
else
break;
}
}
qualityLevels.forEach(function (level, index) {
level.enabled = (index === targetQualityLevel);
});
});
});
}
}
player.vttThumbnails({
src: '/api/v1/storyboards/' + video_data.id + '?height=90',
showTimestamp: true
src: location.origin + '/api/v1/storyboards/' + video_data.id + '?height=90'
});
// Enable annotations
if (!video_data.params.listen && video_data.params.annotations) {
addEventListener('load', function (e) {
addEventListener('__ar_annotation_click', function (e) {
const url = e.detail.url,
target = e.detail.target,
seconds = e.detail.seconds;
window.addEventListener('load', function (e) {
var video_container = document.getElementById('player');
let xhr = new XMLHttpRequest();
xhr.responseType = 'text';
xhr.timeout = 60000;
xhr.open('GET', '/api/v1/annotations/' + video_data.id, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
videojs.registerPlugin('youtubeAnnotationsPlugin', youtubeAnnotationsPlugin);
if (!player.paused()) {
player.youtubeAnnotationsPlugin({ annotationXml: xhr.response, videoContainer: video_container });
} else {
player.one('play', function (event) {
player.youtubeAnnotationsPlugin({ annotationXml: xhr.response, videoContainer: video_container });
});
}
}
}
}
window.addEventListener('__ar_annotation_click', e => {
const { url, target, seconds } = e.detail;
var path = new URL(url);
if (path.href.startsWith('https://www.youtube.com/watch?') && seconds) {
@ -392,104 +187,75 @@ if (!video_data.params.listen && video_data.params.annotations) {
path = path.pathname + path.search;
if (target === 'current') {
location.href = path;
window.location.href = path;
} else if (target === 'new') {
open(path, '_blank');
}
});
helpers.xhr('GET', '/api/v1/annotations/' + video_data.id, {
responseType: 'text',
timeout: 60000
}, {
on200: function (response) {
var video_container = document.getElementById('player');
videojs.registerPlugin('youtubeAnnotationsPlugin', youtubeAnnotationsPlugin);
if (player.paused()) {
player.one('play', function (event) {
player.youtubeAnnotationsPlugin({ annotationXml: response, videoContainer: video_container });
});
} else {
player.youtubeAnnotationsPlugin({ annotationXml: response, videoContainer: video_container });
}
window.open(path, '_blank');
}
});
xhr.send();
});
}
function change_volume(delta) {
function increase_volume(delta) {
const curVolume = player.volume();
let newVolume = curVolume + delta;
newVolume = helpers.clamp(newVolume, 0, 1);
if (newVolume > 1) {
newVolume = 1;
} else if (newVolume < 0) {
newVolume = 0;
}
player.volume(newVolume);
}
function toggle_muted() {
player.muted(!player.muted());
const isMuted = player.muted();
player.muted(!isMuted);
}
function skip_seconds(delta) {
const duration = player.duration();
const curTime = player.currentTime();
let newTime = curTime + delta;
newTime = helpers.clamp(newTime, 0, duration);
if (newTime > duration) {
newTime = duration;
} else if (newTime < 0) {
newTime = 0;
}
player.currentTime(newTime);
}
function set_seconds_after_start(delta) {
const start = video_data.params.video_start;
player.currentTime(start + delta);
}
function save_video_time(seconds) {
const all_video_times = get_all_video_times();
all_video_times[video_data.id] = seconds;
helpers.storage.set(save_player_pos_key, all_video_times);
}
function get_video_time() {
return get_all_video_times()[video_data.id] || 0;
}
function get_all_video_times() {
return helpers.storage.get(save_player_pos_key) || {};
}
function remove_all_video_times() {
helpers.storage.remove(save_player_pos_key);
}
function set_time_percent(percent) {
const duration = player.duration();
const newTime = duration * (percent / 100);
player.currentTime(newTime);
}
function play() { player.play(); }
function pause() { player.pause(); }
function stop() { player.pause(); player.currentTime(0); }
function toggle_play() { player.paused() ? play() : pause(); }
function toggle_play() {
if (player.paused()) {
player.play();
} else {
player.pause();
}
}
const toggle_captions = (function () {
let toggledTrack = null;
function bindChange(onOrOff) {
player.textTracks()[onOrOff]('change', function (e) {
toggledTrack = null;
});
}
const onChange = function (e) {
toggledTrack = null;
};
const bindChange = function (onOrOff) {
player.textTracks()[onOrOff]('change', onChange);
};
// Wrapper function to ignore our own emitted events and only listen
// to events emitted by Video.js on click on the captions menu items.
function setMode(track, mode) {
const setMode = function (track, mode) {
bindChange('off');
track.mode = mode;
setTimeout(function () {
window.setTimeout(function () {
bindChange('on');
}, 0);
}
};
bindChange('on');
return function () {
if (toggledTrack !== null) {
@ -509,7 +275,9 @@ const toggle_captions = (function () {
const tracks = player.textTracks();
for (let i = 0; i < tracks.length; i++) {
const track = tracks[i];
if (track.kind !== 'captions') continue;
if (track.kind !== 'captions') {
continue;
}
if (fallbackCaptionsTrack === null) {
fallbackCaptionsTrack = track;
@ -530,18 +298,26 @@ const toggle_captions = (function () {
})();
function toggle_fullscreen() {
player.isFullscreen() ? player.exitFullscreen() : player.requestFullscreen();
if (player.isFullscreen()) {
player.exitFullscreen();
} else {
player.requestFullscreen();
}
}
function increase_playback_rate(steps) {
const maxIndex = options.playbackRates.length - 1;
const curIndex = options.playbackRates.indexOf(player.playbackRate());
let newIndex = curIndex + steps;
newIndex = helpers.clamp(newIndex, 0, maxIndex);
if (newIndex > maxIndex) {
newIndex = maxIndex;
} else if (newIndex < 0) {
newIndex = 0;
}
player.playbackRate(options.playbackRates[newIndex]);
}
addEventListener('keydown', function (e) {
window.addEventListener('keydown', e => {
if (e.target.tagName.toLowerCase() === 'input') {
// Ignore input when focus is on certain elements, e.g. form fields.
return;
@ -565,19 +341,18 @@ addEventListener('keydown', function (e) {
switch (decoratedKey) {
case ' ':
case 'k':
case 'MediaPlayPause':
action = toggle_play;
break;
case 'MediaPlay': action = play; break;
case 'MediaPause': action = pause; break;
case 'MediaStop': action = stop; break;
case 'ArrowUp':
if (isPlayerFocused) action = change_volume.bind(this, 0.1);
if (isPlayerFocused) {
action = increase_volume.bind(this, 0.1);
}
break;
case 'ArrowDown':
if (isPlayerFocused) action = change_volume.bind(this, -0.1);
if (isPlayerFocused) {
action = increase_volume.bind(this, -0.1);
}
break;
case 'm':
@ -585,18 +360,16 @@ addEventListener('keydown', function (e) {
break;
case 'ArrowRight':
case 'MediaFastForward':
action = skip_seconds.bind(this, 5 * player.playbackRate());
action = skip_seconds.bind(this, 5);
break;
case 'ArrowLeft':
case 'MediaTrackPrevious':
action = skip_seconds.bind(this, -5 * player.playbackRate());
action = skip_seconds.bind(this, -5);
break;
case 'l':
action = skip_seconds.bind(this, 10 * player.playbackRate());
action = skip_seconds.bind(this, 10);
break;
case 'j':
action = skip_seconds.bind(this, -10 * player.playbackRate());
action = skip_seconds.bind(this, -10);
break;
case '0':
@ -609,33 +382,37 @@ addEventListener('keydown', function (e) {
case '7':
case '8':
case '9':
// Ignore numpad numbers
if (code > 57) break;
const percent = (code - 48) * 10;
action = set_time_percent.bind(this, percent);
break;
case 'c': action = toggle_captions; break;
case 'f': action = toggle_fullscreen; break;
case 'c':
action = toggle_captions;
break;
case 'f':
action = toggle_fullscreen;
break;
case 'N':
case 'MediaTrackNext':
action = next_video;
break;
case 'P':
case 'MediaTrackPrevious':
// TODO: Add support to play back previous video.
break;
// TODO: More precise step. Now FPS is taken equal to 29.97
// Common FPS: https://forum.videohelp.com/threads/81868#post323588
// Possible solution is new HTMLVideoElement.requestVideoFrameCallback() https://wicg.github.io/video-rvfc/
case ',': action = function () { pause(); skip_seconds(-1/29.97); }; break;
case '.': action = function () { pause(); skip_seconds( 1/29.97); }; break;
case '.':
// TODO: Add support for next-frame-stepping.
break;
case ',':
// TODO: Add support for previous-frame-stepping.
break;
case '>': action = increase_playback_rate.bind(this, 1); break;
case '<': action = increase_playback_rate.bind(this, -1); break;
case '>':
action = increase_playback_rate.bind(this, 1);
break;
case '<':
action = increase_playback_rate.bind(this, -1);
break;
default:
console.info('Unhandled key down event: %s:', decoratedKey, e);
@ -651,77 +428,47 @@ addEventListener('keydown', function (e) {
// Add support for controlling the player volume by scrolling over it. Adapted from
// https://github.com/ctd1500/videojs-hotkeys/blob/bb4a158b2e214ccab87c2e7b95f42bc45c6bfd87/videojs.hotkeys.js#L292-L328
(function () {
const volumeStep = 0.05;
const enableVolumeScroll = true;
const enableHoverScroll = true;
const doc = document;
const pEl = document.getElementById('player');
var volumeHover = false;
var volumeSelector = pEl.querySelector('.vjs-volume-menu-button') || pEl.querySelector('.vjs-volume-panel');
if (volumeSelector !== null) {
if (volumeSelector != null) {
volumeSelector.onmouseover = function () { volumeHover = true; };
volumeSelector.onmouseout = function () { volumeHover = false; };
}
function mouseScroll(event) {
// When controls are disabled, hotkeys will be disabled as well
if (!player.controls() || !volumeHover) return;
event.preventDefault();
var wheelMove = event.wheelDelta || -event.detail;
var volumeSign = Math.sign(wheelMove);
var mouseScroll = function mouseScroll(event) {
var activeEl = doc.activeElement;
if (enableHoverScroll) {
// If we leave this undefined then it can match non-existent elements below
activeEl = 0;
}
change_volume(volumeSign * 0.05); // decrease/increase by 5%
}
// When controls are disabled, hotkeys will be disabled as well
if (player.controls()) {
if (volumeHover) {
if (enableVolumeScroll) {
event = window.event || event;
var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
event.preventDefault();
if (delta == 1) {
increase_volume(volumeStep);
} else if (delta == -1) {
increase_volume(-volumeStep);
}
}
}
}
};
player.on('mousewheel', mouseScroll);
player.on('DOMMouseScroll', mouseScroll);
player.on("DOMMouseScroll", mouseScroll);
}());
// Since videojs-share can sometimes be blocked, we defer it until last
if (player.share) player.share(shareOptions);
// show the preferred caption by default
if (player_data.preferred_caption_found) {
player.ready(function () {
if (!video_data.params.listen && video_data.params.quality === 'dash') {
// play.textTracks()[0] on DASH mode is showing some debug messages
player.textTracks()[1].mode = 'showing';
} else {
player.textTracks()[0].mode = 'showing';
}
});
}
// Safari audio double duration fix
if (navigator.vendor === 'Apple Computer, Inc.' && video_data.params.listen) {
player.on('loadedmetadata', function () {
player.on('timeupdate', function () {
if (player.remainingTime() < player.duration() / 2 && player.remainingTime() >= 2) {
player.currentTime(player.duration() - 1);
}
});
});
}
// Watch on Invidious link
if (location.pathname.startsWith('/embed/')) {
const Button = videojs.getComponent('Button');
let watch_on_invidious_button = new Button(player);
// Create hyperlink for current instance
var redirect_element = document.createElement('a');
redirect_element.setAttribute('href', location.pathname.replace('/embed/', '/watch?v='));
redirect_element.appendChild(document.createTextNode('Invidious'));
watch_on_invidious_button.el().appendChild(redirect_element);
watch_on_invidious_button.addClass('watch-on-invidious');
var cb = player.getChild('ControlBar');
cb.addChild(watch_on_invidious_button);
}
addEventListener('DOMContentLoaded', function () {
// Save time during redirection on another instance
const changeInstanceLink = document.querySelector('#watch-on-another-invidious-instance > a');
if (changeInstanceLink) changeInstanceLink.addEventListener('click', function () {
changeInstanceLink.href = addCurrentTimeToURL(changeInstanceLink.href);
});
});
player.share(shareOptions);

@ -1,6 +1,4 @@
'use strict';
var playlist_data = JSON.parse(document.getElementById('playlist_data').textContent);
var payload = 'csrf_token=' + playlist_data.csrf_token;
var playlist_data = JSON.parse(document.getElementById('playlist_data').innerHTML);
function add_playlist_video(target) {
var select = target.parentNode.children[0].children[1];
@ -9,12 +7,21 @@ function add_playlist_video(target) {
var url = '/playlist_ajax?action_add_video=1&redirect=false' +
'&video_id=' + target.getAttribute('data-id') +
'&playlist_id=' + option.getAttribute('data-plid');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
helpers.xhr('POST', url, {payload: payload}, {
on200: function (response) {
option.textContent = '✓' + option.textContent;
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
option.innerText = '✓' + option.innerText;
}
}
});
}
xhr.send('csrf_token=' + playlist_data.csrf_token);
}
function add_playlist_item(target) {
@ -24,12 +31,21 @@ function add_playlist_item(target) {
var url = '/playlist_ajax?action_add_video=1&redirect=false' +
'&video_id=' + target.getAttribute('data-id') +
'&playlist_id=' + target.getAttribute('data-plid');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
tile.style.display = '';
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
tile.style.display = '';
}
}
});
}
xhr.send('csrf_token=' + playlist_data.csrf_token);
}
function remove_playlist_item(target) {
@ -39,10 +55,19 @@ function remove_playlist_item(target) {
var url = '/playlist_ajax?action_remove_video=1&redirect=false' +
'&set_video_id=' + target.getAttribute('data-index') +
'&playlist_id=' + target.getAttribute('data-plid');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
tile.style.display = '';
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
tile.style.display = '';
}
}
});
}
}
xhr.send('csrf_token=' + playlist_data.csrf_token);
}

File diff suppressed because one or more lines are too long

@ -1,9 +1,7 @@
'use strict';
var subscribe_data = JSON.parse(document.getElementById('subscribe_data').textContent);
var payload = 'csrf_token=' + subscribe_data.csrf_token;
var subscribe_data = JSON.parse(document.getElementById('subscribe_data').innerHTML);
var subscribe_button = document.getElementById('subscribe');
subscribe_button.parentNode.action = 'javascript:void(0)';
subscribe_button.parentNode['action'] = 'javascript:void(0)';
if (subscribe_button.getAttribute('data-type') === 'subscribe') {
subscribe_button.onclick = subscribe;
@ -11,34 +9,82 @@ if (subscribe_button.getAttribute('data-type') === 'subscribe') {
subscribe_button.onclick = unsubscribe;
}
function subscribe() {
var fallback = subscribe_button.innerHTML;
subscribe_button.onclick = unsubscribe;
subscribe_button.innerHTML = '<b>' + subscribe_data.unsubscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
function subscribe(retries = 5) {
if (retries <= 0) {
console.log('Failed to subscribe.');
return;
}
var url = '/subscription_ajax?action_create_subscription_to_channel=1&redirect=false' +
'&c=' + subscribe_data.ucid;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var fallback = subscribe_button.innerHTML;
subscribe_button.onclick = unsubscribe;
subscribe_button.innerHTML = '<b>' + subscribe_data.unsubscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
helpers.xhr('POST', url, {payload: payload, retries: 5, entity_name: 'subscribe request'}, {
onNon200: function (xhr) {
subscribe_button.onclick = subscribe;
subscribe_button.innerHTML = fallback;
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
subscribe_button.onclick = subscribe;
subscribe_button.innerHTML = fallback;
}
}
});
}
xhr.onerror = function () {
console.log('Subscribing failed... ' + retries + '/5');
setTimeout(function () { subscribe(retries - 1) }, 1000);
}
xhr.ontimeout = function () {
console.log('Subscribing failed... ' + retries + '/5');
subscribe(retries - 1);
}
xhr.send('csrf_token=' + subscribe_data.csrf_token);
}
function unsubscribe() {
var fallback = subscribe_button.innerHTML;
subscribe_button.onclick = subscribe;
subscribe_button.innerHTML = '<b>' + subscribe_data.subscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
function unsubscribe(retries = 5) {
if (retries <= 0) {
console.log('Failed to subscribe');
return;
}
var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
'&c=' + subscribe_data.ucid;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var fallback = subscribe_button.innerHTML;
subscribe_button.onclick = subscribe;
subscribe_button.innerHTML = '<b>' + subscribe_data.subscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
helpers.xhr('POST', url, {payload: payload, retries: 5, entity_name: 'unsubscribe request'}, {
onNon200: function (xhr) {
subscribe_button.onclick = unsubscribe;
subscribe_button.innerHTML = fallback;
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
subscribe_button.onclick = unsubscribe;
subscribe_button.innerHTML = fallback;
}
}
});
}
xhr.onerror = function () {
console.log('Unsubscribing failed... ' + retries + '/5');
setTimeout(function () { unsubscribe(retries - 1) }, 1000);
}
xhr.ontimeout = function () {
console.log('Unsubscribing failed... ' + retries + '/5');
unsubscribe(retries - 1);
}
xhr.send('csrf_token=' + subscribe_data.csrf_token);
}

@ -1,46 +1,79 @@
'use strict';
var toggle_theme = document.getElementById('toggle_theme');
toggle_theme.href = 'javascript:void(0)';
toggle_theme.href = 'javascript:void(0);';
const STORAGE_KEY_THEME = 'dark_mode';
const THEME_DARK = 'dark';
const THEME_LIGHT = 'light';
// TODO: theme state controlled by system
toggle_theme.addEventListener('click', function () {
const isDarkTheme = helpers.storage.get(STORAGE_KEY_THEME) === THEME_DARK;
const newTheme = isDarkTheme ? THEME_LIGHT : THEME_DARK;
setTheme(newTheme);
helpers.storage.set(STORAGE_KEY_THEME, newTheme);
helpers.xhr('GET', '/toggle_theme?redirect=false', {}, {});
var dark_mode = document.getElementById('dark_theme').media === 'none';
var url = '/toggle_theme?redirect=false';
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
set_mode(dark_mode);
window.localStorage.setItem('dark_mode', dark_mode ? 'dark' : 'light');
xhr.send();
});
/** @param {THEME_DARK|THEME_LIGHT} theme */
function setTheme(theme) {
// By default body element has .no-theme class that uses OS theme via CSS @media rules
// It rewrites using hard className below
if (theme === THEME_DARK) {
toggle_theme.children[0].className = 'icon ion-ios-sunny';
document.body.className = 'dark-theme';
} else if (theme === THEME_LIGHT) {
toggle_theme.children[0].className = 'icon ion-ios-moon';
document.body.className = 'light-theme';
} else {
document.body.className = 'no-theme';
window.addEventListener('storage', function (e) {
if (e.key === 'dark_mode') {
update_mode(e.newValue);
}
}
});
// Handles theme change event caused by other tab
addEventListener('storage', function (e) {
if (e.key === STORAGE_KEY_THEME)
setTheme(helpers.storage.get(STORAGE_KEY_THEME));
window.addEventListener('load', function () {
window.localStorage.setItem('dark_mode', document.getElementById('dark_mode_pref').textContent);
// Update localStorage if dark mode preference changed on preferences page
update_mode(window.localStorage.dark_mode);
});
// Set theme from preferences on page load
addEventListener('DOMContentLoaded', function () {
const prefTheme = document.getElementById('dark_mode_pref').textContent;
if (prefTheme) {
setTheme(prefTheme);
helpers.storage.set(STORAGE_KEY_THEME, prefTheme);
var darkScheme = window.matchMedia('(prefers-color-scheme: dark)');
var lightScheme = window.matchMedia('(prefers-color-scheme: light)');
darkScheme.addListener(scheme_switch);
lightScheme.addListener(scheme_switch);
function scheme_switch (e) {
// ignore this method if we have a preference set
if (localStorage.getItem('dark_mode')) {
return;
}
if (e.matches) {
if (e.media.includes("dark")) {
set_mode(true);
} else if (e.media.includes("light")) {
set_mode(false);
}
});
}
}
function set_mode (bool) {
document.getElementById('dark_theme').media = !bool ? 'none' : '';
document.getElementById('light_theme').media = bool ? 'none' : '';
if (bool) {
toggle_theme.children[0].setAttribute('class', 'icon ion-ios-sunny');
} else {
toggle_theme.children[0].setAttribute('class', 'icon ion-ios-moon');
}
}
function update_mode (mode) {
if (mode === 'true' /* for backwards compatibility */ || mode === 'dark') {
// If preference for dark mode indicated
set_mode(true);
}
else if (mode === 'false' /* for backwards compaibility */ || mode === 'light') {
// If preference for light mode indicated
set_mode(false);
}
else if (document.getElementById('dark_mode_pref').textContent === '' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// If no preference indicated here and no preference indicated on the preferences page (backend), but the browser tells us that the operating system has a dark theme
set_mode(true);
}
// else do nothing, falling back to the mode defined by the `dark_mode` preference on the preferences page (backend)
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,2 @@
/*! @name videojs-contrib-quality-levels @version 2.0.9 @license Apache-2.0 */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js"),require("global/document")):"function"==typeof define&&define.amd?define(["video.js","global/document"],t):e.videojsContribQualityLevels=t(e.videojs,e.document)}(this,function(e,t){"use strict";function n(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}e=e&&e.hasOwnProperty("default")?e.default:e,t=t&&t.hasOwnProperty("default")?t.default:t;var r=function(r){var i,l;function o(){var i,l=n(n(i=r.call(this)||this));if(e.browser.IS_IE8)for(var s in l=t.createElement("custom"),o.prototype)"constructor"!==s&&(l[s]=o.prototype[s]);return l.levels_=[],l.selectedIndex_=-1,Object.defineProperty(l,"selectedIndex",{get:function(){return l.selectedIndex_}}),Object.defineProperty(l,"length",{get:function(){return l.levels_.length}}),l||n(i)}l=r,(i=o).prototype=Object.create(l.prototype),i.prototype.constructor=i,i.__proto__=l;var s=o.prototype;return s.addQualityLevel=function(n){var r=this.getQualityLevelById(n.id);if(r)return r;var i=this.levels_.length;return r=new function n(r){var i=this;if(e.browser.IS_IE8)for(var l in i=t.createElement("custom"),n.prototype)"constructor"!==l&&(i[l]=n.prototype[l]);return i.id=r.id,i.label=i.id,i.width=r.width,i.height=r.height,i.bitrate=r.bandwidth,i.enabled_=r.enabled,Object.defineProperty(i,"enabled",{get:function(){return i.enabled_()},set:function(e){i.enabled_(e)}}),i}(n),""+i in this||Object.defineProperty(this,i,{get:function(){return this.levels_[i]}}),this.levels_.push(r),this.trigger({qualityLevel:r,type:"addqualitylevel"}),r},s.removeQualityLevel=function(e){for(var t=null,n=0,r=this.length;n<r;n++)if(this[n]===e){t=this.levels_.splice(n,1)[0],this.selectedIndex_===n?this.selectedIndex_=-1:this.selectedIndex_>n&&this.selectedIndex_--;break}return t&&this.trigger({qualityLevel:e,type:"removequalitylevel"}),t},s.getQualityLevelById=function(e){for(var t=0,n=this.length;t<n;t++){var r=this[t];if(r.id===e)return r}return null},s.dispose=function(){this.selectedIndex_=-1,this.levels_.length=0},o}(e.EventTarget);for(var i in r.prototype.allowedEvents_={change:"change",addqualitylevel:"addqualitylevel",removequalitylevel:"removequalitylevel"},r.prototype.allowedEvents_)r.prototype["on"+i]=null;var l=function(t){return n=this,e.mergeOptions({},t),i=n.qualityLevels,l=new r,n.on("dispose",function e(){l.dispose(),n.qualityLevels=i,n.off("dispose",e)}),n.qualityLevels=function(){return l},n.qualityLevels.VERSION="2.0.9",l;var n,i,l};return(e.registerPlugin||e.plugin)("qualityLevels",l),l.VERSION="2.0.9",l});

@ -0,0 +1,7 @@
/**
* videojs-http-source-selector
* @version 1.1.6
* @copyright 2019 Justin Fujita <Justin@pivotshare.com>
* @license MIT
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js")):"function"==typeof define&&define.amd?define(["video.js"],t):(e=e||self)["videojs-http-source-selector"]=t(e.videojs)}(this,function(r){"use strict";function o(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t}function s(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var e=(r=r&&r.hasOwnProperty("default")?r.default:r).getComponent("MenuItem"),t=r.getComponent("Component"),a=function(n){function e(e,t){return t.selectable=!0,t.multiSelectable=!1,n.call(this,e,t)||this}o(e,n);var t=e.prototype;return t.handleClick=function(){var e=this.options_;console.log("Changing quality to:",e.label),n.prototype.handleClick.call(this);for(var t=this.player().qualityLevels(),o=0;o<t.length;o++)e.index==t.length?t[o].enabled=!0:e.index==o?t[o].enabled=!0:t[o].enabled=!1},t.update=function(){var e=this.player().qualityLevels().selectedIndex;this.selected(this.options_.index==e)},e}(e);t.registerComponent("SourceMenuItem",a);var u=r.getComponent("MenuButton"),n=function(i){function e(e,t){var o;o=i.call(this,e,t)||this,u.apply(s(o),arguments);var n=o.player().qualityLevels();if(t&&t.default)if("low"==t.default)for(var l=0;l<n.length;l++)n[l].enabled=0==l;else if(t.default="high")for(l=0;l<n.length;l++)n[l].enabled=l==n.length-1;return o.player().qualityLevels().on(["change","addqualitylevel"],r.bind(s(o),o.update)),o}o(e,i);var t=e.prototype;return t.createEl=function(){return r.dom.createEl("div",{className:"vjs-http-source-selector vjs-menu-button vjs-menu-button-popup vjs-control vjs-button"})},t.buildCSSClass=function(){return u.prototype.buildCSSClass.call(this)+" vjs-icon-cog"},t.update=function(){return u.prototype.update.call(this)},t.createItems=function(){for(var e=[],t=this.player().qualityLevels(),o=[],n=0;n<t.length;n++){var l=t.length-(n+1),i=l===t.selectedIndex,r=""+l,s=l;t[l].height?(r=t[l].height+"p",s=parseInt(t[l].height,10)):t[l].bitrate&&(r=Math.floor(t[l].bitrate/1e3)+" kbps",s=parseInt(t[l].bitrate,10)),0<=o.indexOf(r)||(o.push(r),e.push(new a(this.player_,{label:r,index:l,selected:i,sortVal:s})))}return 1<t.length&&e.push(new a(this.player_,{label:"Auto",index:t.length,selected:!1,sortVal:99999})),e.sort(function(e,t){return e.options_.sortVal<t.options_.sortVal?1:e.options_.sortVal>t.options_.sortVal?-1:0}),e},e}(u),l={},i=r.registerPlugin||r.plugin,c=function(e){var t=this;this.ready(function(){!function(n,e){if(n.addClass("vjs-http-source-selector"),console.log("videojs-http-source-selector initialized!"),console.log("player.techName_:"+n.techName_),"Html5"!=n.techName_)return;n.on(["loadedmetadata"],function(e){if(n.qualityLevels(),r.log("loadmetadata event"),"undefined"==n.videojs_http_source_selector_initialized||1==n.videojs_http_source_selector_initialized)console.log("player.videojs_http_source_selector_initialized == true");else{console.log("player.videojs_http_source_selector_initialized == false"),n.videojs_http_source_selector_initialized=!0;var t=n.controlBar,o=t.getChild("fullscreenToggle").el();t.el().insertBefore(t.addChild("SourceMenuButton").el(),o)}})}(t,r.mergeOptions(l,e))}),r.registerComponent("SourceMenuButton",n),r.registerComponent("SourceMenuItem",a)};return i("httpSourceSelector",c),c.VERSION="1.1.6",c});

File diff suppressed because one or more lines are too long

@ -0,0 +1,2 @@
/*! @name videojs-overlay @version 2.1.4 @license Apache-2.0 */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("video.js"),require("global/window")):"function"==typeof define&&define.amd?define(["video.js","global/window"],e):t.videojsOverlay=e(t.videojs,t.window)}(this,function(t,e){"use strict";function n(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}t=t&&t.hasOwnProperty("default")?t.default:t,e=e&&e.hasOwnProperty("default")?e.default:e;var r={align:"top-left",class:"",content:"This overlay will show up while the video is playing",debug:!1,showBackground:!0,attachToControlBar:!1,overlays:[{start:"playing",end:"paused"}]},i=t.getComponent("Component"),o=t.dom||t,s=t.registerPlugin||t.plugin,a=function(t){return"number"==typeof t&&t==t},h=function(t){return"string"==typeof t&&/^\S+$/.test(t)},d=function(r){var i,s;function d(t,e){var i;return i=r.call(this,t,e)||this,["start","end"].forEach(function(t){var e=i.options_[t];if(a(e))i[t+"Event_"]="timeupdate";else if(h(e))i[t+"Event_"]=e;else if("start"===t)throw new Error('invalid "start" option; expected number or string')}),["endListener_","rewindListener_","startListener_"].forEach(function(t){i[t]=function(e){return d.prototype[t].call(n(n(i)),e)}}),"timeupdate"===i.startEvent_&&i.on(t,"timeupdate",i.rewindListener_),i.debug('created, listening to "'+i.startEvent_+'" for "start" and "'+(i.endEvent_||"nothing")+'" for "end"'),i.hide(),i}s=r,(i=d).prototype=Object.create(s.prototype),i.prototype.constructor=i,i.__proto__=s;var l=d.prototype;return l.createEl=function(){var t=this.options_,n=t.content,r=t.showBackground?"vjs-overlay-background":"vjs-overlay-no-background",i=o.createEl("div",{className:"\n vjs-overlay\n vjs-overlay-"+t.align+"\n "+t.class+"\n "+r+"\n vjs-hidden\n "});return"string"==typeof n?i.innerHTML=n:n instanceof e.DocumentFragment?i.appendChild(n):o.appendContent(i,n),i},l.debug=function(){if(this.options_.debug){for(var e=t.log,n=e,r=arguments.length,i=new Array(r),o=0;o<r;o++)i[o]=arguments[o];e.hasOwnProperty(i[0])&&"function"==typeof e[i[0]]&&(n=e[i.shift()]),n.apply(void 0,["overlay#"+this.id()+": "].concat(i))}},l.hide=function(){return r.prototype.hide.call(this),this.debug("hidden"),this.debug('bound `startListener_` to "'+this.startEvent_+'"'),this.endEvent_&&(this.debug('unbound `endListener_` from "'+this.endEvent_+'"'),this.off(this.player(),this.endEvent_,this.endListener_)),this.on(this.player(),this.startEvent_,this.startListener_),this},l.shouldHide_=function(t,e){var n=this.options_.end;return a(n)?t>=n:n===e},l.show=function(){return r.prototype.show.call(this),this.off(this.player(),this.startEvent_,this.startListener_),this.debug("shown"),this.debug('unbound `startListener_` from "'+this.startEvent_+'"'),this.endEvent_&&(this.debug('bound `endListener_` to "'+this.endEvent_+'"'),this.on(this.player(),this.endEvent_,this.endListener_)),this},l.shouldShow_=function(t,e){var n=this.options_.start,r=this.options_.end;return a(n)?a(r)?t>=n&&t<r:this.hasShownSinceSeek_?Math.floor(t)===n:(this.hasShownSinceSeek_=!0,t>=n):n===e},l.startListener_=function(t){var e=this.player().currentTime();this.shouldShow_(e,t.type)&&this.show()},l.endListener_=function(t){var e=this.player().currentTime();this.shouldHide_(e,t.type)&&this.hide()},l.rewindListener_=function(t){var e=this.player().currentTime(),n=this.previousTime_,r=this.options_.start,i=this.options_.end;e<n&&(this.debug("rewind detected"),a(i)&&!this.shouldShow_(e)?(this.debug("hiding; "+i+" is an integer and overlay should not show at this time"),this.hasShownSinceSeek_=!1,this.hide()):h(i)&&e<r&&(this.debug("hiding; show point ("+r+") is before now ("+e+") and end point ("+i+") is an event"),this.hasShownSinceSeek_=!1,this.hide())),this.previousTime_=e},d}(i);t.registerComponent("Overlay",d);var l=function(e){var n=this,i=t.mergeOptions(r,e);Array.isArray(this.overlays_)&&this.overlays_.forEach(function(t){n.removeChild(t),n.controlBar&&n.controlBar.removeChild(t),t.dispose()});var o=i.overlays;delete i.overlays,this.overlays_=o.map(function(e){var r=t.mergeOptions(i,e),o="string"==typeof r.attachToControlBar||!0===r.attachToControlBar;if(!n.controls()||!n.controlBar)return n.addChild("overlay",r);if(o&&-1!==r.align.indexOf("bottom")){var s=n.controlBar.children()[0];if(void 0!==n.controlBar.getChild(r.attachToControlBar)&&(s=n.controlBar.getChild(r.attachToControlBar)),s){var a=n.controlBar.addChild("overlay",r);return n.controlBar.el().insertBefore(a.el(),s.el()),a}}var h=n.addChild("overlay",r);return n.el().insertBefore(h.el(),n.controlBar.el()),h})};return l.VERSION="2.1.4",s("overlay",l),l});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,35 +1,32 @@
'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>';
var video_data = JSON.parse(document.getElementById('video_data').innerHTML);
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];
if (body.style.display === 'none') {
target.textContent = '[ ]';
body.style.display = '';
} else {
target.textContent = '[ + ]';
body = target.parentNode.parentNode.children[1];
if (body.style.display === null || body.style.display === '') {
target.innerHTML = '[ + ]';
body.style.display = 'none';
} else {
target.innerHTML = '[ - ]';
body.style.display = '';
}
}
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 = target.parentNode.parentNode.parentNode.children[1];
if (body.style.display === null || body.style.display === '') {
target.innerHTML = '[ + ]';
body.style.display = 'none';
} else {
target.innerHTML = '[ - ]';
body.style.display = '';
}
}
@ -46,13 +43,13 @@ 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');
sub_text = target.getAttribute('data-inner-text');
inner_text = target.getAttribute('data-sub-text');
var body = target.parentNode.parentNode.children[1];
body = target.parentNode.parentNode.children[1];
body.style.display = 'none';
target.textContent = sub_text;
target.innerHTML = sub_text;
target.onclick = show_youtube_replies;
target.setAttribute('data-inner-text', inner_text);
target.setAttribute('data-sub-text', sub_text);
@ -61,13 +58,13 @@ function hide_youtube_replies(event) {
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');
sub_text = target.getAttribute('data-inner-text');
inner_text = target.getAttribute('data-sub-text');
var body = target.parentNode.parentNode.children[1];
body = target.parentNode.parentNode.children[1];
body.style.display = '';
target.textContent = sub_text;
target.innerHTML = sub_text;
target.onclick = hide_youtube_replies;
target.setAttribute('data-inner-text', inner_text);
target.setAttribute('data-sub-text', sub_text);
@ -81,259 +78,373 @@ if (continue_button) {
function next_video() {
var url = new URL('https://example.com/watch?v=' + video_data.next_video);
if (video_data.params.autoplay || video_data.params.continue_autoplay)
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
url.searchParams.set('autoplay', '1');
if (video_data.params.listen !== video_data.preferences.listen)
}
if (video_data.params.listen !== video_data.preferences.listen) {
url.searchParams.set('listen', video_data.params.listen);
if (video_data.params.speed !== video_data.preferences.speed)
}
if (video_data.params.speed !== video_data.preferences.speed) {
url.searchParams.set('speed', video_data.params.speed);
if (video_data.params.local !== video_data.preferences.local)
}
if (video_data.params.local !== video_data.preferences.local) {
url.searchParams.set('local', video_data.params.local);
url.searchParams.set('continue', '1');
}
url.searchParams.set('continue', '1');
location.assign(url.pathname + url.search);
}
function continue_autoplay(event) {
if (event.target.checked) {
player.on('ended', next_video);
player.on('ended', function () {
next_video();
});
} else {
player.off('ended');
}
}
function get_playlist(plid) {
var playlist = document.getElementById('playlist');
function number_with_separator(val) {
while (/(\d+)(\d{3})/.test(val.toString())) {
val = val.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
}
return val;
}
function get_playlist(plid, retries) {
if (retries == undefined) retries = 5;
playlist = document.getElementById('playlist');
playlist.innerHTML = spinnerHTMLwithHR;
if (retries <= 0) {
console.log('Failed to pull playlist');
playlist.innerHTML = '';
return;
}
playlist.innerHTML = ' \
<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3> \
<hr>'
var plid_url;
if (plid.startsWith('RD')) {
plid_url = '/api/v1/mixes/' + plid +
var plid_url = '/api/v1/mixes/' + plid +
'?continuation=' + video_data.id +
'&format=html&hl=' + video_data.preferences.locale;
} else {
plid_url = '/api/v1/playlists/' + plid +
var plid_url = '/api/v1/playlists/' + plid +
'?index=' + video_data.index +
'&continuation=' + video_data.id +
'&format=html&hl=' + video_data.preferences.locale;
}
helpers.xhr('GET', plid_url, {retries: 5, entity_name: 'playlist'}, {
on200: function (response) {
playlist.innerHTML = response.playlistHtml;
if (!response.nextVideo) return;
var nextVideo = document.getElementById(response.nextVideo);
nextVideo.parentNode.parentNode.scrollTop = nextVideo.offsetTop;
player.on('ended', function () {
var url = new URL('https://example.com/watch?v=' + response.nextVideo);
url.searchParams.set('list', plid);
if (!plid.startsWith('RD'))
url.searchParams.set('index', response.index);
if (video_data.params.autoplay || video_data.params.continue_autoplay)
url.searchParams.set('autoplay', '1');
if (video_data.params.listen !== video_data.preferences.listen)
url.searchParams.set('listen', video_data.params.listen);
if (video_data.params.speed !== video_data.preferences.speed)
url.searchParams.set('speed', video_data.params.speed);
if (video_data.params.local !== video_data.preferences.local)
url.searchParams.set('local', video_data.params.local);
location.assign(url.pathname + url.search);
});
},
onNon200: function (xhr) {
playlist.innerHTML = '';
document.getElementById('continue').style.display = '';
},
onError: function (xhr) {
playlist.innerHTML = spinnerHTMLwithHR;
},
onTimeout: function (xhr) {
playlist.innerHTML = spinnerHTMLwithHR;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', plid_url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
playlist.innerHTML = xhr.response.playlistHtml;
if (xhr.response.nextVideo) {
player.on('ended', function () {
var url = new URL('https://example.com/watch?v=' + xhr.response.nextVideo);
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
url.searchParams.set('autoplay', '1');
}
if (video_data.params.listen !== video_data.preferences.listen) {
url.searchParams.set('listen', video_data.params.listen);
}
if (video_data.params.speed !== video_data.preferences.speed) {
url.searchParams.set('speed', video_data.params.speed);
}
if (video_data.params.local !== video_data.preferences.local) {
url.searchParams.set('local', video_data.params.local);
}
url.searchParams.set('list', plid);
if (!plid.startsWith('RD')) {
url.searchParams.set('index', xhr.response.index);
}
location.assign(url.pathname + url.search);
});
}
} else {
playlist.innerHTML = '';
document.getElementById('continue').style.display = '';
}
}
});
}
xhr.onerror = function () {
playlist = document.getElementById('playlist');
playlist.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3><hr>';
console.log('Pulling playlist timed out... ' + retries + '/5');
setTimeout(function () { get_playlist(plid, retries - 1) }, 1000);
}
xhr.ontimeout = function () {
playlist = document.getElementById('playlist');
playlist.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3><hr>';
console.log('Pulling playlist timed out... ' + retries + '/5');
get_playlist(plid, retries - 1);
}
xhr.send();
}
function get_reddit_comments() {
var comments = document.getElementById('comments');
function get_reddit_comments(retries) {
if (retries == undefined) retries = 5;
comments = document.getElementById('comments');
if (retries <= 0) {
console.log('Failed to pull comments');
comments.innerHTML = '';
return;
}
var fallback = comments.innerHTML;
comments.innerHTML = spinnerHTML;
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
var url = '/api/v1/comments/' + video_data.id +
'?source=reddit&format=html' +
'&hl=' + video_data.preferences.locale;
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: ''}, {
on200: function (response) {
comments.innerHTML = ' \
<div> \
<h3> \
<a href="javascript:void(0)">[ ]</a> \
{title} \
</h3> \
<p> \
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
comments.innerHTML = ' \
<div> \
<h3> \
<a href="javascript:void(0)">[ - ]</a> \
{title} \
</h3> \
<p> \
<b> \
<a href="javascript:void(0)" data-comments="youtube"> \
{youtubeCommentsText} \
</a> \
</b> \
</p> \
<b> \
<a href="javascript:void(0)" data-comments="youtube"> \
{youtubeCommentsText} \
</a> \
<a rel="noopener" target="_blank" href="https://reddit.com{permalink}">{redditPermalinkText}</a> \
</b> \
</p> \
<b> \
<a rel="noopener" target="_blank" href="https://reddit.com{permalink}">{redditPermalinkText}</a> \
</b> \
</div> \
<div>{contentHtml}</div> \
<hr>'.supplant({
title: response.title,
youtubeCommentsText: video_data.youtube_comments_text,
redditPermalinkText: video_data.reddit_permalink_text,
permalink: response.permalink,
contentHtml: response.contentHtml
});
comments.children[0].children[0].children[0].onclick = toggle_comments;
comments.children[0].children[1].children[0].onclick = swap_comments;
},
onNon200: onNon200, // declared above
});
</div> \
<div>{contentHtml}</div> \
<hr>'.supplant({
title: xhr.response.title,
youtubeCommentsText: video_data.youtube_comments_text,
redditPermalinkText: video_data.reddit_permalink_text,
permalink: xhr.response.permalink,
contentHtml: xhr.response.contentHtml
});
comments.children[0].children[0].children[0].onclick = toggle_comments;
comments.children[0].children[1].children[0].onclick = swap_comments;
} else {
if (video_data.params.comments[1] === 'youtube') {
console.log('Pulling comments failed... ' + retries + '/5');
setTimeout(function () { get_youtube_comments(retries - 1) }, 1000);
} else {
comments.innerHTML = fallback;
}
}
}
}
xhr.onerror = function () {
console.log('Pulling comments failed... ' + retries + '/5');
setInterval(function () { get_reddit_comments(retries - 1) }, 1000);
}
xhr.ontimeout = function () {
console.log('Pulling comments failed... ' + retries + '/5');
get_reddit_comments(retries - 1);
}
xhr.send();
}
function get_youtube_comments() {
var comments = document.getElementById('comments');
function get_youtube_comments(retries) {
if (retries == undefined) retries = 5;
comments = document.getElementById('comments');
if (retries <= 0) {
console.log('Failed to pull comments');
comments.innerHTML = '';
return;
}
var fallback = comments.innerHTML;
comments.innerHTML = spinnerHTML;
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
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;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
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: xhr.response.contentHtml,
redditComments: video_data.reddit_comments_text,
commentsText: video_data.comments_text.supplant(
{ commentCount: number_with_separator(xhr.response.commentCount) }
)
});
comments.children[0].children[0].children[0].onclick = toggle_comments;
comments.children[0].children[1].children[0].onclick = swap_comments;
} else {
if (video_data.params.comments[1] === 'youtube') {
setTimeout(function () { get_youtube_comments(retries - 1) }, 1000);
} else {
comments.innerHTML = '';
}
}
}
});
}
xhr.onerror = function () {
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
console.log('Pulling comments failed... ' + retries + '/5');
setInterval(function () { get_youtube_comments(retries - 1) }, 1000);
}
xhr.ontimeout = function () {
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
console.log('Pulling comments failed... ' + retries + '/5');
get_youtube_comments(retries - 1);
}
xhr.send();
}
function get_youtube_replies(target, load_more, load_replies) {
function get_youtube_replies(target, load_more) {
var continuation = target.getAttribute('data-continuation');
var body = target.parentNode.parentNode;
var fallback = body.innerHTML;
body.innerHTML = spinnerHTML;
body.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
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);
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
if (load_more) {
body = body.parentNode.parentNode;
body.removeChild(body.lastElementChild);
body.innerHTML += xhr.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.innerText = video_data.hide_replies_text;
var div = document.createElement('div');
div.innerHTML = xhr.response.contentHtml;
body.appendChild(p);
body.appendChild(div);
}
} 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);
body.innerHTML = fallback;
}
},
onNon200: function (xhr) {
body.innerHTML = fallback;
},
onTimeout: function (xhr) {
console.warn('Pulling comments failed');
body.innerHTML = fallback;
}
});
}
xhr.ontimeout = function () {
console.log('Pulling comments failed.');
body.innerHTML = fallback;
}
xhr.send();
}
if (video_data.play_next) {
player.on('ended', function () {
var url = new URL('https://example.com/watch?v=' + video_data.next_video);
if (video_data.params.autoplay || video_data.params.continue_autoplay)
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
url.searchParams.set('autoplay', '1');
if (video_data.params.listen !== video_data.preferences.listen)
}
if (video_data.params.listen !== video_data.preferences.listen) {
url.searchParams.set('listen', video_data.params.listen);
if (video_data.params.speed !== video_data.preferences.speed)
}
if (video_data.params.speed !== video_data.preferences.speed) {
url.searchParams.set('speed', video_data.params.speed);
if (video_data.params.local !== video_data.preferences.local)
}
if (video_data.params.local !== video_data.preferences.local) {
url.searchParams.set('local', video_data.params.local);
url.searchParams.set('continue', '1');
}
url.searchParams.set('continue', '1');
location.assign(url.pathname + url.search);
});
}
addEventListener('load', function (e) {
if (video_data.plid)
window.addEventListener('load', function (e) {
if (video_data.plid) {
get_playlist(video_data.plid);
}
if (video_data.params.comments[0] === 'youtube') {
get_youtube_comments();
@ -344,7 +455,7 @@ addEventListener('load', function (e) {
} else if (video_data.params.comments[1] === 'reddit') {
get_reddit_comments();
} else {
var comments = document.getElementById('comments');
comments = document.getElementById('comments');
comments.innerHTML = '';
}
});

@ -1,24 +0,0 @@
'use strict';
var save_player_pos_key = 'save_player_pos';
function get_all_video_times() {
return helpers.storage.get(save_player_pos_key) || {};
}
document.querySelectorAll('.watched-indicator').forEach(function (indicator) {
var watched_part = get_all_video_times()[indicator.dataset.id];
var total = parseInt(indicator.dataset.length, 10);
if (watched_part === undefined) {
watched_part = total;
}
var percentage = Math.round((watched_part / total) * 100);
if (percentage < 5) {
percentage = 5;
}
if (percentage > 90) {
percentage = 100;
}
indicator.style.width = percentage + '%';
});

@ -1,6 +1,4 @@
'use strict';
var watched_data = JSON.parse(document.getElementById('watched_data').textContent);
var payload = 'csrf_token=' + watched_data.csrf_token;
var watched_data = JSON.parse(document.getElementById('watched_data').innerHTML);
function mark_watched(target) {
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
@ -8,27 +6,45 @@ function mark_watched(target) {
var url = '/watch_ajax?action_mark_watched=1&redirect=false' +
'&id=' + target.getAttribute('data-id');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
tile.style.display = '';
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
tile.style.display = '';
}
}
});
}
xhr.send('csrf_token=' + watched_data.csrf_token);
}
function mark_unwatched(target) {
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
tile.style.display = 'none';
var count = document.getElementById('count');
count.textContent--;
var count = document.getElementById('count')
count.innerText = count.innerText - 1;
var url = '/watch_ajax?action_mark_unwatched=1&redirect=false' +
'&id=' + target.getAttribute('data-id');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
count.textContent++;
tile.style.display = '';
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status != 200) {
count.innerText = count.innerText - 1 + 2;
tile.style.display = '';
}
}
});
}
}
xhr.send('csrf_token=' + watched_data.csrf_token);
}

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

@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

@ -1,926 +0,0 @@
#########################################
#
# Database configuration
#
#########################################
##
## Database configuration with separate parameters.
## This setting is MANDATORY, unless 'database_url' is used.
##
db:
user: kemal
password: kemal
host: localhost
port: 5432
dbname: invidious
##
## Database configuration using a single URI. This is an
## alternative to the 'db' parameter above. If both forms
## are used, then only database_url is used.
## This setting is MANDATORY, unless 'db' is used.
##
## Note: The 'database_url' setting allows the use of UNIX
## sockets. To do so, remove the IP address (or FQDN) and port
## and append the 'host' parameter. E.g:
## postgres://kemal:kemal@/invidious?host=/var/run/postgresql
##
## Accepted values: a postgres:// URI
## Default: postgres://kemal:kemal@localhost:5432/invidious
##
#database_url: postgres://kemal:kemal@localhost:5432/invidious
##
## Enable automatic table integrity check. This will create
## the required tables and columns if anything is missing.
##
## Accepted values: true, false
## Default: false
##
#check_tables: false
#########################################
#
# Server config
#
#########################################
# -----------------------------
# Network (inbound)
# -----------------------------
##
## Port to listen on for incoming connections.
##
## Note: Ports lower than 1024 requires either root privileges
## (not recommended) or the "CAP_NET_BIND_SERVICE" capability
## (See https://stackoverflow.com/a/414258 and `man capabilities`)
##
## Accepted values: 1-65535
## Default: 3000
##
#port: 3000
##
## When the invidious instance is behind a proxy, and the proxy
## listens on a different port than the instance does, this lets
## invidious know about it. This is used to craft absolute URLs
## to the instance (e.g in the API).
##
## Note: This setting is MANDATORY if invidious is behind a
## reverse proxy.
##
## Accepted values: 1-65535
## Default: <none>
##
#external_port:
##
## Interface address to listen on for incoming connections.
##
## Accepted values: a valid IPv4 or IPv6 address.
## default: 0.0.0.0 (listen on all interfaces)
##
#host_binding: 0.0.0.0
##
## Domain name under which this instance is hosted. This is
## used to craft absolute URLs to the instance (e.g in the API).
## The domain MUST be defined if your instance is accessed from
## a domain name (like 'example.com').
##
## Accepted values: a fully qualified domain name (FQDN)
## Default: <none>
##
domain:
##
## Tell Invidious that it is behind a proxy that provides only
## HTTPS, so all links must use the https:// scheme. This
## setting MUST be set to true if invidious is behind a
## reverse proxy serving HTTPs.
##
## Accepted values: true, false
## Default: false
##
https_only: false
##
## Enable/Disable 'Strict-Transport-Security'. Make sure that
## the domain specified under 'domain' is served securely.
##
## Accepted values: true, false
## Default: true
##
#hsts: true
# -----------------------------
# Network (outbound)
# -----------------------------
##
## Disable proxying server-wide. Can be disable as a whole, or
## only for a single function.
##
## Accepted values: true, false, dash, livestreams, downloads, local
## Default: false
##
#disable_proxy: false
##
## Size of the HTTP pool used to connect to youtube. Each
## domain ('youtube.com', 'ytimg.com', ...) has its own pool.
##
## Accepted values: a positive integer
## Default: 100
##
#pool_size: 100
##
## Enable/Disable the use of QUIC (HTTP/3) when connecting
## to the youtube API and websites ('youtube.com', 'ytimg.com').
## QUIC's main advantages are its lower latency and lower bandwidth
## use, compared to its predecessors. However, the current version
## of QUIC used in invidious is still based on the IETF draft 31,
## meaning that the underlying library may still not be fully
## optimized. You can read more about QUIC at the link below:
## https://datatracker.ietf.org/doc/html/draft-ietf-quic-transport-31
##
## Note: you should try both options and see what is the best for your
## instance. In general QUIC is recommended for public instances. Your
## mileage may vary.
##
## Note 2: Using QUIC prevents some captcha challenges from appearing.
## See: https://github.com/iv-org/invidious/issues/957#issuecomment-576424042
##
## Accepted values: true, false
## Default: false
##
#use_quic: false
##
## Additional cookies to be sent when requesting the youtube API.
##
## Accepted values: a string in the format "name1=value1; name2=value2..."
## Default: <none>
##
#cookies:
##
## Force connection to youtube over a specific IP family.
##
## Note: This may sometimes resolve issues involving rate-limiting.
## See https://github.com/ytdl-org/youtube-dl/issues/21729.
##
## Accepted values: ipv4, ipv6
## Default: <none>
##
#force_resolve:
# -----------------------------
# Logging
# -----------------------------
##
## Path to log file. Can be absolute or relative to the invidious
## binary. This is overridden if "-o OUTPUT" or "--output=OUTPUT"
## are passed on the command line.
##
## Accepted values: a filesystem path or 'STDOUT'
## Default: STDOUT
##
#output: STDOUT
##
## Logging Verbosity. This is overridden if "-l LEVEL" or
## "--log-level=LEVEL" are passed on the command line.
##
## Accepted values: All, Trace, Debug, Info, Warn, Error, Fatal, Off
## Default: Info
##
#log_level: Info
# -----------------------------
# Features
# -----------------------------
##
## Enable/Disable the "Popular" tab on the main page.
##
## Accepted values: true, false
## Default: true
##
#popular_enabled: true
##
## Enable/Disable statstics (available at /api/v1/stats).
## The following data is available:
## - Software name ("invidious") and version+branch (same data as
## displayed in the footer, e.g: "2021.05.13-75e5b49" / "master")
## - The value of the 'registration_enabled' config (true/false)
## - Number of currently registered users
## - Number of registered users who connected in the last month
## - Number of registered users who connected in the last 6 months
## - Timestamp of the last server restart
## - Timestamp of the last "Channel Refresh" job execution
##
## Warning: This setting MUST be set to true if you plan to run
## a public instance. It is used by api.invidious.io to refresh
## your instance's status.
##
## Accepted values: true, false
## Default: false
##
#statistics_enabled: false
# -----------------------------
# Users and accounts
# -----------------------------
##
## Allow/Forbid Invidious (local) account creation. Invidious
## accounts allow users to subscribe to channels and to create
## playlists without a Google account.
##
## Accepted values: true, false
## Default: true
##
#registration_enabled: true
##
## Allow/Forbid users to log-in.
##
## Accepted values: true, false
## Default: true
##
#login_enabled: true
##
## Enable/Disable the captcha challenge on the login page.
##
## Note: this is a basic captcha challenge that doesn't
## depend on any third parties.
##
## Accepted values: true, false
## Default: true
##
#captcha_enabled: true
##
## List of usernames that will be granted administrator rights.
## A user with administrator rights will be able to change the
## server configuration options listed below in /preferences,
## in addition to the usual user preferences.
##
## Server-wide settings:
## - popular_enabled
## - captcha_enabled
## - login_enabled
## - registration_enabled
## - statistics_enabled
## Default user preferences:
## - default_home
## - feed_menu
##
## Accepted values: an array of strings
## Default: [""]
##
#admins: [""]
##
## Enable/Disable the user notifications for all users
##
## Note: On large instances, it is recommended to set this option to 'false'
## in order to reduce the amount of data written to the database, and hence
## improve the overall performance of the instance.
##
## Accepted values: true, false
## Default: true
##
#enable_user_notifications: true
# -----------------------------
# Background jobs
# -----------------------------
##
## Number of threads to use when crawling channel videos (during
## subscriptions update).
##
## Notes: This setting is overridden if either "-c THREADS" or
## "--channel-threads=THREADS" is passed on the command line.
##
## Accepted values: a positive integer
## Default: 1
##
channel_threads: 1
##
## Time interval between two executions of the job that crawls
## channel videos (subscriptions update).
##
## Accepted values: a valid time interval (like 1h30m or 90m)
## Default: 30m
##
#channel_refresh_interval: 30m
##
## Forcefully dump and re-download the entire list of uploaded
## videos when crawling channel (during subscriptions update).
##
## Accepted values: true, false
## Default: false
##
full_refresh: false
##
## Number of threads to use when updating RSS feeds.
##
## Notes: This setting is overridden if either "-f THREADS" or
## "--feed-threads=THREADS" is passed on the command line.
##
## Accepted values: a positive integer
## Default: 1
##
feed_threads: 1
##
## Enable/Disable the polling job that keeps the decryption
## function (for "secured" videos) up to date.
##
## Note: This part of the code generate a small amount of data every minute.
## This may not be desired if you have bandwidth limits set by your ISP.
##
## Note 2: This part of the code is currently broken, so changing
## this setting has no impact.
##
## Accepted values: true, false
## Default: false
##
#decrypt_polling: false
jobs:
## Options for the database cleaning job
clear_expired_items:
## Enable/Disable job
##
## Accepted values: true, false
## Default: true
##
enable: true
## Options for the channels updater job
refresh_channels:
## Enable/Disable job
##
## Accepted values: true, false
## Default: true
##
enable: true
## Options for the RSS feeds updater job
refresh_feeds:
## Enable/Disable job
##
## Accepted values: true, false
## Default: true
##
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
# -----------------------------
##
## custom banner displayed at the top of every page. This can
## used for instance announcements, e.g.
##
## Accepted values: any string. HTML is accepted.
## Default: <none>
##
#banner:
##
## Subscribe to channels using PubSubHub (Google PubSubHubbub service).
## PubSubHub allows Invidious to be instantly notified when a new video
## is published on any subscribed channels. When PubSubHub is not used,
## Invidious will check for new videos every minute.
##
## Note: This setting is recommended for public instances.
##
## Note 2:
## - Requires a public instance (it uses /feed/webhook/v1)
## - Requires 'domain' and 'hmac_key' to be set.
## - Setting this parameter to any number greater than zero will
## enable channel subscriptions via PubSubHub, but will limit the
## amount of concurrent subscriptions.
##
## Accepted values: true, false, a positive integer
## Default: false
##
#use_pubsub_feeds: false
##
## HMAC signing key used for CSRF tokens and pubsub
## subscriptions verification.
##
## Accepted values: a string
## Default: <none>
##
#hmac_key:
##
## List of video IDs where the "download" widget must be
## disabled, in order to comply with DMCA requests.
##
## Accepted values: an array of string
## Default: <none>
##
#dmca_content:
##
## Cache video annotations in the database.
##
## Warning: empty annotations or annotations that only contain
## cards won't be cached.
##
## Accepted values: true, false
## Default: false
##
#cache_annotations: false
##
## Source code URL. If your instance is running a modified source
## code, you MUST publish it somewhere and set this option.
##
## Accepted values: a string
## Default: <none>
##
#modified_source_code_url: ""
##
## Maximum custom playlist length limit.
##
## Accepted values: Integer
## Default: 500
##
#playlist_length_limit: 500
#########################################
#
# Default user preferences
#
#########################################
##
## NOTE: All the settings below define the default user
## preferences. They will apply to ALL users connecting
## without a preferences cookie (so either on the first
## connection to the instance or after clearing the
## browser's cookies).
##
default_user_preferences:
# -----------------------------
# Internationalization
# -----------------------------
##
## Default user interface language (locale).
##
## Note: When hosting a public instance, overriding the
## default (english) is not recommended, as it may
## people using other languages.
##
## Accepted values:
## ar (Arabic)
## da (Danish)
## de (German)
## en-US (english, US)
## el (Greek)
## eo (Esperanto)
## es (Spanish)
## fa (Persian)
## fi (Finnish)
## fr (French)
## he (Hebrew)
## hr (Hungarian)
## id (Indonesian)
## is (Icelandic)
## it (Italian)
## ja (Japanese)
## nb-NO (Norwegian, Bokmål)
## nl (Dutch)
## pl (Polish)
## pt-BR (Portuguese, Brazil)
## pt-PT (Portuguese, Portugal)
## ro (Romanian)
## ru (Russian)
## sv (Swedish)
## tr (Turkish)
## uk (Ukrainian)
## zh-CN (Chinese, China) (a.k.a "Simplified Chinese")
## zh-TW (Chinese, Taiwan) (a.k.a "Traditional Chinese")
##
## Default: en-US
##
#locale: en-US
##
## Default geographical location for content.
##
## Accepted values:
## AE, AR, AT, AU, AZ, BA, BD, BE, BG, BH, BO, BR, BY, CA, CH, CL, CO, CR,
## CY, CZ, DE, DK, DO, DZ, EC, EE, EG, ES, FI, FR, GB, GE, GH, GR, GT, HK,
## HN, HR, HU, ID, IE, IL, IN, IQ, IS, IT, JM, JO, JP, KE, KR, KW, KZ, LB,
## LI, LK, LT, LU, LV, LY, MA, ME, MK, MT, MX, MY, NG, NI, NL, NO, NP, NZ,
## OM, PA, PE, PG, PH, PK, PL, PR, PT, PY, QA, RO, RS, RU, SA, SE, SG, SI,
## SK, SN, SV, TH, TN, TR, TW, TZ, UA, UG, US, UY, VE, VN, YE, ZA, ZW
##
## Default: US
##
#region: US
##
## Top 3 preferred languages for video captions.
##
## Note: overriding the default (no preferred
## caption language) is not recommended, in order
## to not penalize people using other languages.
##
## Accepted values: a three-entries array.
## Each entry can be one of:
## "English", "English (auto-generated)",
## "Afrikaans", "Albanian", "Amharic", "Arabic",
## "Armenian", "Azerbaijani", "Bangla", "Basque",
## "Belarusian", "Bosnian", "Bulgarian", "Burmese",
## "Catalan", "Cebuano", "Chinese (Simplified)",
## "Chinese (Traditional)", "Corsican", "Croatian",
## "Czech", "Danish", "Dutch", "Esperanto", "Estonian",
## "Filipino", "Finnish", "French", "Galician", "Georgian",
## "German", "Greek", "Gujarati", "Haitian Creole", "Hausa",
## "Hawaiian", "Hebrew", "Hindi", "Hmong", "Hungarian",
## "Icelandic", "Igbo", "Indonesian", "Irish", "Italian",
## "Japanese", "Javanese", "Kannada", "Kazakh", "Khmer",
## "Korean", "Kurdish", "Kyrgyz", "Lao", "Latin", "Latvian",
## "Lithuanian", "Luxembourgish", "Macedonian",
## "Malagasy", "Malay", "Malayalam", "Maltese", "Maori",
## "Marathi", "Mongolian", "Nepali", "Norwegian Bokmål",
## "Nyanja", "Pashto", "Persian", "Polish", "Portuguese",
## "Punjabi", "Romanian", "Russian", "Samoan",
## "Scottish Gaelic", "Serbian", "Shona", "Sindhi",
## "Sinhala", "Slovak", "Slovenian", "Somali",
## "Southern Sotho", "Spanish", "Spanish (Latin America)",
## "Sundanese", "Swahili", "Swedish", "Tajik", "Tamil",
## "Telugu", "Thai", "Turkish", "Ukrainian", "Urdu",
## "Uzbek", "Vietnamese", "Welsh", "Western Frisian",
## "Xhosa", "Yiddish", "Yoruba", "Zulu"
##
## Default: ["", "", ""]
##
#captions: ["", "", ""]
# -----------------------------
# Interface
# -----------------------------
##
## Enable/Disable dark mode.
##
## Accepted values: "dark", "light", "auto"
## Default: "auto"
##
#dark_mode: "auto"
##
## Enable/Disable thin mode (no video thumbnails).
##
## Accepted values: true, false
## Default: false
##
#thin_mode: false
##
## List of feeds available on the home page.
##
## Note: "Subscriptions" and "Playlists" are only visible
## when the user is logged in.
##
## Accepted values: A list of strings
## Each entry can be one of: "Popular", "Trending",
## "Subscriptions", "Playlists"
##
## Default: ["Popular", "Trending", "Subscriptions", "Playlists"] (show all feeds)
##
#feed_menu: ["Popular", "Trending", "Subscriptions", "Playlists"]
##
## Default feed to display on the home page.
##
## Note: setting this option to "Popular" has no
## effect when 'popular_enabled' is set to false.
##
## Accepted values: Popular, Trending, Subscriptions, Playlists, <none>
## Default: Popular
##
#default_home: Popular
##
## Default number of results to display per page.
##
## Note: this affects invidious-generated pages only, such
## as watch history and subscription feeds. Playlists, search
## results and channel videos depend on the data returned by
## the Youtube API.
##
## Accepted values: any positive integer
## Default: 40
##
#max_results: 40
##
## Show/hide annotations.
##
## Accepted values: true, false
## Default: false
##
#annotations: false
##
## Show/hide annotation.
##
## Accepted values: true, false
## Default: false
##
#annotations_subscribed: false
##
## Type of comments to display below video.
##
## Accepted values: a two-entries array.
## Each entry can be one of: "youtube", "reddit", ""
##
## Default: ["youtube", ""]
##
#comments: ["youtube", ""]
##
## Default player style.
##
## Accepted values: invidious, youtube
## Default: invidious
##
#player_style: invidious
##
## Show/Hide the "related videos" sidebar when
## watching a video.
##
## Accepted values: true, false
## Default: true
##
#related_videos: true
# -----------------------------
# Video player behavior
# -----------------------------
##
## Automatically play videos on page load.
##
## Accepted values: true, false
## Default: false
##
#autoplay: false
##
## Automatically load the "next" video (either next in
## playlist or proposed) when the current video ends.
##
## Accepted values: true, false
## Default: false
##
#continue: false
##
## Autoplay next video by default.
##
## Note: Only effective if 'continue' is set to true.
##
## Accepted values: true, false
## Default: true
##
#continue_autoplay: true
##
## Play videos in Audio-only mode by default.
##
## Accepted values: true, false
## Default: false
##
#listen: false
##
## Loop videos automatically.
##
## Accepted values: true, false
## Default: false
##
#video_loop: false
# -----------------------------
# Video playback settings
# -----------------------------
##
## Default video quality.
##
## Accepted values: dash, hd720, medium, small
## Default: hd720
##
#quality: hd720
##
## Default dash video quality.
##
## Note: this setting only takes effet if the
## 'quality' parameter is set to "dash".
##
## Accepted values:
## auto, best, 4320p, 2160p, 1440p, 1080p,
## 720p, 480p, 360p, 240p, 144p, worst
## Default: auto
##
#quality_dash: auto
##
## Default video playback speed.
##
## Accepted values: 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0
## Default: 1.0
##
#speed: 1.0
##
## Default volume.
##
## Accepted values: 0-100
## Default: 100
##
#volume: 100
##
## Allow 360° videos to be played.
##
## Note: This feature requires a WebGL-enabled browser.
##
## Accepted values: true, false
## Default: true
##
#vr_mode: true
##
## Save the playback position
## Allow to continue watching at the previous position when
## watching the same video.
##
## Accepted values: true, false
## Default: false
##
#save_player_pos: false
# -----------------------------
# Subscription feed
# -----------------------------
##
## In the "Subscription" feed, only show the latest video
## of each channel the user is subscribed to.
##
## Note: when combined with 'unseen_only', the latest unseen
## video of each channel will be displayed instead of the
## latest by date.
##
## Accepted values: true, false
## Default: false
##
#latest_only: false
##
## Enable/Disable user subscriptions desktop notifications.
##
## Accepted values: true, false
## Default: false
##
#notifications_only: false
##
## In the "Subscription" feed, Only show the videos that the
## user haven't watched yet (i.e which are not in their watch
## history).
##
## Accepted values: true, false
## Default: false
##
#unseen_only: false
##
## Default sorting parameter for subscription feeds.
##
## Accepted values:
## 'alphabetically'
## 'alphabetically - reverse'
## 'channel name'
## 'channel name - reverse'
## 'published'
## 'published - reverse'
##
## Default: published
##
#sort: published
# -----------------------------
# Miscellaneous
# -----------------------------
##
## Proxy videos through instance by default.
##
## Warning: As most users won't change this setting in their
## preferences, defaulting to true will significantly
## increase the instance's network usage, so make sure that
## your server's connection can handle it.
##
## Accepted values: true, false
## Default: false
##
#local: false
##
## Show the connected user's nick at the top right.
##
## Accepted values: true, false
## Default: true
##
#show_nick: true
##
## Automatically redirect to a random instance when the user uses
## any "switch invidious instance" link (For videos, it's the plane
## icon, next to "watch on youtube" and "listen"). When set to false,
## the user is sent to https://redirect.invidious.io instead, where
## they can manually select an instance.
##
## Accepted values: true, false
## Default: false
##
#automatic_instance_redirect: false
##
## Show the entire video description by default (when set to 'false',
## only the first few lines of the description are shown and a
## "show more" button allows to expand it).
##
## Accepted values: true, false
## Default: false
##
#extend_desc: false

@ -0,0 +1,11 @@
channel_threads: 1
feed_threads: 1
db:
user: kemal
password: kemal
host: localhost
port: 5432
dbname: invidious
full_refresh: false
https_only: false
domain:

@ -1,7 +1,4 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN subscribed bool;"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET subscribed = false;"
psql invidious kemal -c "ALTER TABLE channels ADD COLUMN subscribed bool;"
psql invidious kemal -c "UPDATE channels SET subscribed = false;"

@ -1,10 +1,7 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql invidious kemal -c "ALTER TABLE channel_videos DROP COLUMN live_now CASCADE"
psql invidious kemal -c "ALTER TABLE channel_videos DROP COLUMN premiere_timestamp CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos DROP COLUMN live_now CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos DROP COLUMN premiere_timestamp CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN live_now bool"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz"
psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN live_now bool"
psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz"

@ -1,22 +0,0 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN title CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN views CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN likes CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN dislikes CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN wilson_score CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN published CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN description CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN language CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN author CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN ucid CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN allowed_regions CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN is_family_friendly CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN genre CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN genre_url CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN license CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN sub_count_text CASCADE"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN author_thumbnail CASCADE"

@ -1,7 +1,4 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN deleted bool;"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET deleted = false;"
psql invidious kemal -c "ALTER TABLE channels ADD COLUMN deleted bool;"
psql invidious kemal -c "UPDATE channels SET deleted = false;"

@ -1,8 +1,5 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" < config/sql/session_ids.sql
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "INSERT INTO session_ids (SELECT unnest(id), email, CURRENT_TIMESTAMP FROM users) ON CONFLICT (id) DO NOTHING"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE users DROP COLUMN id"
psql invidious kemal < config/sql/session_ids.sql
psql invidious kemal -c "INSERT INTO session_ids (SELECT unnest(id), email, CURRENT_TIMESTAMP FROM users) ON CONFLICT (id) DO NOTHING"
psql invidious kemal -c "ALTER TABLE users DROP COLUMN id"

@ -1,6 +1,3 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" < config/sql/annotations.sql
psql invidious kemal < config/sql/annotations.sql

@ -1,6 +1,3 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN views bigint;"
psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN views bigint;"

@ -1,7 +1,4 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN live_now bool;"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channel_videos SET live_now = false;"
psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN live_now bool;"
psql invidious kemal -c "UPDATE channel_videos SET live_now = false;"

@ -1,6 +1,3 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE users ADD COLUMN feed_needs_update boolean"
psql invidious kemal -c "ALTER TABLE users ADD COLUMN feed_needs_update boolean"

@ -1,6 +1,3 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz;"
psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz;"

@ -1,8 +1,5 @@
#!/bin/sh
[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal
[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels DROP COLUMN subscribed"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN subscribed timestamptz"
psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET subscribed = '2019-01-01 00:00:00+00'"
psql invidious kemal -c "ALTER TABLE channels DROP COLUMN subscribed"
psql invidious kemal -c "ALTER TABLE channels ADD COLUMN subscribed timestamptz"
psql invidious kemal -c "UPDATE channels SET subscribed = '2019-01-01 00:00:00+00'"

@ -2,11 +2,11 @@
-- DROP TABLE public.annotations;
CREATE TABLE IF NOT EXISTS public.annotations
CREATE TABLE public.annotations
(
id text NOT NULL,
annotations xml,
CONSTRAINT annotations_id_key UNIQUE (id)
);
GRANT ALL ON TABLE public.annotations TO current_user;
GRANT ALL ON TABLE public.annotations TO kemal;

@ -2,7 +2,7 @@
-- DROP TABLE public.channel_videos;
CREATE TABLE IF NOT EXISTS public.channel_videos
CREATE TABLE public.channel_videos
(
id text NOT NULL,
title text,
@ -17,13 +17,13 @@ CREATE TABLE IF NOT EXISTS public.channel_videos
CONSTRAINT channel_videos_id_key UNIQUE (id)
);
GRANT ALL ON TABLE public.channel_videos TO current_user;
GRANT ALL ON TABLE public.channel_videos TO kemal;
-- Index: public.channel_videos_ucid_idx
-- DROP INDEX public.channel_videos_ucid_idx;
CREATE INDEX IF NOT EXISTS channel_videos_ucid_idx
CREATE INDEX channel_videos_ucid_idx
ON public.channel_videos
USING btree
(ucid COLLATE pg_catalog."default");

@ -2,7 +2,7 @@
-- DROP TABLE public.channels;
CREATE TABLE IF NOT EXISTS public.channels
CREATE TABLE public.channels
(
id text NOT NULL,
author text,
@ -12,13 +12,13 @@ CREATE TABLE IF NOT EXISTS public.channels
CONSTRAINT channels_id_key UNIQUE (id)
);
GRANT ALL ON TABLE public.channels TO current_user;
GRANT ALL ON TABLE public.channels TO kemal;
-- Index: public.channels_id_idx
-- DROP INDEX public.channels_id_idx;
CREATE INDEX IF NOT EXISTS channels_id_idx
CREATE INDEX channels_id_idx
ON public.channels
USING btree
(id COLLATE pg_catalog."default");

@ -2,20 +2,20 @@
-- DROP TABLE public.nonces;
CREATE TABLE IF NOT EXISTS public.nonces
CREATE TABLE public.nonces
(
nonce text,
expire timestamp with time zone,
CONSTRAINT nonces_id_key UNIQUE (nonce)
);
GRANT ALL ON TABLE public.nonces TO current_user;
GRANT ALL ON TABLE public.nonces TO kemal;
-- Index: public.nonces_nonce_idx
-- DROP INDEX public.nonces_nonce_idx;
CREATE INDEX IF NOT EXISTS nonces_nonce_idx
CREATE INDEX nonces_nonce_idx
ON public.nonces
USING btree
(nonce COLLATE pg_catalog."default");

@ -2,7 +2,7 @@
-- DROP TABLE public.playlist_videos;
CREATE TABLE IF NOT EXISTS public.playlist_videos
CREATE TABLE playlist_videos
(
title text,
id text,
@ -16,4 +16,4 @@ CREATE TABLE IF NOT EXISTS public.playlist_videos
PRIMARY KEY (index,plid)
);
GRANT ALL ON TABLE public.playlist_videos TO current_user;
GRANT ALL ON TABLE public.playlist_videos TO kemal;

@ -13,7 +13,7 @@ CREATE TYPE public.privacy AS ENUM
-- DROP TABLE public.playlists;
CREATE TABLE IF NOT EXISTS public.playlists
CREATE TABLE public.playlists
(
title text,
id text primary key,
@ -26,4 +26,4 @@ CREATE TABLE IF NOT EXISTS public.playlists
index int8[]
);
GRANT ALL ON public.playlists TO current_user;
GRANT ALL ON public.playlists TO kemal;

@ -2,7 +2,7 @@
-- DROP TABLE public.session_ids;
CREATE TABLE IF NOT EXISTS public.session_ids
CREATE TABLE public.session_ids
(
id text NOT NULL,
email text,
@ -10,13 +10,13 @@ CREATE TABLE IF NOT EXISTS public.session_ids
CONSTRAINT session_ids_pkey PRIMARY KEY (id)
);
GRANT ALL ON TABLE public.session_ids TO current_user;
GRANT ALL ON TABLE public.session_ids TO kemal;
-- Index: public.session_ids_id_idx
-- DROP INDEX public.session_ids_id_idx;
CREATE INDEX IF NOT EXISTS session_ids_id_idx
CREATE INDEX session_ids_id_idx
ON public.session_ids
USING btree
(id COLLATE pg_catalog."default");

@ -2,7 +2,7 @@
-- DROP TABLE public.users;
CREATE TABLE IF NOT EXISTS public.users
CREATE TABLE public.users
(
updated timestamp with time zone,
notifications text[],
@ -16,13 +16,13 @@ CREATE TABLE IF NOT EXISTS public.users
CONSTRAINT users_email_key UNIQUE (email)
);
GRANT ALL ON TABLE public.users TO current_user;
GRANT ALL ON TABLE public.users TO kemal;
-- Index: public.email_unique_idx
-- DROP INDEX public.email_unique_idx;
CREATE UNIQUE INDEX IF NOT EXISTS email_unique_idx
CREATE UNIQUE INDEX email_unique_idx
ON public.users
USING btree
(lower(email) COLLATE pg_catalog."default");

@ -2,21 +2,38 @@
-- DROP TABLE public.videos;
CREATE UNLOGGED TABLE IF NOT EXISTS public.videos
CREATE TABLE public.videos
(
id text NOT NULL,
info text,
updated timestamp with time zone,
title text,
views bigint,
likes integer,
dislikes integer,
wilson_score double precision,
published timestamp with time zone,
description text,
language text,
author text,
ucid text,
allowed_regions text[],
is_family_friendly boolean,
genre text,
genre_url text,
license text,
sub_count_text text,
author_thumbnail text,
CONSTRAINT videos_pkey PRIMARY KEY (id)
);
GRANT ALL ON TABLE public.videos TO current_user;
GRANT ALL ON TABLE public.videos TO kemal;
-- Index: public.id_idx
-- DROP INDEX public.id_idx;
CREATE UNIQUE INDEX IF NOT EXISTS id_idx
CREATE UNIQUE INDEX id_idx
ON public.videos
USING btree
(id COLLATE pg_catalog."default");

@ -1,12 +1,14 @@
# Warning: This docker-compose file is made for development purposes.
# Using it will build an image from the locally cloned repository.
#
# If you want to use Invidious in production, see the docker-compose.yml file provided
# in the installation documentation: https://docs.invidious.io/installation/
version: "3"
version: '3'
services:
postgres:
build:
context: .
dockerfile: docker/Dockerfile.postgres
restart: unless-stopped
volumes:
- postgresdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
invidious:
build:
context: .
@ -15,42 +17,21 @@ services:
ports:
- "127.0.0.1:3000:3000"
environment:
# Please read the following file for a comprehensive list of all available
# configuration options and their associated syntax:
# https://github.com/iv-org/invidious/blob/master/config/config.example.yml
# Adapted from ./config/config.yml
INVIDIOUS_CONFIG: |
channel_threads: 1
feed_threads: 1
db:
dbname: invidious
user: kemal
password: kemal
host: invidious-db
host: postgres
port: 5432
check_tables: true
# external_port:
# domain:
# https_only: false
# statistics_enabled: false
healthcheck:
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
interval: 30s
timeout: 5s
retries: 2
dbname: invidious
full_refresh: false
https_only: false
domain:
depends_on:
- invidious-db
invidious-db:
image: docker.io/library/postgres:13
restart: unless-stopped
volumes:
- postgresdata:/var/lib/postgresql/data
- ./config/sql:/config/sql
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
environment:
POSTGRES_DB: invidious
POSTGRES_USER: kemal
POSTGRES_PASSWORD: kemal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
- postgres
volumes:
postgresdata:

@ -1,62 +1,38 @@
FROM crystallang/crystal:1.4.1-alpine AS builder
RUN apk add --no-cache sqlite-static yaml-static
ARG release
ARG disable_quic
FROM alpine:edge AS builder
RUN apk add --no-cache curl crystal shards libc-dev \
yaml-dev libxml2-dev sqlite-dev zlib-dev openssl-dev \
yaml-static sqlite-static zlib-static openssl-libs-static
WORKDIR /invidious
RUN curl -Lo /etc/apk/keys/omarroth.rsa.pub https://github.com/omarroth/boringssl-alpine/releases/download/1.1.0-r0/omarroth.rsa.pub && \
curl -Lo boringssl-dev.apk https://github.com/omarroth/boringssl-alpine/releases/download/1.1.0-r0/boringssl-dev-1.1.0-r0.apk && \
curl -Lo lsquic.apk https://github.com/omarroth/lsquic-alpine/releases/download/2.6.3-r0/lsquic-2.6.3-r0.apk && \
apk verify --no-cache boringssl-dev.apk lsquic.apk && \
tar -xf boringssl-dev.apk usr/lib/libcrypto.a usr/lib/libssl.a && \
tar -xf lsquic.apk usr/lib/liblsquic.a && \
rm /etc/apk/keys/omarroth.rsa.pub boringssl-dev.apk lsquic.apk
COPY ./shard.yml ./shard.yml
COPY ./shard.lock ./shard.lock
RUN shards install --production
COPY --from=quay.io/invidious/lsquic-compiled /root/liblsquic.a ./lib/lsquic/src/lsquic/ext/liblsquic.a
RUN shards update && shards install && \
mv ./usr/lib/* ./lib/lsquic/src/lsquic/ext && \
rm -r ./usr /root/.cache
COPY ./src/ ./src/
# TODO: .git folder is required for building this is destructive.
# See definition of CURRENT_BRANCH, CURRENT_COMMIT and CURRENT_VERSION.
COPY ./.git/ ./.git/
# Required for fetching player dependencies
COPY ./scripts/ ./scripts/
COPY ./assets/ ./assets/
COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
RUN crystal spec --warnings all \
RUN crystal build ./src/invidious.cr \
--static --warnings all --error-on-warnings \
--link-flags "-lxml2 -llzma"
RUN if [[ "${release}" == 1 && "${disable_quic}" == 1 ]] ; then \
crystal build ./src/invidious.cr \
--release \
-Ddisable_quic \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
elif [[ "${release}" == 1 ]] ; then \
crystal build ./src/invidious.cr \
--release \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
else \
crystal build ./src/invidious.cr \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
fi
FROM alpine:3.16
RUN apk add --no-cache librsvg ttf-opensans tini
FROM alpine:latest
RUN apk add --no-cache librsvg ttf-opensans
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \
adduser -u 1000 -S invidious -G invidious
COPY --chown=invidious ./config/config.* ./config/
RUN mv -n config/config.example.yml config/config.yml
RUN sed -i 's/host: \(127.0.0.1\|localhost\)/host: invidious-db/' config/config.yml
COPY ./assets/ ./assets/
COPY --chown=invidious ./config/config.yml ./config/config.yml
RUN sed -i 's/host: \(127.0.0.1\|localhost\)/host: postgres/' config/config.yml
COPY ./config/sql/ ./config/sql/
COPY ./locales/ ./locales/
COPY --from=builder /invidious/assets ./assets/
COPY --from=builder /invidious/invidious .
RUN chmod o+rX -R ./assets ./config ./locales
EXPOSE 3000
USER invidious
ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "/invidious/invidious" ]

@ -1,61 +0,0 @@
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
ARG release
ARG disable_quic
WORKDIR /invidious
COPY ./shard.yml ./shard.yml
COPY ./shard.lock ./shard.lock
RUN shards install --production
COPY --from=quay.io/invidious/lsquic-compiled /root/liblsquic.a ./lib/lsquic/src/lsquic/ext/liblsquic.a
COPY ./src/ ./src/
# TODO: .git folder is required for building this is destructive.
# See definition of CURRENT_BRANCH, CURRENT_COMMIT and CURRENT_VERSION.
COPY ./.git/ ./.git/
# Required for fetching player dependencies
COPY ./scripts/ ./scripts/
COPY ./assets/ ./assets/
COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
RUN crystal spec --warnings all \
--link-flags "-lxml2 -llzma"
RUN if [[ "${release}" == 1 && "${disable_quic}" == 1 ]] ; then \
crystal build ./src/invidious.cr \
--release \
-Ddisable_quic \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
elif [[ "${release}" == 1 ]] ; then \
crystal build ./src/invidious.cr \
--release \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
else \
crystal build ./src/invidious.cr \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
fi
FROM alpine:3.16
RUN apk add --no-cache librsvg ttf-opensans tini
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \
adduser -u 1000 -S invidious -G invidious
COPY --chown=invidious ./config/config.* ./config/
RUN mv -n config/config.example.yml config/config.yml
RUN sed -i 's/host: \(127.0.0.1\|localhost\)/host: invidious-db/' config/config.yml
COPY ./config/sql/ ./config/sql/
COPY ./locales/ ./locales/
COPY --from=builder /invidious/assets ./assets/
COPY --from=builder /invidious/invidious .
RUN chmod o+rX -R ./assets ./config ./locales
EXPOSE 3000
USER invidious
ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "/invidious/invidious" ]

@ -0,0 +1,12 @@
FROM postgres:10
ENV POSTGRES_USER postgres
# Do not require a PostgreSQL superuser password.
# See https://github.com/docker-library/postgres/issues/681.
ENV POSTGRES_HOST_AUTH_METHOD trust
ADD ./config/sql /config/sql
ADD ./docker/entrypoint.postgres.sh /entrypoint.sh
ENTRYPOINT [ "/entrypoint.sh" ]
CMD [ "postgres" ]

@ -0,0 +1,30 @@
#!/usr/bin/env bash
CMD="$@"
if [ ! -f /var/lib/postgresql/data/setupFinished ]; then
echo "### first run - setting up invidious database"
/usr/local/bin/docker-entrypoint.sh postgres &
sleep 10
until runuser -l postgres -c 'pg_isready' 2>/dev/null; do
>&2 echo "### Postgres is unavailable - waiting"
sleep 5
done
>&2 echo "### importing table schemas"
su postgres -c 'createdb invidious'
su postgres -c 'psql -c "CREATE USER kemal WITH PASSWORD '"'kemal'"'"'
su postgres -c 'psql invidious kemal < config/sql/channels.sql'
su postgres -c 'psql invidious kemal < config/sql/videos.sql'
su postgres -c 'psql invidious kemal < config/sql/channel_videos.sql'
su postgres -c 'psql invidious kemal < config/sql/users.sql'
su postgres -c 'psql invidious kemal < config/sql/session_ids.sql'
su postgres -c 'psql invidious kemal < config/sql/nonces.sql'
su postgres -c 'psql invidious kemal < config/sql/annotations.sql'
su postgres -c 'psql invidious kemal < config/sql/playlists.sql'
su postgres -c 'psql invidious kemal < config/sql/playlist_videos.sql'
touch /var/lib/postgresql/data/setupFinished
echo "### invidious database setup finished"
exit
fi
echo "running postgres /usr/local/bin/docker-entrypoint.sh $CMD"
exec /usr/local/bin/docker-entrypoint.sh $CMD

@ -1,12 +0,0 @@
#!/bin/bash
set -eou pipefail
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/channels.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/videos.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/channel_videos.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/users.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/session_ids.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/nonces.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/annotations.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/playlists.sql
psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/playlist_videos.sql

@ -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"
repository: https://kubernetes-charts.storage.googleapis.com/
version: 8.3.0
digest: sha256:1feec3c396cbf27573dc201831ccd3376a4a6b58b2e7618ce30a89b8f5d707fd
generated: "2020-02-07T13:39:38.624846+01:00"

@ -1,7 +1,7 @@
apiVersion: v2
name: invidious
description: Invidious is an alternative front-end to YouTube
version: 1.1.1
version: 1.0.0
appVersion: 0.20.1
keywords:
- youtube
@ -9,14 +9,14 @@ keywords:
- video
- privacy
home: https://invidio.us/
icon: https://raw.githubusercontent.com/iv-org/invidious/05988c1c49851b7d0094fca16aeaf6382a7f64ab/assets/favicon-32x32.png
icon: https://raw.githubusercontent.com/omarroth/invidious/05988c1c49851b7d0094fca16aeaf6382a7f64ab/assets/favicon-32x32.png
sources:
- https://github.com/iv-org/invidious
- https://github.com/omarroth/invidious
maintainers:
- name: Leon Klingele
email: mail@leonklingele.de
dependencies:
- name: postgresql
version: ~12.1.6
repository: "https://charts.bitnami.com/bitnami/"
version: ~8.3.0
repository: "https://kubernetes-charts.storage.googleapis.com/"
engine: gotpl

@ -23,13 +23,6 @@ spec:
runAsUser: {{ .Values.securityContext.runAsUser }}
runAsGroup: {{ .Values.securityContext.runAsGroup }}
fsGroup: {{ .Values.securityContext.fsGroup }}
initContainers:
- name: wait-for-postgresql
image: postgres
args:
- /bin/sh
- -c
- until pg_isready -h {{ .Values.config.db.host }} -p {{ .Values.config.db.port }} -U {{ .Values.config.db.user }}; do echo waiting for database; sleep 2; done;
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
@ -57,5 +50,4 @@ spec:
httpGet:
port: 3000
path: /
initialDelaySeconds: 15
restartPolicy: Always

@ -7,14 +7,10 @@ metadata:
chart: {{ .Chart.Name }}
release: {{ .Release.Name }}
spec:
type: {{ .Values.service.type }}
ports:
- name: http
port: {{ .Values.service.port }}
port: 3000
targetPort: 3000
selector:
app: {{ template "invidious.name" . }}
release: {{ .Release.Name }}
{{- if .Values.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}

@ -1,7 +1,7 @@
name: invidious
image:
repository: quay.io/invidious/invidious
repository: omarroth/invidious
tag: latest
pullPolicy: Always
@ -13,11 +13,6 @@ autoscaling:
maxReplicas: 16
targetCPUUtilizationPercentage: 50
service:
type: ClusterIP
port: 3000
#loadBalancerIP:
resources: {}
#requests:
# cpu: 100m
@ -32,19 +27,14 @@ securityContext:
runAsGroup: 1000
fsGroup: 1000
# See https://github.com/bitnami/charts/tree/master/bitnami/postgresql
# See https://github.com/helm/charts/tree/master/stable/postgresql
postgresql:
image:
tag: 13
auth:
username: kemal
password: kemal
database: invidious
primary:
initdb:
username: kemal
password: kemal
scriptsConfigMap: invidious-postgresql-init
postgresqlUsername: kemal
postgresqlPassword: kemal
postgresqlDatabase: invidious
initdbUsername: kemal
initdbPassword: kemal
initdbScriptsConfigMap: invidious-postgresql-init
# Adapted from ../config/config.yml
config:

@ -1,124 +1,128 @@
{
"LIVE": "مُباشِر",
"Shared `x` ago": "تمَّ الرفع مُنذ `x`",
"Unsubscribe": "إلغاء الاشتراك",
"Subscribe": "الاشتراك",
"View channel on YouTube": "زيارة القناة على يوتيوب",
"View playlist on YouTube": "عرض قائمة التشغيل على يوتيوب",
"newest": "الأحدث",
"`x` subscribers": "`x` المشتركين",
"`x` videos": "`x` الفيديوهات",
"`x` playlists": "`x` قوائم التشغيل",
"LIVE": "مباشر",
"Shared `x` ago": "تم رفع الفيديو منذ `x`",
"Unsubscribe": "إلغاء الإشتراك",
"Subscribe": "إشتراك",
"View channel on YouTube": "زيارة القناة على موقع يوتيوب",
"View playlist on YouTube": "عرض قائمة التشغيل على اليوتيوب",
"newest": "الأجدد",
"oldest": "الأقدم",
"popular": "الأكثر شعبية",
"last": "الأخيرة",
"Next page": "الصفحة التالية",
"last": "اخر قوائم التشغيل المعدلة",
"Next page": "الصفحة الثانية",
"Previous page": "الصفحة السابقة",
"Clear watch history?": "هل تريد محو سجل المشاهدة؟",
"New password": "كلمة مرور جديدة",
"New passwords must match": "يَجبُ أن تكون كلمتا المرور متطابقتين",
"Authorize token?": "رمز التفويض؟",
"Authorize token for `x`?": "السماح بالرمز المميز ل 'x'؟",
"Clear watch history?": "مسح السجل ؟",
"New password": "الرقم السرى الجديد",
"New passwords must match": "الأرقام السرية يجب ان تكون متطابقة",
"Cannot change password for Google accounts": "لا يستطيع تغيير الرقم السرى لحساب جوجل",
"Authorize token?": "رمز الإذن ؟",
"Authorize token for `x`?": "تصريح الرمز لـ `x` ؟",
"Yes": "نعم",
"No": "لا",
"Import and Export Data": "اِستيراد البيانات وتصديرها",
"Import": "استيراد",
"Import Invidious data": "استيراد بيانات JSON Invidious",
"Import YouTube subscriptions": "استيراد اشتراكات YouTube/OPML",
"Import FreeTube subscriptions (.db)": "استيراد اشتراكات فريتيوب (.db)",
"Import NewPipe subscriptions (.json)": "استيراد اشتراكات نيو بايب (.json)",
"Import NewPipe data (.zip)": "استيراد بيانات نيو بايب (.zip)",
"Export": "تصدير",
"Export subscriptions as OPML": "تصدير الاشتراكات كـOPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "تصدير الاشتراكات كـOPML (لِنيو بايب و فريتيوب)",
"Export data as JSON": "تصدير بيانات Invidious كـ JSON",
"Delete account?": "حذف الحساب؟",
"History": "السِّجل",
"An alternative front-end to YouTube": "واجهة أمامية بديلة لموقع يوتيوب",
"JavaScript license information": "معلومات ترخيص جافا سكربت",
"Import and Export Data": "استخراج و إضافة البيانات",
"Import": "إضافة",
"Import Invidious data": "إضافة بيانات Invidious",
"Import YouTube subscriptions": "إضافةالإشتراكات من موقع يوتيوب",
"Import FreeTube subscriptions (.db)": "إضافةالمشتركين من FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "إضافة المشتركين من NewPipe (.json)",
"Import NewPipe data (.zip)": "إضافة بيانات NewPipe (.zip)",
"Export": "استخراج",
"Export subscriptions as OPML": "استخراج المشتركين كـ OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "استخراج المشتركين كـ OPML (لـ NewPipe و FreeTube)",
"Export data as JSON": "استخراج البيانات كـ JSON",
"Delete account?": "حذف الحساب ؟",
"History": "السجل",
"An alternative front-end to YouTube": "البديل الكامل لموقع يوتيوب",
"JavaScript license information": "معلومات ترخيص JavaScript",
"source": "المصدر",
"Log in": "تسجيل الدخول",
"Log in/register": "تسجيل الدخول \\ إنشاء حساب",
"User ID": "مُعرِّف المُستخدم",
"Password": "كلمة المرور",
"Time (h:mm:ss):": "الوقت (h:mm:ss):",
"Text CAPTCHA": "نص الكابتشا",
"Image CAPTCHA": "صورة الكابتشا",
"Log in/register": "تسجيل الدخول\\إنشاء حساب",
"Log in with Google": "تسجيل الدخول بإستخدام جوجل",
"User ID": "إسم المستخدم",
"Password": "الرقم السرى",
"Time (h:mm:ss):": "(يجب ان يكتب مثل هذا التنسيق) الوقت (h(ساعات):mm(دقائق):ss(ثوانى)):",
"Text CAPTCHA": "CAPTCHA كلامية",
"Image CAPTCHA": "CAPTCHA صورية",
"Sign In": "تسجيل الدخول",
"Register": "التسجيل",
"E-mail": "البريد الإلكتروني",
"Preferences": "الإعدادات",
"preferences_category_player": "إعدادات المُشغِّل",
"preferences_video_loop_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_comments_label": "التعليقات الافتراضية: ",
"Register": "انشاء الحساب",
"E-mail": "الإيميل",
"Google verification code": "رمز تحقق جوجل",
"Preferences": "التفضيلات",
"Player preferences": "التفضيلات المشغل",
"Always loop: ": "كرر الفيديو دائما: ",
"Autoplay: ": "تشغيل تلقائى: ",
"Play next by default: ": "شغل الفيديو التالي تلقائيا: ",
"Autoplay next video: ": "شغل الفيديو التالي تلقائيا (في قوائم التشغيل) ",
"Listen by default: ": "تشغيل النسخة السمعية تلقائى: ",
"Proxy videos: ": "عرض الفيديوهات عن طريق البروكسي؟ ",
"Default speed: ": "السرعة الإفتراضية: ",
"Preferred video quality: ": "الجودة المفضلة للفيديوهات: ",
"Player volume: ": "صوت المشغل: ",
"Default comments: ": "إضهار التعليقات الإفتراضية لـ: ",
"youtube": "يوتيوب",
"reddit": "ريديت",
"preferences_captions_label": "التسميات التوضيحية الإفتراضية: ",
"Fallback captions: ": "التسميات التوضيحية الاحتياطيَّة: ",
"preferences_related_videos_label": "اعرض الفيديوهات ذات الصلة: ",
"preferences_annotations_label": "اعرض الملاحظات في الفيديو تلقائيا: ",
"preferences_extend_desc_label": "توسيع وصف الفيديو تلقائيا: ",
"preferences_vr_mode_label": "مقاطع فيديو تفاعلية بزاوية 360 درجة (تتطلب WebGL): ",
"preferences_category_visual": "التفضيلات المرئية",
"preferences_player_style_label": "شكل مشغل الفيديوهات: ",
"Dark mode: ": "الوضع الليلي: ",
"preferences_dark_mode_label": "المظهر: ",
"reddit": "Reddit",
"Default captions: ": "الترجمات الإفتراضية: ",
"Fallback captions: ": "الترجمات المصاحبة: ",
"Show related videos: ": "اعرض الفيديوهات ذات الصلة: ",
"Show annotations by default: ": "اعرض الملاحظات في الفيديو تلقائيا: ",
"Visual preferences": "التفضيلات المرئية",
"Player style: ": "شكل مشغل الفيديوهات: ",
"Dark mode: ": "الوضع الليلى: ",
"Theme: ": "المظهر: ",
"dark": "غامق (اسود)",
"light": "فاتح (ابيض)",
"preferences_thin_mode_label": "الوضع الخفيف: ",
"preferences_category_misc": "تفضيلات متنوعة",
"preferences_automatic_instance_redirect_label": "إعادة توجيه المثيل التلقائي (إعادة التوجيه إلى redirect.invidious.io): ",
"preferences_category_subscription": "تفضيلات الاشتراك",
"preferences_annotations_subscribed_label": "عرض الملاحظات في الفيديوهات تلقائيا في القنوات المشترك بها فقط: ",
"Thin mode: ": "الوضع الخفيف: ",
"Subscription preferences": "تفضيلات الإشتراك",
"Show annotations by default for subscribed channels: ": "عرض الملاحظات في الفيديوهات تلقائيا في القنوات المشترك بها فقط: ",
"Redirect homepage to feed: ": "إعادة التوجية من الصفحة الرئيسية لصفحة المشتركين (لرؤية اخر فيديوهات المشتركين): ",
"preferences_max_results_label": "عدد الفيديوهات التى ستظهر فى صفحة المشتركين: ",
"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": "فقط أظهر الذي لم يتم رؤيته: ",
"preferences_notifications_only_label": "إظهار الإشعارات فقط (إذا كان هناك أي): ",
"Number of videos shown in feed: ": "عدد الفيديوهات التى ستظهر فى صفحة المشتركين: ",
"Sort videos by: ": "ترتيب الفيديو بـ: ",
"published": "احدث فيديو",
"published - reverse": "احدث فيديو - عكسى",
"alphabetically": "ترتيب ابجدى",
"alphabetically - reverse": "ابجدى - عكسى",
"channel name": إسم القناة",
"channel name - reverse": إسم القناة - عكسى",
"Only show latest video from channel: ": "فقط إظهر اخر فيديو من القناة: ",
"Only show latest unwatched video from channel: ": "فقط اظهر اخر فيديو لم يتم رؤيتة من القناة: ",
"Only show unwatched: ": "فقط اظهر الذى لم يتم رؤيتة: ",
"Only show notifications (if there are any): ": "إظهار الإشعارات فقط (إذا كان هناك أي): ",
"Enable web notifications": "تفعيل إشعارات المتصفح",
"`x` uploaded a video": "`x` رفع فيديو",
"`x` is live": "`x` في بث مباشر",
"preferences_category_data": "إعدادات التفضيلات",
"`x` is live": "`x` فى بث مباشر",
"Data preferences": "إعدادات التفضيلات",
"Clear watch history": "حذف سجل المشاهدة",
"Import/export data": ستيراد و تصدير البيانات",
"Change password": "تغير كلمة السر",
"Manage subscriptions": "إدارة الاشتراكات",
"Import/export data": ضافة\\إستخراج البيانات",
"Change password": "غير الرقم السرى",
"Manage subscriptions": "إدارة المشتركين",
"Manage tokens": "إدارة الرموز",
"Watch history": "سجل المشاهدة",
"Delete account": "حذف الحساب",
"preferences_category_admin": "إعدادات المدير",
"preferences_default_home_label": "الصفحة الرئيسية الافتراضية: ",
"preferences_feed_menu_label": "قائمة التدفقات: ",
"preferences_show_nick_label": "إظهار اللقب في الأعلى: ",
"Administrator preferences": "إعدادات المدير",
"Default homepage: ": "الصفحة الرئيسية الافتراضية ",
"Feed menu: ": "قائمة التدفقات: ",
"Top enabled: ": "تفعيل 'الأفضل' ؟ ",
"CAPTCHA enabled: ": "تفعيل الكابتشا: ",
"Login enabled: ": مكين تسجيل الدخول: ",
"Login enabled: ": فعيل الولوج: ",
"Registration enabled: ": "تفعيل التسجيل: ",
"Report statistics: ": "تقرير الإحصائيات: ",
"Save preferences": "حفظ الإعدادات",
"Subscription manager": "مدير الاشتراكات",
"Report statistics: ": "الإبلاغ عن الإحصائيات: ",
"Save preferences": "حفظ التفضيلات",
"Subscription manager": "مدير الإشتراكات",
"Token manager": "إداره الرمز",
"Token": "الرمز",
"Import/export": "استيراد/تصدير",
"unsubscribe": "إلغاء الاشتراك",
"`x` subscriptions": "`x` مشتركين",
"`x` tokens": "`x` رموز",
"Import/export": "إضافة\\إستخراج",
"unsubscribe": "إلغاء الإشتراك",
"revoke": "مسح",
"Subscriptions": "الاشتراكات",
"Subscriptions": "الإشتراكات",
"`x` unseen notifications": "`x` إشعارات لم تشاهدها بعد",
"search": "بحث",
"Log out": "تسجيل الخروج",
"Released under the AGPLv3 on Github.": "صدر تحت AGPLv3 على GitHub.",
"Released under the AGPLv3 by Omar Roth.": "تم الإنشاء تحت AGPLv3 بواسطة عمر روث.",
"Source available here.": "الأكواد متوفرة هنا.",
"View JavaScript license information.": "مشاهدة معلومات حول تراخيص الجافاسكريبت.",
"View privacy policy.": "عرض سياسة الخصوصية.",
@ -128,77 +132,79 @@
"Private": "خاص",
"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`",
"Show more": "عرض المزيد",
"Show less": "عرض اقل",
"Playlist privacy": "إعدادات الخصوصيه",
"Editing playlist `x`": "تعديل قائمه التشفيل `x`",
"Watch on YouTube": "مشاهدة الفيديو على اليوتيوب",
"Switch Invidious Instance": "تبديل المثيل Invidious",
"Hide annotations": "إخفاء الملاحظات في الفيديو",
"Show annotations": "عرض الملاحظات في الفيديو",
"Hide annotations": "إخفاء الملاحظات فى الفيديو",
"Show annotations": "عرض الملاحظات فى الفيديو",
"Genre: ": "النوع: ",
"License: ": "التراخيص: ",
"Family friendly? ": "محتوى عائلي؟ ",
"Family friendly? ": "محتوى عائلى? ",
"Wilson score: ": "درجة ويلسون: ",
"Engagement: ": "نسبة التفاعل: ",
"Engagement: ": "نسبة المشاركة (عدد المشاهدات\\عدد الإعجابات): ",
"Whitelisted regions: ": "الدول المسموح فيها هذا الفيديو: ",
"Blacklisted regions: ": "الدول المحظور فيها هذا الفيديو: ",
"Shared `x`": "تمت المشاركة في `x`",
"Blacklisted regions: ": "الدول الحظور فيها هذا الفيديو: ",
"Shared `x`": "شارك منذ `x`",
"`x` views": "`x` مشاهدات",
"Premieres in `x`": "يعرض فى `x`",
"Premieres `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.": "أهلًا! يبدو أن جافاسكريبت معطلٌ لديك. اضغط هنا لعرض التعليقات، وَضَع في اعتبارك أنها ستأخذ وقتًا أطول للتحميل.",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "اهلا! يبدو ان الجافاسكريبت معطلة. اضغط هنا لعرض التعليقات, ضع فى إعتبارك انها ستأخذ وقت اطول للعرض.",
"View YouTube comments": "عرض تعليقات اليوتيوب",
"View more comments on Reddit": "عرض المزيد من التعليقات على\\من موقع ريديت",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "عرض `x` تعليقات",
"": "عرض `x` تعليقات"
},
"View Reddit comments": "عرض تعليقات ريديت",
"View more comments on Reddit": "عرض المزيد من التعليقات على\\من موقع Reddit",
"View `x` comments": "عرض `x` تعليقات",
"View Reddit comments": "عرض تعليقات ريدإت Reddit",
"Hide replies": "إخفاء الردود",
"Show replies": "عرض الردود",
"Incorrect password": "كلمة السر غير صحيحة",
"Incorrect password": "الرقم السرى غير صحيح",
"Quota exceeded, try again in a few hours": "تم تجاوز عدد المرات المسموح بها, حاول مرة اخرى بعد عدة ساعات",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "غير قادر على تسجيل الدخول, تأكد من تشغيل المصادقة الثنائية 2FA.",
"Invalid TFA code": "كود مصادقة ثنائية 2FA غير صحيح",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "لم يتم تسجيل الدخول. هذا ربما بسبب ان المصادقة الثنائية 2FA معطلة فى حسابك.",
"Wrong answer": "إجابة خاطئة",
"Erroneous CAPTCHA": "الكابتشا CAPTCHA غير صاحلة",
"CAPTCHA is a required field": "مكان الكابتشا CAPTCHA مطلوب",
"User ID is a required field": "مكان اسم المستخدم مطلوب",
"Password is a required field": "مكان كلمة السر مطلوب",
"Wrong username or password": "اسم المستخدم او كلمة السر غير صحيح",
"Password cannot be empty": "لا يمكن أن تكون كلمة السر فارغة",
"Password cannot be longer than 55 characters": "يجب أن لا تتعدى كلمة السر 55 حرفًا",
"User ID is a required field": "مكان إسم المستخدم مطلوب",
"Password is a required field": "مكان الرقم السرى مطلوب",
"Wrong username or password": "إسم المستخدم او الرقم السرى غير صحيح",
"Please sign in using 'Log in with Google'": "الرجاء تسجيل الدخول 'تسجيل الدخول بواسطة جوجل'",
"Password cannot be empty": "الرقم السرى لايمكن ان يكون فارغ",
"Password cannot be longer than 55 characters": "الرقم السرى لا يتعدى 55 حرف",
"Please log in": "الرجاء تسجيل الدخول",
"Invidious Private Feed for `x`": "تغذية Invidious خاصة ل 'x'",
"Invidious Private Feed for `x`": "صفحة Invidious للمشتركين الخاصة\\مخفية لـ `x`",
"channel:`x`": "قناة:`x`",
"Deleted or invalid channel": "قناة ممسوحة او غير صالحة",
"This channel does not exist.": "هذه القناة غير موجودة.",
"Could not get channel info.": "لم يتمكن الحصول على معلومات القناة.",
"Could not fetch comments": "لا يتمكن إحضار التعليقات",
"This channel does not exist.": "القناة غير موجودة.",
"Could not get channel info.": "لم يستطع الحصول على معلومات القناة.",
"Could not fetch comments": "لم يتمكن من إحضار التعليقات",
"View `x` replies": "عرض `x` ردود",
"`x` ago": "`x` منذ",
"Load more": "تحميل المزيد",
"Could not create mix.": "تعذر إنشاء مزيج.",
"Load more": "عرض المزيد",
"`x` points": "`x` نقاط",
"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": "الحقل المخفي \"تحدي\" حقل مطلوب",
"Hidden field \"token\" is a required field": "الحقل المخفي \"رمز\" حقل مطلوب",
"Erroneous challenge": "تحدي خاطئ",
"Erroneous token": مز مميز خاطئ",
"No such user": "مستخدم غير موجود",
"Token is expired, please try again": "الرمز منتهى الصلاحية، الرجاء المحاولة مرة اخرى",
"English": "إنجليزي",
"English (auto-generated)": "إنجليزي (تم إنشائه تلقائيًا)",
"Could not pull trending pages.": م يستطع عرض الصفحات الراجئة.",
"Hidden field \"challenge\" is a required field": "مكان مخفى \"تحدى\" مكان مطلوب",
"Hidden field \"token\" is a required field": "مكان مخفى \"رمز\" مكان مطلوب",
"Erroneous challenge": "تحدى غير صالح",
"Erroneous token": وز غير صالح",
"No such user": "مستخدم غير صالح",
"Token is expired, please try again": "الرمز منتهى الصلاحية , الرجاء المحاولة مرة اخرى",
"English": "إنجليزى",
"English (auto-generated)": "إنجليزى (تم إنشائة تلقائى)",
"Afrikaans": "الأفريكانية",
"Albanian": "الألبانية",
"Amharic": "الأمهرية",
"Arabic": "العربية",
"Armenian": "الأرمينية",
"Azerbaijani": "أذربيجانية",
"Armenian": "الأرميني",
"Azerbaijani": "أذربيجان",
"Bangla": "البنغالية",
"Basque": "الباسكية",
"Basque": "الباسكي",
"Belarusian": "البيلاروسية",
"Bosnian": "البوسنية",
"Bulgarian": "البلغارية",
@ -295,13 +301,19 @@
"Yiddish": "اليديشية",
"Yoruba": "اليوروبا",
"Zulu": "الزولو",
"`x` years": "`x` سنوات",
"`x` months": "`x` شهور",
"`x` weeks": "`x` اسابيع",
"`x` days": "`x` ايام",
"`x` hours": "`x` ساعات",
"`x` minutes": "`x` دقائق",
"`x` seconds": "`x` ثوانى",
"Fallback comments: ": "التعليقات البديلة: ",
"Popular": "الأكثر شعبية",
"Search": "بحث",
"Top": "الأفضل",
"About": "حول",
"Rating: ": "التقييم: ",
"preferences_locale_label": "اللغة: ",
"Language: ": "اللغة: ",
"View as playlist": "عرض كا قائمة التشغيل",
"Default": "الكل",
"Music": "الاغانى",
@ -309,236 +321,16 @@
"News": "الأخبار",
"Movies": "الأفلام",
"Download": "نزّل",
"Download as: ": "نزله كـ: ",
"Download as: ": "نزّله كـ: ",
"%A %B %-d, %Y": "%A %-d %B %Y",
"(edited)": "(معدّل)",
"(edited)": "(تم تعديلة)",
"YouTube comment permalink": "رابط التعليق على اليوتيوب",
"permalink": "الرابط",
"`x` marked it with a ❤": "`x` أعجب بهذا",
"Audio mode": "الوضع الصوتي",
"`x` marked it with a ❤": "`x` اعجب بهذا",
"Audio mode": "الوضع الصوتى",
"Video mode": "وضع الفيديو",
"channel_tab_videos_label": "الفيديوهات",
"Videos": "الفيديوهات",
"Playlists": "قوائم التشغيل",
"channel_tab_community_label": "المجتمع",
"search_filters_sort_option_relevance": "ملائمة",
"search_filters_sort_option_rating": "تقييم",
"search_filters_sort_option_date": "التاريخ",
"search_filters_sort_option_views": "مشاهدات",
"search_filters_type_label": "نوع المحتوى",
"search_filters_duration_label": "المدة",
"search_filters_features_label": "الميزات",
"search_filters_sort_label": "فرز",
"search_filters_date_option_hour": "آخر ساعة",
"search_filters_date_option_today": "اليوم",
"search_filters_date_option_week": "هذا الأسبوع",
"search_filters_date_option_month": "هذا الشهر",
"search_filters_date_option_year": "هذه السنة",
"search_filters_type_option_video": "فيديو",
"search_filters_type_option_channel": "قناة",
"search_filters_type_option_playlist": "قائمة التشغيل",
"search_filters_type_option_movie": "فيلم",
"search_filters_type_option_show": "عرض",
"search_filters_features_option_hd": "عالية الدقة",
"search_filters_features_option_subtitles": "ترجمات",
"search_filters_features_option_c_commons": "المشاع الإبداعي",
"search_filters_features_option_three_d": "ثلاثي الأبعاد",
"search_filters_features_option_live": "مباشر",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "المكان",
"search_filters_features_option_hdr": "وضع التباين العالي",
"Current version: ": "الإصدار الحالي: ",
"next_steps_error_message": "بعد ذلك يجب أن تحاول: ",
"next_steps_error_message_refresh": "تحديث",
"next_steps_error_message_go_to_youtube": "انتقل إلى يوتيوب",
"search_filters_duration_option_short": "قصير (< 4 دقائق)",
"search_filters_duration_option_long": "طويل (> 20 دقيقة)",
"footer_source_code": "الكود المصدر",
"footer_original_source_code": "الكود المصدر الأصلي",
"footer_modfied_source_code": "الكود المصدر المعدل",
"adminprefs_modified_source_code_url_label": "URL إلى مستودع الكود المصدر المعدل",
"footer_documentation": "التوثيق",
"footer_donate_page": "تبرّع",
"preferences_region_label": "بلد المحتوى: ",
"preferences_quality_dash_label": "جودة فيديو DASH المفضلة: ",
"preferences_quality_option_dash": "DASH (الجودة التلقائية)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "متوسطة",
"preferences_quality_option_small": "صغيرة",
"preferences_quality_dash_option_auto": "تلقائي",
"preferences_quality_dash_option_best": "الأفضل",
"preferences_quality_dash_option_worst": "أسوأ",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_144p": "144p",
"search_filters_features_option_purchased": "تم شراؤها",
"none": "لاشيء",
"videoinfo_started_streaming_x_ago": "بدأ البث منذ `x`",
"videoinfo_watch_on_youTube": "مشاهدة على يوتيوب",
"videoinfo_youTube_embed_link": "مضمن",
"videoinfo_invidious_embed_link": "رابط مضمن",
"user_created_playlists": "'x' إنشاء قوائم التشغيل",
"user_saved_playlists": "قوائم التشغيل المحفوظة 'x'",
"Video unavailable": "الفيديو غير متوفر",
"search_filters_features_option_three_sixty": "360°",
"download_subtitles": "ترجمات - 'x' (.vtt)",
"invidious": "الخيالي",
"preferences_save_player_pos_label": "حفظ موضع التشغيل: ",
"crash_page_you_found_a_bug": "يبدو أنك قد وجدت خطأً برمجيًّا في Invidious!",
"generic_videos_count_0": "لا يوجد فيديوهات",
"generic_videos_count_1": "فيديو واحد",
"generic_videos_count_2": "فيديوهين",
"generic_videos_count_3": "{{count}} فيديوهات",
"generic_videos_count_4": "{{count}} فيديو",
"generic_videos_count_5": "{{count}} فيديو",
"generic_subscribers_count_0": "لا يوجد مشترك",
"generic_subscribers_count_1": "مشترك واحد",
"generic_subscribers_count_2": "مشتركان",
"generic_subscribers_count_3": "{{count}} مشتركين",
"generic_subscribers_count_4": "{{count}} مشترك",
"generic_subscribers_count_5": "{{count}} مشترك",
"generic_views_count_0": "لا يوجد مشاهدة",
"generic_views_count_1": "مشاهدة واحدة",
"generic_views_count_2": "مشاهدتان",
"generic_views_count_3": "{{count}} مشاهدات",
"generic_views_count_4": "{{count}} مشاهدة",
"generic_views_count_5": "{{count}} مشاهدة",
"generic_subscriptions_count_0": "لا يوجد اشتراك",
"generic_subscriptions_count_1": "اشتراك واحد",
"generic_subscriptions_count_2": "اشتراكان",
"generic_subscriptions_count_3": "{{count}} اشتراكات",
"generic_subscriptions_count_4": "{{count}} اشتراك",
"generic_subscriptions_count_5": "{{count}} اشتراك",
"generic_playlists_count_0": "لا يوجد قوائم تشغيل",
"generic_playlists_count_1": "قائمة تشغيل واحدة",
"generic_playlists_count_2": "قائمتا تشغيل",
"generic_playlists_count_3": "{{count}} قوائم تشغيل",
"generic_playlists_count_4": "{{count}} قائمة تشغيل",
"generic_playlists_count_5": "{{count}} قائمة تشغيل",
"English (United States)": "الإنجليزية (الولايات المتحدة)",
"Indonesian (auto-generated)": "إندونيسي (مُنشأ تلقائيًا)",
"Interlingue": "إنترلينغوي",
"Italian (auto-generated)": "الإيطالية (مُنشأة تلقائيًا)",
"Spanish (auto-generated)": "الأسبانية (تم إنشاؤه تلقائيًا)",
"crash_page_before_reporting": "قبل الإبلاغ عن خطأ، تأكد من وجود:",
"French (auto-generated)": "الفرنسية (مُنشأة تلقائيًا)",
"Portuguese (auto-generated)": "البرتغالية (تم إنشاؤه تلقائيًا)",
"Turkish (auto-generated)": "التركية (تم إنشاؤها تلقائيًا)",
"crash_page_refresh": "حاول <a href=\"`x`\"> تحديث الصفحة </a>",
"crash_page_switch_instance": "حاول <a href=\"`x`\"> استخدام مثيل آخر </a>",
"Korean (auto-generated)": "كوري (تم إنشاؤه تلقائيًا)",
"Spanish (Mexico)": "الإسبانية (المكسيك)",
"Vietnamese (auto-generated)": "فيتنامي (تم إنشاؤه تلقائيًا)",
"crash_page_report_issue": "إذا لم يساعد أي مما سبق، يرجى فتح <a href=\"`x`\"> مشكلة جديدة على GitHub </a> (ويفضل أن يكون باللغة الإنجليزية) وتضمين النص التالي في رسالتك (لا تترجم هذا النص):",
"crash_page_read_the_faq": "قراءة <a href=\"`x`\"> الأسئلة المتكررة (الأسئلة الشائعة) </a>",
"preferences_watch_history_label": "تمكين سجل المشاهدة: ",
"English (United Kingdom)": "الإنجليزية (المملكة المتحدة)",
"Cantonese (Hong Kong)": "الكانتونية (هونغ كونغ)",
"Chinese": "الصينية",
"Chinese (China)": "الصينية (الصين)",
"Chinese (Hong Kong)": "الصينية (هونج كونج)",
"Chinese (Taiwan)": "الصينية (تايوان)",
"Dutch (auto-generated)": "هولندي (تم إنشاؤه تلقائيًا)",
"German (auto-generated)": "ألماني (تم إنشاؤه تلقائيًا)",
"Japanese (auto-generated)": "اليابانية (مُنشأة تلقائيًا)",
"Portuguese (Brazil)": "البرتغالية (البرازيل)",
"Russian (auto-generated)": "الروسية (منشأة تلقائيا)",
"Spanish (Spain)": "الإسبانية (إسبانيا)",
"crash_page_search_issue": "بحثت عن <a href=\"`x`\"> المشكلات الموجودة على GitHub </a>",
"search_filters_title": "معامل الفرز",
"search_message_no_results": "لا توجد نتائج.",
"search_message_change_filters_or_query": "حاول توسيع استعلام البحث و / أو تغيير عوامل التصفية.",
"search_filters_date_label": "تاريخ الرفع",
"generic_count_weeks_0": "{{count}} أسبوع",
"generic_count_weeks_1": "أسبوع واحد",
"generic_count_weeks_2": "أسبوعين",
"generic_count_weeks_3": "{{count}} أسابيع",
"generic_count_weeks_4": "{{count}} أسبوع",
"generic_count_weeks_5": "{{count}} أسبوع",
"Popular enabled: ": "تم تمكين الشعبية: ",
"search_filters_duration_option_medium": "متوسط (4-20 دقيقة)",
"search_filters_date_option_none": "أي تاريخ",
"search_filters_type_option_all": "أي نوع",
"search_filters_features_option_vr180": "VR180",
"generic_count_minutes_0": "{{count}} دقيقة",
"generic_count_minutes_1": "دقيقة واحدة",
"generic_count_minutes_2": "دقيقتين",
"generic_count_minutes_3": "{{count}} دقائق",
"generic_count_minutes_4": "{{count}} دقيقة",
"generic_count_minutes_5": "{{count}} دقيقة",
"generic_count_hours_0": "{{count}} ساعة",
"generic_count_hours_1": "ساعة واحدة",
"generic_count_hours_2": "ساعتين",
"generic_count_hours_3": "{{count}} ساعات",
"generic_count_hours_4": "{{count}} ساعة",
"generic_count_hours_5": "{{count}} ساعة",
"comments_view_x_replies_0": "عرض رد {{count}}",
"comments_view_x_replies_1": "عرض رد {{count}}",
"comments_view_x_replies_2": "عرض رد {{count}}",
"comments_view_x_replies_3": "عرض رد {{count}}",
"comments_view_x_replies_4": "عرض الردود {{count}}",
"comments_view_x_replies_5": "عرض رد {{count}}",
"search_message_use_another_instance": " يمكنك أيضًا البحث عن <a href=\"`x`\"> في مثيل آخر </a>.",
"comments_points_count_0": "{{count}} نقطة",
"comments_points_count_1": "نقطة واحدة",
"comments_points_count_2": "نقطتان",
"comments_points_count_3": "{{count}} نقط",
"comments_points_count_4": "{{count}} نقطة",
"comments_points_count_5": "{{count}} نقطة",
"generic_count_years_0": "{{count}} السنة",
"generic_count_years_1": "{{count}} السنة",
"generic_count_years_2": "{{count}} السنة",
"generic_count_years_3": "{{count}} السنة",
"generic_count_years_4": "{{count}} سنوات",
"generic_count_years_5": "{{count}} السنة",
"tokens_count_0": "الرمز المميز {{count}}",
"tokens_count_1": "الرمز المميز {{count}}",
"tokens_count_2": "الرمز المميز {{count}}",
"tokens_count_3": "الرمز المميز {{count}}",
"tokens_count_4": "الرموز المميزة {{count}}",
"tokens_count_5": "الرمز المميز {{count}}",
"search_filters_apply_button": "تطبيق الفلاتر المحددة",
"search_filters_duration_option_none": "أي مدة",
"subscriptions_unseen_notifs_count_0": "{{count}} إشعار جديد",
"subscriptions_unseen_notifs_count_1": "إشعار واحد جديد",
"subscriptions_unseen_notifs_count_2": "إشعارين جديدين",
"subscriptions_unseen_notifs_count_3": "{{count}} إشعارات جديدة",
"subscriptions_unseen_notifs_count_4": "{{count}} إشعارا جديد",
"subscriptions_unseen_notifs_count_5": "{{count}} إشعار جديد",
"generic_count_days_0": "{{count}} يوم",
"generic_count_days_1": "يوم واحد",
"generic_count_days_2": "يومين",
"generic_count_days_3": "{{count}} أيام",
"generic_count_days_4": "{{count}} يوم",
"generic_count_days_5": "{{count}} يوم",
"generic_count_months_0": "{{count}} شهر",
"generic_count_months_1": "{{count}} شهر",
"generic_count_months_2": "{{count}} شهر",
"generic_count_months_3": "{{count}} شهر",
"generic_count_months_4": "{{count}} شهور",
"generic_count_months_5": "{{count}} شهر",
"generic_count_seconds_0": "{{count}} ثانية",
"generic_count_seconds_1": "ثانية واحدة",
"generic_count_seconds_2": "ثانيتين",
"generic_count_seconds_3": "{{count}} ثوانٍ",
"generic_count_seconds_4": "{{count}} ثانية",
"generic_count_seconds_5": "{{count}} ثانية",
"error_video_not_in_playlist": "الفيديو المطلوب غير موجود في قائمة التشغيل هذه. <a href=\"`x`\"> انقر هنا للحصول على الصفحة الرئيسية لقائمة التشغيل. </a>",
"channel_tab_shorts_label": "الفيديوهات القصيرة",
"channel_tab_streams_label": "البث المباشر",
"channel_tab_playlists_label": "قوائم التشغيل",
"channel_tab_channels_label": "القنوات",
"Music in this video": "الموسيقى في هذا الفيديو",
"Album: ": "الألبوم: ",
"Artist: ": "الفنان: ",
"Song: ": "أغنية: ",
"Channel Sponsor": "راعي القناة",
"Standard YouTube license": "ترخيص YouTube القياسي",
"Download is disabled": "تم تعطيل التحميلات",
"Import YouTube playlist (.csv)": "استيراد قائمة تشغيل YouTube (.csv)"
}
"Community": "المجتمع",
"Current version: ": "الإصدار الحالي: "
}

@ -1,94 +0,0 @@
{
"Subscribe": "সাবস্ক্রাইব",
"View channel on YouTube": "ইউটিউবে চ্যানেল দেখুন",
"View playlist on YouTube": "ইউটিউবে প্লেলিস্ট দেখুন",
"newest": "সর্ব-নতুন",
"oldest": "পুরানতম",
"popular": "জনপ্রিয়",
"last": "শেষটা",
"Next page": "পরের পৃষ্ঠা",
"Previous page": "আগের পৃষ্ঠা",
"Clear watch history?": "দেখার ইতিহাস সাফ করবেন?",
"New password": "নতুন পাসওয়ার্ড",
"New passwords must match": "নতুন পাসওয়ার্ড অবশ্যই মিলতে হবে",
"Authorize token?": "টোকেন অনুমোদন করবেন?",
"Authorize token for `x`?": "`x` -এর জন্য টোকেন অনুমোদন?",
"Yes": "হ্যাঁ",
"No": "না",
"Import and Export Data": "তথ্য আমদানি ও রপ্তানি",
"Import": "আমদানি",
"Import Invidious data": "ইনভিডিয়াস তথ্য আমদানি",
"Import YouTube subscriptions": "ইউটিউব সাবস্ক্রিপশন আনুন",
"Import FreeTube subscriptions (.db)": "ফ্রিটিউব সাবস্ক্রিপশন (.db) আনুন",
"Import NewPipe subscriptions (.json)": "নতুন পাইপ সাবস্ক্রিপশন আনুন (.json)",
"Import NewPipe data (.zip)": "নিউপাইপ তথ্য আনুন (.zip)",
"Export": "তথ্য বের করুন",
"Export subscriptions as OPML": "সাবস্ক্রিপশন OPML হিসাবে আনুন",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML-এ সাবস্ক্রিপশন বের করুন(নিউ পাইপ এবং ফ্রিউটিউব এর জন্য)",
"Export data as JSON": "JSON হিসাবে তথ্য বের করুন",
"Delete account?": "অ্যাকাউন্ট মুছে ফেলবেন?",
"History": "ইতিহাস",
"An alternative front-end to YouTube": "ইউটিউবের একটি বিকল্পস্বরূপ সম্মুখ-প্রান্ত",
"JavaScript license information": "জাভাস্ক্রিপ্ট লাইসেন্সের তথ্য",
"source": "সূত্র",
"Log in": "লগ ইন",
"Log in/register": "লগ ইন/রেজিস্টার",
"User ID": "ইউজার আইডি",
"Password": "পাসওয়ার্ড",
"Time (h:mm:ss):": "সময় (ঘণ্টা:মিনিট:সেকেন্ড):",
"Text CAPTCHA": "টেক্সট ক্যাপচা",
"Image CAPTCHA": "চিত্র ক্যাপচা",
"Sign In": "সাইন ইন",
"Register": "নিবন্ধন",
"E-mail": "ই-মেইল",
"Preferences": "পছন্দসমূহ",
"preferences_category_player": "প্লেয়ারের পছন্দসমূহ",
"preferences_video_loop_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": "প্লেয়ার শব্দের মাত্রা: ",
"LIVE": "লাইভ",
"Shared `x` ago": "`x` আগে শেয়ার করা হয়েছে",
"Unsubscribe": "আনসাবস্ক্রাইব",
"generic_views_count": "{{count}}জন দেখেছে",
"generic_views_count_plural": "{{count}}জন দেখেছে",
"generic_videos_count": "{{count}}টি ভিডিও",
"generic_videos_count_plural": "{{count}}টি ভিডিও",
"generic_subscribers_count": "{{count}}জন অনুসরণকারী",
"generic_subscribers_count_plural": "{{count}}জন অনুসরণকারী",
"preferences_watch_history_label": "দেখার ইতিহাস চালু করো: ",
"preferences_quality_option_dash": "ড্যাশ (সময়োপযোগী মান)",
"preferences_quality_dash_option_auto": "স্বয়ংক্রিয়",
"preferences_quality_dash_option_best": "সেরা",
"preferences_quality_dash_option_worst": "মন্দতম",
"preferences_quality_dash_option_4320p": "৪৩২০পি",
"preferences_quality_dash_option_2160p": "২১৬০পি",
"preferences_quality_dash_option_1440p": "১৪৪০পি",
"preferences_quality_dash_option_480p": "৪৮০পি",
"preferences_quality_dash_option_360p": "৩৬০পি",
"preferences_quality_dash_option_240p": "২৪০পি",
"preferences_quality_dash_option_144p": "১৪৪পি",
"preferences_comments_label": "সহজাত মন্তব্য: ",
"youtube": "ইউটিউব",
"Fallback captions: ": "বিকল্প উপাখ্যান: ",
"preferences_related_videos_label": "সম্পর্কিত ভিডিও দেখাও: ",
"preferences_annotations_label": "সহজাতভাবে টীকা দেখাও ",
"preferences_quality_option_hd720": "উচ্চ৭২০",
"preferences_quality_dash_label": "পছন্দের ড্যাশ ভিডিও মান: ",
"preferences_captions_label": "সহজাত উপাখ্যান: ",
"generic_playlists_count": "{{count}}টি চালুতালিকা",
"generic_playlists_count_plural": "{{count}}টি চালুতালিকা",
"reddit": "রেডিট",
"invidious": "ইনভিডিয়াস",
"generic_subscriptions_count": "{{count}}টি অনুসরণ",
"generic_subscriptions_count_plural": "{{count}}টি অনুসরণ",
"preferences_quality_option_medium": "মধ্যম",
"preferences_quality_option_small": "ছোট",
"preferences_quality_dash_option_1080p": "১০৮০পি",
"preferences_quality_dash_option_720p": "৭২০পি"
}

@ -1,58 +0,0 @@
{
"LIVE": "লাইভ",
"Shared `x` ago": "`x` আগে শেয়ার করা হয়েছে",
"Unsubscribe": "আনসাবস্ক্রাইব",
"Subscribe": "সাবস্ক্রাইব",
"View channel on YouTube": "ইউটিউবে চ্যানেল দেখুন",
"View playlist on YouTube": "ইউটিউবে প্লেলিস্ট দেখুন",
"newest": "সর্ব-নতুন",
"oldest": "পুরানতম",
"popular": "জনপ্রিয়",
"last": "শেষটা",
"Next page": "পরের পৃষ্ঠা",
"Previous page": "আগের পৃষ্ঠা",
"Clear watch history?": "দেখার ইতিহাস সাফ করবেন?",
"New password": "নতুন পাসওয়ার্ড",
"New passwords must match": "নতুন পাসওয়ার্ড অবশ্যই মিলতে হবে",
"Authorize token?": "টোকেন অনুমোদন করবেন?",
"Authorize token for `x`?": "`x` -এর জন্য টোকেন অনুমোদন?",
"Yes": "হ্যাঁ",
"No": "না",
"Import and Export Data": "তথ্য আমদানি ও রপ্তানি",
"Import": "আমদানি",
"Import Invidious data": "ইনভিডিয়াস তথ্য আমদানি",
"Import YouTube subscriptions": "ইউটিউব সাবস্ক্রিপশন আনুন",
"Import FreeTube subscriptions (.db)": "ফ্রিটিউব সাবস্ক্রিপশন (.db) আনুন",
"Import NewPipe subscriptions (.json)": "নতুন পাইপ সাবস্ক্রিপশন আনুন (.json)",
"Import NewPipe data (.zip)": "নিউপাইপ তথ্য আনুন (.zip)",
"Export": "তথ্য বের করুন",
"Export subscriptions as OPML": "সাবস্ক্রিপশন OPML হিসাবে আনুন",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML-এ সাবস্ক্রিপশন বের করুন(নিউ পাইপ এবং ফ্রিউটিউব এর জন্য)",
"Export data as JSON": "JSON হিসাবে তথ্য বের করুন",
"Delete account?": "অ্যাকাউন্ট মুছে ফেলবেন?",
"History": "ইতিহাস",
"An alternative front-end to YouTube": "ইউটিউবের একটি বিকল্পস্বরূপ সম্মুখ-প্রান্ত",
"JavaScript license information": "জাভাস্ক্রিপ্ট লাইসেন্সের তথ্য",
"source": "সূত্র",
"Log in": "লগ ইন",
"Log in/register": "লগ ইন/রেজিস্টার",
"User ID": "ইউজার আইডি",
"Password": "পাসওয়ার্ড",
"Time (h:mm:ss):": "সময় (ঘণ্টা:মিনিট:সেকেন্ড):",
"Text CAPTCHA": "টেক্সট ক্যাপচা",
"Image CAPTCHA": "চিত্র ক্যাপচা",
"Sign In": "সাইন ইন",
"Register": "নিবন্ধন",
"E-mail": "ই-মেইল",
"Preferences": "পছন্দসমূহ",
"preferences_category_player": "প্লেয়ারের পছন্দসমূহ",
"preferences_video_loop_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": "প্লেয়ার শব্দের মাত্রা: "
}

@ -1,479 +0,0 @@
{
"oldest": "més antic",
"Yes": "Sí",
"preferences_quality_label": "Qualitat de vídeo preferida: ",
"newest": "més nou",
"No": "No",
"User ID": "ID d'usuari",
"Preferences": "Preferències",
"Dark mode: ": "Mode fosc: ",
"dark": "fosc",
"light": "clar",
"published": "publicat",
"published - reverse": "publicat - invers",
"alphabetically": "alfabèticament",
"alphabetically - reverse": "alfabèticament - invers",
"channel name - reverse": "nom del canal - invers",
"preferences_category_data": "Preferències de dades",
"Delete account": "Elimina compte",
"Save preferences": "Guarda preferències",
"Private": "Privat",
"Show more": "Mostra'n més",
"Show less": "Mostra'n menys",
"Hide replies": "Amaga respostes",
"Arabic": "Àrab",
"Armenian": "Armeni",
"Basque": "Basc",
"Filipino": "Filipí",
"Finnish": "Finès",
"German": "Alemany",
"Greek": "Grec",
"Hungarian": "Hongarès",
"Icelandic": "Islandès",
"Italian": "Italià",
"Japanese": "Japonès",
"Korean": "Coreà",
"Kurdish": "Kurd",
"Lithuanian": "Lituà",
"Luxembourgish": "Luxemburguès",
"Macedonian": "Macedoni",
"Polish": "Polonès",
"Portuguese": "Portuguès",
"Romanian": "Romanès",
"Russian": "Rus",
"Serbian": "Serbi",
"Spanish (Latin America)": "Castellà (Amèrica llatina)",
"Turkish": "Turc",
"Ukrainian": "Ucraïnès",
"preferences_locale_label": "Idioma: ",
"Gaming": "Jocs",
"Movies": "Películes",
"Download": "Descarrega",
"Download as: ": "Descarrega com: ",
"channel_tab_videos_label": "Vídeos",
"search_filters_type_label": "Tipus",
"search_filters_duration_label": "Duració",
"search_filters_sort_label": "Ordena per",
"search_filters_date_option_week": "Aquesta setmana",
"search_filters_date_option_month": "Aquest mes",
"search_filters_date_option_year": "Aquest any",
"search_filters_type_option_video": "Vídeo",
"search_filters_type_option_channel": "Canal",
"search_filters_duration_option_short": "Curt (< 4 minuts)",
"search_filters_duration_option_long": "Llarg (> 20 minuts)",
"Current version: ": "Versió actual: ",
"Malay": "Malai",
"Persian": "Persa",
"Slovak": "Eslovac",
"Search": "Cerca",
"Show annotations": "Mostra anotacions",
"preferences_region_label": "País del contingut: ",
"preferences_sort_label": "Ordena vídeos per: ",
"Import/export": "Importa/exporta",
"channel name": "nom del canal",
"Title": "Títol",
"Belarusian": "Bielorús",
"Enable web notifications": "Activa notificacions web",
"search": "Cerca",
"Catalan": "Català",
"Croatian": "Croat",
"preferences_category_admin": "Preferències d'administrador",
"Hide annotations": "Amaga anotacions",
"Show replies": "Mostra respostes",
"Bulgarian": "Búlgar",
"Albanian": "Albanès",
"French": "Francès",
"Irish": "Irlandès",
"Maltese": "Maltès",
"Danish": "Danès",
"Galician": "Gallec",
"Hebrew": "Hebreu",
"Indonesian": "Indonesi",
"Spanish": "Castellà",
"Vietnamese": "Vietnamita",
"News": "Notícies",
"search_filters_type_option_show": "Mostra",
"footer_documentation": "Documentació",
"Thai": "Tailandès",
"Music": "Música",
"search_filters_sort_option_relevance": "Rellevància",
"search_filters_date_option_hour": "Última hora",
"search_filters_date_option_today": "Avui",
"preferences_volume_label": "Volum del reproductor: ",
"invidious": "Invidious",
"preferences_quality_dash_option_144p": "144p",
"Turkish (auto-generated)": "Turc (generat automàticament)",
"Urdu": "Urdú",
"Vietnamese (auto-generated)": "Vietnamita (generat automàticament)",
"Welsh": "Gal·lès",
"Yoruba": "Ioruba",
"YouTube comment permalink": "Enllaç permanent de comentari de YouTube",
"Channel Sponsor": "Patrocinador del canal",
"Audio mode": "Mode d'àudio",
"search_filters_date_option_none": "Qualsevol data",
"search_filters_type_option_playlist": "Llista de reproducció",
"search_filters_type_option_movie": "Pel·lícula",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_subtitles": "Subtítols/CC",
"search_filters_features_option_live": "Directe",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_hdr": "HDR",
"search_filters_features_option_location": "Ubicació",
"search_filters_apply_button": "Aplica els filtres seleccionats",
"videoinfo_started_streaming_x_ago": "Ha començat el directe fa `x`",
"next_steps_error_message_go_to_youtube": "Vés a YouTube",
"footer_donate_page": "Feu un donatiu",
"footer_original_source_code": "Codi font original",
"videoinfo_watch_on_youTube": "Veure a YouTube",
"user_saved_playlists": "`x` llistes de reproducció guardades",
"adminprefs_modified_source_code_url_label": "URL al repositori de codi font modificat",
"none": "cap",
"footer_modfied_source_code": "Codi font modificat",
"videoinfo_invidious_embed_link": "Incrusta l'enllaç",
"download_subtitles": "Subtítols - `x` (.vtt)",
"user_created_playlists": "`x`llistes de reproducció creades",
"Video unavailable": "Vídeo no disponible",
"channel_tab_channels_label": "Canals",
"channel_tab_playlists_label": "Llistes de reproducció",
"channel_tab_community_label": "Comunitat",
"Czech": "Txec",
"Default": "Per defecte",
"Amharic": "Amàric",
"preferences_automatic_instance_redirect_label": "Redirecció automàtica d'instàncies (retorna a redirect.invidious.io): ",
"Login enabled: ": "Activa inici de sessió: ",
"Registration enabled: ": "Activa registre: ",
"Whitelisted regions: ": "Regions a la llista blanca: ",
"Chinese (Simplified)": "Xinès (Simplificat)",
"Corsican": "Cors",
"Estonian": "Estonià",
"Japanese (auto-generated)": "Japonès (generat automàticament)",
"English (United States)": "Anglès (Estats Units)",
"English (auto-generated)": "Anglès (generat automàticament)",
"Cebuano": "Cebuà",
"Esperanto": "Esperanto",
"Scottish Gaelic": "Gaèlic escocès",
"Playlists": "Llistes de reproducció",
"search_filters_title": "Filtres",
"search_filters_type_option_all": "Qualsevol tipus",
"search_filters_duration_option_none": "Qualsevol duració",
"next_steps_error_message": "Després d'això, hauríeu d'intentar: ",
"next_steps_error_message_refresh": "Recarregar la pàgina",
"crash_page_refresh": "ha intentat <a href=\"`x`\">actualitzar la pàgina</a>",
"crash_page_report_issue": "Si cap de les anteriors no ha ajudat, <a href=\"`x`\">obre un nou issue a GitHub</a> (preferiblement en anglès) i inclou el text següent al missatge (NO tradueixis aquest text):",
"generic_subscriptions_count": "{{count}} subscripció",
"generic_subscriptions_count_plural": "{{count}} subscripcions",
"error_video_not_in_playlist": "El vídeo sol·licitat no existeix en aquesta llista de reproducció. <a href=\"`x`\">Feu clic aquí per a la pàgina d'inici de la llista de reproducció.</a>",
"comments_points_count": "{{count}} punt",
"comments_points_count_plural": "{{count}} punts",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"Create playlist": "Crear llista de reproducció",
"Text CAPTCHA": "Text CAPTCHA",
"Next page": "Pàgina següent",
"preferences_category_visual": "Preferències visuals",
"preferences_unseen_only_label": "Mostra només no vistos: ",
"preferences_listen_label": "Escolta per defecte: ",
"Import": "Importar",
"Token": "Testimoni",
"Wilson score: ": "Puntuació de Wilson: ",
"search_filters_date_label": "Data de càrrega",
"search_filters_features_option_three_sixty": "360°",
"source": "font",
"preferences_default_home_label": "Pàgina d'inici per defecte: ",
"preferences_comments_label": "Comentaris per defecte: ",
"`x` uploaded a video": "`x` ha penjat un vídeo",
"Released under the AGPLv3 on Github.": "Publicat sota l'AGPLv3 a GitHub.",
"Token manager": "Gestor de testimonis",
"Watch history": "Historial de reproduccions",
"Authorize token?": "Autoritzar testimoni?",
"Source available here.": "Font disponible aquí.",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exporta subscripcions com a OPML (per a NewPipe i FreeTube)",
"Log in": "Inicia sessió",
"search_filters_sort_option_date": "Data de càrrega",
"Unlisted": "No llistat",
"View privacy policy.": "Veure política de privadesa.",
"Public": "Públic",
"View all playlists": "Veure totes les llistes de reproducció",
"reddit": "Reddit",
"Manage tokens": "Gestiona testimonis",
"Not a playlist.": "No és una llista de reproducció.",
"preferences_local_label": "Vídeos de Proxy: ",
"View channel on YouTube": "Veure canal a Youtube",
"preferences_quality_dash_option_1080p": "1080p",
"Top enabled: ": "Activa top: ",
"Delete playlist `x`?": "Eliminar llista de reproducció `x`?",
"View JavaScript license information.": "Consulta la informació de la llicència de JavaScript.",
"Playlist privacy": "Privacitat de la llista de reproducció",
"search_message_no_results": "No s'han trobat resultats.",
"search_message_use_another_instance": " També es pot <a href=\"`x`\">buscar en una altra instància</a>.",
"Genre: ": "Gènere: ",
"Hidden field \"challenge\" is a required field": "El camp ocult \"repte\" és un camp obligatori",
"Burmese": "Birmà",
"View as playlist": "Mostra com a llista de reproducció",
"preferences_category_subscription": "Preferències de subscripció",
"Music in this video": "Música en aquest vídeo",
"Artist: ": "Artista: ",
"Album: ": "Àlbum: ",
"Shared `x`": "Compartit `x`",
"Premieres `x`": "Estrena `x`",
"View more comments on Reddit": "Veure més comentaris a Reddit",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Veure `x` comentari",
"": "Veure `x` comentaris"
},
"View Reddit comments": "Veure comentaris de Reddit",
"Incorrect password": "Contrasenya incorrecta",
"Erroneous CAPTCHA": "CAPTCHA erroni",
"CAPTCHA is a required field": "El CAPTCHA és un camp obligatori",
"Korean (auto-generated)": "Coreà (generat automàticament)",
"Kyrgyz": "Kirguís",
"Latin": "Llatí",
"Malagasy": "Malgaix",
"Maori": "Maori",
"Marathi": "Marathi",
"Norwegian Bokmål": "Bokmål Noruec",
"Nyanja": "Nyanja",
"Portuguese (Brazil)": "Portuguès (Brazil)",
"Punjabi": "Panjabi",
"Russian (auto-generated)": "Rus (generat automàticament)",
"Samoan": "Samoà",
"Somali": "Somali",
"Southern Sotho": "Sesotho",
"Spanish (Mexico)": "Espanyol (Mèxic)",
"Spanish (Spain)": "Espanyol (Espanya)",
"Sundanese": "Sondanès",
"Swahili": "Suahili",
"Tamil": "Tàmil",
"Telugu": "Telugu",
"Zulu": "Zulu",
"generic_count_months": "{{count}} mes",
"generic_count_months_plural": "{{count}} mesos",
"generic_count_weeks": "{{count}} setmana",
"generic_count_weeks_plural": "{{count}} setmanes",
"About": "Sobre",
"`x` marked it with a ❤": "`x`marca'l amb un ❤",
"Video mode": "Mode de vídeo",
"search_filters_features_label": "Característiques",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_vr180": "VR180",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_purchased": "Comprat",
"Chinese (Hong Kong)": "Xinès (Hong Kong)",
"Chinese (Taiwan)": "Xinès (Taiwan)",
"Hmong": "Hmong",
"Kazakh": "Kazakh",
"Igbo": "Igbo",
"Javanese": "Javanès",
"Indonesian (auto-generated)": "Indonesi (generat automàticament)",
"Interlingue": "Interlingüe",
"Khmer": "Khmer",
"This channel does not exist.": "Aquest canal no existeix.",
"Song: ": "Cançó: ",
"channel:`x`": "canal: `x`",
"Deleted or invalid channel": "Canal suprimit o no vàlid",
"Could not get channel info.": "No s'ha pogut obtenir la informació del canal.",
"Could not pull trending pages.": "No s'han pogut extreure les pàgines de tendència.",
"comments_view_x_replies": "Veure {{count}} resposta",
"comments_view_x_replies_plural": "Veure {{count}} respostes",
"Subscriptions": "Subscripcions",
"generic_count_seconds": "{{count}} segon",
"generic_count_seconds_plural": "{{count}} segons",
"channel_tab_shorts_label": "Vídeos curts",
"preferences_save_player_pos_label": "Desa la posició de reproducció: ",
"crash_page_before_reporting": "Abans d'informar d'un error, assegureu-vos que teniu:",
"crash_page_switch_instance": "ha intentat <a href=\"`x`\">utilitzar una altra instància</a>",
"crash_page_read_the_faq": "heu llegit les <a href=\"`x`\">Preguntes més freqüents (FAQ)</a>",
"crash_page_search_issue": "ha cercat <a href=\"`x`\">problemes existents a GitHub</a>",
"User ID is a required field": "L'identificador d'usuari és un camp obligatori",
"Password is a required field": "La contrasenya és un camp obligatori",
"Wrong username or password": "Nom d'usuari o contrasenya incorrectes",
"Password cannot be longer than 55 characters": "La contrasenya no pot tenir més de 55 caràcters",
"Invidious Private Feed for `x`": "Feed privat Invidious per a `x`",
"generic_views_count": "{{count}} visualització",
"generic_views_count_plural": "{{count}} visualitzacions",
"generic_videos_count": "{{count}} vídeo",
"generic_videos_count_plural": "{{count}} vídeos",
"Token is expired, please try again": "El testimoni ha caducat, torna-ho a provar",
"English": "Anglès",
"Kannada": "Kanarès",
"Erroneous token": "Testimoni erroni",
"`x` ago": "fa `x`",
"Empty playlist": "Llista de reproducció buida",
"Playlist does not exist.": "La llista de reproducció no existeix.",
"No such user": "No hi ha tal usuari",
"Afrikaans": "Afrikàans",
"Azerbaijani": "Azerbaidjana",
"Cantonese (Hong Kong)": "Cantonès (Hong Kong)",
"Chinese": "Xinès",
"Chinese (China)": "Xinès (Xina)",
"Chinese (Traditional)": "Xinès (Tradicional)",
"Dutch": "Holandès",
"Dutch (auto-generated)": "Holandès (generat automàticament)",
"French (auto-generated)": "Francès (generat automàticament)",
"Georgian": "Georgià",
"German (auto-generated)": "Alemany (generat automàticament)",
"Gujarati": "Gujarati",
"Hawaiian": "Hawaià",
"generic_count_years": "{{count}} any",
"generic_count_years_plural": "{{count}} anys",
"Popular": "Popular",
"Rating: ": "Valoració: ",
"permalink": "enllaç permanent",
"preferences_quality_dash_option_worst": "Pitjor",
"Yiddish": "Ídix",
"preferences_quality_dash_option_auto": "Automàtic",
"Western Frisian": "Frisó occidental",
"Swedish": "Suec",
"Only show latest unwatched video from channel: ": "Mostra només l'últim vídeo no vist del canal: ",
"preferences_continue_label": "Reprodueix el següent per defecte: ",
"Import YouTube subscriptions": "Importar subscripcions de YouTube",
"search_filters_sort_option_rating": "Valoració",
"preferences_thin_mode_label": "Mode prim: ",
"preferences_quality_option_small": "Petit",
"CAPTCHA enabled: ": "activa CAPTCHA: ",
"Import and Export Data": "Importar i exportar dades",
"preferences_quality_dash_option_360p": "360p",
"Popular enabled: ": "Activa popular: ",
"Password": "Contrasenya",
"Blacklisted regions: ": "Regions a la llista negra: ",
"Register": "Registra't",
"Shared `x` ago": "Compartit fa `x`",
"search_filters_sort_option_views": "Recompte de visualitzacions",
"Import Invidious data": "Importa dades JSON d'Invidious",
"preferences_related_videos_label": "Mostra vídeos relacionats: ",
"preferences_show_nick_label": "Mostra l'àlies a la part superior: ",
"Time (h:mm:ss):": "Temps (h:mm:ss):",
"Could not fetch comments": "No s'han pogut obtenir els comentaris",
"New password": "Nova contrasenya",
"preferences_notifications_only_label": "Mostra només notificacions (si n'hi ha): ",
"preferences_annotations_label": "Mostra anotacions per defecte: ",
"Import FreeTube subscriptions (.db)": "Importar subscripcions de FreeTube (.db)",
"Fallback captions: ": "Subtítols alternatius: ",
"Log out": "Tancar sessió",
"preferences_quality_dash_option_2160p": "2160p",
"Unsubscribe": "Cancel·la la subscripció",
"Log in/register": "Inicia sessió/registra't",
"Nepali": "Nepalí",
"Xhosa": "Xosa",
"preferences_captions_label": "Subtítols per defecte: ",
"preferences_autoplay_label": "Reproducció automàtica: ",
"`x` is live": "`x` està en directe",
"Uzbek": "Uzbek",
"Hausa": "Haussa",
"Bosnian": "Bosnià",
"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! Sembla que tens JavaScript desactivat. Feu clic aquí per veure els comentaris, tingueu en compte que poden trigar una mica més a carregar-se.",
"Password cannot be empty": "La contrasenya no pot estar buida",
"preferences_video_loop_label": "Sempre en bucle: ",
"preferences_quality_option_dash": "DASH (qualitat adaptativa)",
"Change password": "Canvia la contrasenya",
"Export data as JSON": "Exporta dades d'Invidious com a JSON",
"Wrong answer": "Resposta incorrecta",
"Clear watch history": "Neteja l'historial de reproduccions",
"Mongolian": "Mongol",
"preferences_quality_dash_option_best": "Millor",
"Authorize token for `x`?": "Autoritzar testimoni per a `x`?",
"Report statistics: ": "Estadístiques de l'informe: ",
"Switch Invidious Instance": "Canvia la instància d'Invidious",
"History": "Historial",
"Portuguese (auto-generated)": "Portuguès (generat automàticament)",
"footer_source_code": "Codi font",
"videoinfo_youTube_embed_link": "Insereix",
"generic_count_minutes": "{{count}} minut",
"generic_count_minutes_plural": "{{count}} minuts",
"preferences_category_player": "Preferències del reproductor",
"Sign In": "Inicia Sessió",
"preferences_continue_autoplay_label": "Reprodueix automàticament el següent vídeo: ",
"generic_playlists_count": "{{count}} llista de reproducció",
"generic_playlists_count_plural": "{{count}} llistes de reproducció",
"Delete account?": "Esborrar compte?",
"Please log in": "Si us plau inicieu sessió",
"Import NewPipe data (.zip)": "Importar dades de NewPipe (.zip)",
"Image CAPTCHA": "Imatge CAPTCHA",
"channel_tab_streams_label": "Transmissions en directe",
"preferences_category_misc": "Preferències diverses",
"preferences_annotations_subscribed_label": "Mostra les anotacions per defecte dels canals subscrits? ",
"Tajik": "Tadjik",
"preferences_player_style_label": "Estil del reproductor: ",
"Load more": "Carrega més",
"preferences_vr_mode_label": "Vídeos interactius de 360 graus (requereix WebGL): ",
"Manage subscriptions": "Gestionar les subscripcions",
"preferences_quality_option_medium": "Mitjà",
"Editing playlist `x`": "Editant la llista de reproducció `x`",
"search_filters_duration_option_medium": "Mitjà (4 - 20 minuts)",
"E-mail": "Correu electrònic",
"Spanish (auto-generated)": "Castellà (generat automàticament)",
"Export": "Exportar",
"preferences_quality_dash_option_4320p": "4320p",
"JavaScript license information": "Informació de la llicència de JavaScript",
"Hidden field \"token\" is a required field": "El camp ocult \"testimoni\" és un camp obligatori",
"Shona": "Xona",
"Family friendly? ": "Apte per a tots els públics? ",
"preferences_quality_dash_label": "Qualitat de vídeo DASH preferida: ",
"Hindi": "Hindi",
"An alternative front-end to YouTube": "Una interfície alternativa a YouTube",
"Export subscriptions as OPML": "Exporta subscripcions com a OPML",
"Watch on YouTube": "Veure a YouTube",
"Lao": "Laosià",
"search_message_change_filters_or_query": "Proveu d'ampliar la vostra consulta de cerca i/o canviar els filtres.",
"View YouTube comments": "Veure comentaris de YouTube",
"New passwords must match": "Les contrasenyes noves han de coincidir",
"Subscription manager": "Gestor de subscripcions",
"Premieres in `x`": "Estrena en `x`",
"youtube": "YouTube",
"Latvian": "Letó",
"LIVE": "EN VIU",
"Could not create mix.": "No s'ha pogut crear la barreja.",
"preferences_speed_label": "Velocitat per defecte: ",
"preferences_extend_desc_label": "Amplieu automàticament la descripció del vídeo: ",
"popular": "popular",
"Erroneous challenge": "Repte erroni",
"last": "darrer",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_1440p": "1440p",
"Previous page": "Pàgina anterior",
"Only show latest video from channel: ": "Mostra només l'últim vídeo del canal: ",
"unsubscribe": "cancel·la la subscripció",
"View playlist on YouTube": "Veure llista de reproducció a YouTube",
"Import NewPipe subscriptions (.json)": "Importar subscripcions de NewPipe (.json)",
"crash_page_you_found_a_bug": "Heu trobat un error a Invidious!",
"Subscribe": "Subscriu-me",
"generic_count_days": "{{count}} dia",
"generic_count_days_plural": "{{count}} dies",
"Trending": "Tendència",
"Updated `x` ago": "Actualitzat fa `x`",
"Haitian Creole": "Crioll Haitià",
"preferences_watch_history_label": "Habilita historial de reproduccions: ",
"generic_count_hours": "{{count}} hora",
"generic_count_hours_plural": "{{count}} hores",
"Malayalam": "Maialàiam",
"Clear watch history?": "Neteja historial de reproduccions?",
"Import/export data": "Importa/exporta dades",
"Sinhala": "Singalès",
"Delete playlist": "Eliminar llista de reproducció",
"Bangla": "Bengalí",
"Italian (auto-generated)": "Italià (generat automàticament)",
"License: ": "Llicència: ",
"(edited)": "(editat)",
"Pashto": "Paixtu",
"preferences_dark_mode_label": "Tema: ",
"revoke": "revocar",
"English (United Kingdom)": "Anglès (Regne Unit)",
"preferences_quality_option_hd720": "HD720",
"tokens_count": "{{count}} testimoni",
"tokens_count_plural": "{{count}} testimonis",
"subscriptions_unseen_notifs_count": "{{count}} notificació no vista",
"subscriptions_unseen_notifs_count_plural": "{{count}} notificacions no vistes",
"generic_subscribers_count": "{{count}} subscriptor",
"generic_subscribers_count_plural": "{{count}} subscriptors",
"Sindhi": "Sindhi",
"Slovenian": "Eslovè",
"preferences_feed_menu_label": "Menú del feed: ",
"Fallback comments: ": "Comentaris alternatius: ",
"Top": "Millors",
"preferences_max_results_label": "Nombre de vídeos mostrats al feed: ",
"Engagement: ": "Atracció: ",
"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"
}

@ -1,496 +0,0 @@
{
"LIVE": "ŽIVĚ",
"Shared `x` ago": "Zveřejněno před `x`",
"Unsubscribe": "Odhlásit odběr",
"Subscribe": "Odebírat",
"View channel on YouTube": "Otevřít kanál na YouTube",
"View playlist on YouTube": "Zobrazit playlist na YouTube",
"newest": "nejnovější",
"oldest": "nejstarší",
"popular": "populární",
"last": "poslední",
"Next page": "Další strana",
"Previous page": "Předchozí strana",
"Clear watch history?": "Smazat historii?",
"New password": "Nové heslo",
"New passwords must match": "Hesla se musí shodovat",
"Authorize token?": "Autorizovat token?",
"Authorize token for `x`?": "Autorizovat token pro `x`?",
"Yes": "Ano",
"No": "Ne",
"Import and Export Data": "Import a export dat",
"Import": "Importovat",
"Import Invidious data": "Importovat JSON údaje Invidious",
"Import YouTube subscriptions": "Importovat odběry z YouTube/OPML",
"Import FreeTube subscriptions (.db)": "Importovat odběry z FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importovat odběry z NewPipe (.json)",
"Import NewPipe data (.zip)": "Importovat údeje z NewPipe (.zip)",
"Export": "Exportovat",
"Export subscriptions as OPML": "Exportovat odběry jako OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportovat údaje jako OPML (na NewPipe a FreeTube)",
"Export data as JSON": "Exportovat data Invidious jako JSON",
"Delete account?": "Smazat účet?",
"History": "Historie",
"An alternative front-end to YouTube": "Alternativní front-end pro YouTube",
"JavaScript license information": "Informace o licenci JavaScript",
"source": "zdrojový kód",
"Log in": "Přihlásit se",
"Log in/register": "Přihlásit se/vytvořit účet",
"User ID": "ID uživatele",
"Password": "Heslo",
"Time (h:mm:ss):": "Čas (h:mm:ss):",
"Text CAPTCHA": "Textové CAPTCHA",
"Image CAPTCHA": "Obrázkové CAPTCHA",
"Sign In": "Přihlásit se",
"Register": "Vytvořit účet",
"E-mail": "E-mail",
"Preferences": "Nastavení",
"preferences_category_player": "Nastavení přehravače",
"preferences_video_loop_label": "Vždy opakovat: ",
"preferences_autoplay_label": "Automatické přehrávání: ",
"preferences_continue_label": "Automaticky přehrát další: ",
"preferences_continue_autoplay_label": "Automaticky přehrát další video: ",
"preferences_listen_label": "Poslouchat ve výchozím nastavení: ",
"preferences_local_label": "Video přes proxy: ",
"preferences_speed_label": "Výchozí rychlost: ",
"preferences_quality_label": "Preferovaná kvalita videa: ",
"preferences_volume_label": "Hlasitost přehrávače: ",
"preferences_comments_label": "Předpřipravené komentáře: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_captions_label": "Výchozí titulky: ",
"Fallback captions: ": "Záložní titulky: ",
"preferences_related_videos_label": "Zobrazit podobná videa: ",
"preferences_annotations_label": "Zobrazovat poznámky ve výchozím nastavení: ",
"preferences_extend_desc_label": "Rozšířit automaticky popis u videa: ",
"preferences_category_visual": "Nastavení vzhledu",
"preferences_player_style_label": "Styl přehrávače ",
"Dark mode: ": "Tmavý režim ",
"preferences_dark_mode_label": "Vzhled: ",
"dark": "tmavý",
"light": "světlý",
"preferences_thin_mode_label": "Kompaktní režim: ",
"preferences_category_subscription": "Nastavení předplatných",
"preferences_annotations_subscribed_label": "Ve výchozím nastavení zobrazovat poznámky u odebíraných kanálů: ",
"Redirect homepage to feed: ": "Přesměrovávat domovskou stránku na informační kanál: ",
"preferences_max_results_label": "Počet videí zobrazovaných v informačním kanále: ",
"preferences_sort_label": "Roztřídit videa podle: ",
"published": "publikováno",
"published - reverse": "podle publikování - obrátit",
"alphabetically": "podle abecedy",
"alphabetically - reverse": "podle abecedy - převrátit",
"channel name": "název kanálu",
"channel name - reverse": "podle jména kanálu - převrátit",
"Only show latest video from channel: ": "Jenom zobrazit nejnovjejší video z kanálu: ",
"Only show latest unwatched video from channel: ": "Zobrazit jen nejnovější nezhlédnuté video z daného kanálu: ",
"preferences_unseen_only_label": "Zobrazit jen již nezhlédnuté: ",
"preferences_notifications_only_label": "Zobrazit pouze upozornění (pokud nějaká jsou): ",
"Enable web notifications": "Povolit webová upozornění",
"`x` uploaded a video": "`x` nahrál(a) video",
"`x` is live": "`x` je živě",
"preferences_category_data": "Nastavení dat",
"Clear watch history": "Smazat historii",
"Import/export data": "Importovat/exportovat data",
"Change password": "Změnit heslo",
"Manage subscriptions": "Spravovat odebírané kanály",
"Manage tokens": "Spravovat tokeny",
"Watch history": "Historie sledování",
"Delete account": "Smazat účet",
"preferences_category_admin": "Administrátorská nastavení",
"preferences_default_home_label": "Základní domovská stránka: ",
"preferences_feed_menu_label": "Menu doporučených: ",
"CAPTCHA enabled: ": "CAPTCHA povolena: ",
"Login enabled: ": "Přihlášení povoleno: ",
"Registration enabled: ": "Registrace povolena ",
"Report statistics: ": "Oznámit statistiky: ",
"Save preferences": "Uložit nastavení",
"Subscription manager": "Správa odběrů",
"Token manager": "Správa tokenů",
"Token": "Token",
"Import/export": "Importovat/exportovat",
"unsubscribe": "odhlásit odběr",
"revoke": "vrátit zpět",
"Subscriptions": "Odběry",
"search": "hledat",
"Log out": "Odhlásit se",
"Source available here.": "Zdrojový kód dostupný zde.",
"View JavaScript license information.": "Zobrazit informace o licenci JavaScript .",
"View privacy policy.": "Zobrazit zásady ochrany osobních údajů.",
"Trending": "Trendy",
"Public": "Veřejné",
"Unlisted": "Neveřejné",
"Private": "Soukromé",
"View all playlists": "Zobrazit všechny playlisty",
"Updated `x` ago": "Aktualizováno před `x`",
"Delete playlist `x`?": "Smazat playlist `x`?",
"Delete playlist": "Smazat playlist",
"Create playlist": "Vytvořit playlist",
"Title": "Název",
"Editing playlist `x`": "Upravování playlistu `x`",
"Show more": "Zobrazit více",
"Show less": "Zobrazit méně",
"Watch on YouTube": "Sledovat na YouTube",
"Hide annotations": "Skrýt poznámky",
"Show annotations": "Zobrazit poznámky",
"Genre: ": "Žánr: ",
"License: ": "Licence: ",
"Family friendly? ": "Vhodné pro rodiny? ",
"Engagement: ": "Zapojení: ",
"English": "Angličtina",
"English (auto-generated)": "Angličtina (automaticky generováno)",
"Afrikaans": "Afrikánština",
"Albanian": "Albánština",
"Amharic": "Amharština",
"Arabic": "Arabština",
"Armenian": "Arménština",
"Azerbaijani": "Azerbajdžánština",
"Bangla": "Bengálština",
"Basque": "Baskičtina",
"Belarusian": "Běloruština",
"Bosnian": "Bosenština",
"Bulgarian": "Bulharština",
"Burmese": "Barmština",
"Catalan": "Katalánština",
"Cebuano": "Cebuánština",
"Chinese (Simplified)": "Čínština (zjednodušená)",
"Chinese (Traditional)": "Čínština (tradiční)",
"Corsican": "Korsičtina",
"Croatian": "Chorvatština",
"Czech": "Čeština",
"Danish": "Dánština",
"Dutch": "Nizozemština",
"Esperanto": "Esperanto",
"Estonian": "Estonština",
"Filipino": "Filipínština",
"Finnish": "Finština",
"French": "Francouzština",
"Galician": "Galicijština",
"Georgian": "Gruzínština",
"German": "Němčina",
"Greek": "Řečtina",
"Gujarati": "Gudžarátština",
"Haitian Creole": "Haitská kreolština",
"Hausa": "Hauština",
"Hawaiian": "Havajština",
"Hebrew": "Hebrejština",
"Hindi": "Hindština",
"Hmong": "Hmongština",
"Hungarian": "Maďarština",
"Icelandic": "Islandština",
"Igbo": "Igboština",
"Indonesian": "Indonéština",
"Irish": "Irština",
"Italian": "Italština",
"Japanese": "Japonština",
"Javanese": "Javánština",
"Kannada": "Kannadština",
"Kazakh": "Kazaština",
"Khmer": "Khmerština",
"Korean": "Korejština",
"Kurdish": "Kurdština",
"Kyrgyz": "Kyrgyzština",
"Lao": "Laoština",
"Latin": "Latina",
"Latvian": "Lotyština",
"Lithuanian": "Litevština",
"Luxembourgish": "Lucemburština",
"Macedonian": "Makedonština",
"Malagasy": "Malgaština",
"Malay": "Malajština",
"Malayalam": "Malajálamština",
"Maltese": "Maltština",
"Maori": "Maorština",
"Marathi": "Maráthština",
"Mongolian": "Mongolština",
"Nepali": "Nepálština",
"Norwegian Bokmål": "Norština Bokmål",
"Nyanja": "Čičevština",
"Pashto": "Paštština",
"Persian": "Perština",
"Polish": "Polština",
"Portuguese": "Portugalština",
"Punjabi": "Paňdžábština",
"Romanian": "Rumunština",
"Russian": "Ruština",
"Samoan": "Samojština",
"Scottish Gaelic": "Skotská gaelština",
"Serbian": "Srbština",
"Shona": "Shona",
"Sindhi": "Sindhština",
"Sinhala": "Sinhálština",
"Slovak": "Slovenština",
"Slovenian": "Slovinština",
"Somali": "Somálština",
"Southern Sotho": "Sesothština",
"Spanish": "Španělština",
"Spanish (Latin America)": "Španělština (Latinská Amerika)",
"Sundanese": "Sundština",
"Swahili": "Svahilština",
"Swedish": "Švédština",
"Tajik": "Tádžičtina",
"Tamil": "Tamilština",
"Telugu": "Telugština",
"Thai": "Thajština",
"Turkish": "Turečtina",
"Ukrainian": "Ukrajinština",
"Urdu": "Urdština",
"Uzbek": "Uzbečtina",
"Vietnamese": "Vietnamština",
"Welsh": "Velština",
"Western Frisian": "Západofríština",
"Xhosa": "Xhoština",
"Yiddish": "Jidiš",
"Yoruba": "Jorubština",
"Zulu": "Zuluština",
"Popular": "Populární",
"About": "Informace",
"Rating: ": "Hodnocení: ",
"preferences_locale_label": "Jazyk: ",
"Default": "Výchozí",
"Music": "Hudba",
"Gaming": "Hry",
"News": "Zprávy",
"Movies": "Filmy",
"Download": "Stáhnout",
"Download as: ": "Stáhnout jako: ",
"(edited)": "(upraveno)",
"`x` marked it with a ❤": "`x` to označil(a) se ❤",
"Audio mode": "Audiový režim",
"Video mode": "Videový režim",
"channel_tab_videos_label": "Videa",
"channel_tab_community_label": "Komunita",
"search_filters_sort_option_rating": "Hodnocení",
"search_filters_sort_option_date": "Datum nahrání",
"search_filters_sort_option_views": "Počet zhlédnutí",
"search_filters_duration_label": "Délka",
"search_filters_date_option_hour": "Poslední hodina",
"search_filters_date_option_today": "Dnes",
"search_filters_date_option_week": "Tento týden",
"search_filters_date_option_month": "Tento měsíc",
"search_filters_date_option_year": "Tento rok",
"search_filters_type_option_video": "Video",
"search_filters_type_option_channel": "Kanál",
"search_filters_type_option_playlist": "Playlist",
"search_filters_type_option_movie": "Film",
"search_filters_type_option_show": "Seriál",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_subtitles": "Titulky",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_live": "Živě",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Umístění",
"search_filters_features_option_hdr": "HDR",
"generic_count_days_0": "{{count}} dnem",
"generic_count_days_1": "{{count}} dny",
"generic_count_days_2": "{{count}} dny",
"generic_count_hours_0": "{{count}} hodinou",
"generic_count_hours_1": "{{count}} hodinami",
"generic_count_hours_2": "{{count}} hodinami",
"crash_page_refresh": "zkusili <a href=\"`x`\">obnovit stránku</a>",
"crash_page_switch_instance": "zkusili <a href=\"`x`\">použít jinou instanci</a>",
"preferences_vr_mode_label": "Interaktivní 360-stupňová videa (vyžaduje WebGL): ",
"English (United Kingdom)": "Angličtina (Spojené království)",
"Chinese (China)": "Čínština (Čína)",
"Chinese (Hong Kong)": "Čínština (Hong Kong)",
"Chinese (Taiwan)": "Čínština (Taiwan)",
"Portuguese (auto-generated)": "Portugalština (automaticky generováno)",
"Spanish (auto-generated)": "Španělština (automaticky generováno)",
"Spanish (Mexico)": "Španělština (Mexiko)",
"Spanish (Spain)": "Španělština (Španělsko)",
"generic_count_years_0": "{{count}} rokem",
"generic_count_years_1": "{{count}} lety",
"generic_count_years_2": "{{count}} lety",
"Fallback comments: ": "Záložní komentáře: ",
"Search": "Hledat",
"Top": "Nejlepší",
"Playlists": "Playlisty",
"videoinfo_started_streaming_x_ago": "Stream spuštěn před `x`",
"videoinfo_watch_on_youTube": "Sledovat na YouTube",
"videoinfo_youTube_embed_link": "Vložení",
"crash_page_read_the_faq": "si přečetli <a href=\"`x`\">často kladené otázky (FAQ)</a>",
"crash_page_before_reporting": "Před nahlášením chyby se ujistěte, že jste:",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_dash": "DASH (adaptivní kvalita)",
"generic_views_count_0": "{{count}} zhlédnutí",
"generic_views_count_1": "{{count}} zhlédnutí",
"generic_views_count_2": "{{count}} zhlédnutí",
"generic_subscriptions_count_0": "{{count}} odběr",
"generic_subscriptions_count_1": "{{count}} odběry",
"generic_subscriptions_count_2": "{{count}} odběrů",
"preferences_quality_dash_option_4320p": "4320p",
"generic_videos_count_0": "{{count}} video",
"generic_videos_count_1": "{{count}} videa",
"generic_videos_count_2": "{{count}} videí",
"preferences_quality_option_small": "Nízká",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_144p": "144p",
"preferences_quality_option_medium": "Střední",
"preferences_quality_dash_option_1440p": "1440p",
"invidious": "Invidious",
"View more comments on Reddit": "Zobrazit více komentářů na Redditu",
"generic_playlists_count_0": "{{count}} playlist",
"generic_playlists_count_1": "{{count}} playlisty",
"generic_playlists_count_2": "{{count}} playlistů",
"generic_subscribers_count_0": "{{count}} odběratel",
"generic_subscribers_count_1": "{{count}} odběratelé",
"generic_subscribers_count_2": "{{count}} odběratelů",
"preferences_watch_history_label": "Povolit historii sledování: ",
"preferences_quality_dash_option_240p": "240p",
"preferences_region_label": "Země obsahu: ",
"subscriptions_unseen_notifs_count_0": "{{count}} nezobrazené oznámení",
"subscriptions_unseen_notifs_count_1": "{{count}} nezobrazená oznámení",
"subscriptions_unseen_notifs_count_2": "{{count}} nezobrazených oznámení",
"Show replies": "Zobrazit odpovědi",
"Password cannot be longer than 55 characters": "Heslo nesmí být delší než 55 znaků",
"comments_view_x_replies_0": "Zobrazit {{count}} odpověď",
"comments_view_x_replies_1": "Zobrazit {{count}} odpovědi",
"comments_view_x_replies_2": "Zobrazit {{count}} odpovědí",
"comments_points_count_0": "{{count}} bod",
"comments_points_count_1": "{{count}} body",
"comments_points_count_2": "{{count}} bodů",
"German (auto-generated)": "Němčina (automaticky generováno)",
"Indonesian (auto-generated)": "Indonéština (automaticky generováno)",
"Interlingue": "Interlingue",
"Italian (auto-generated)": "Italština (automaticky generováno)",
"Japanese (auto-generated)": "Japonština (automaticky generováno)",
"Korean (auto-generated)": "Korejština (automaticky generováno)",
"Russian (auto-generated)": "Ruština (automaticky generováno)",
"generic_count_months_0": "{{count}} měsícem",
"generic_count_months_1": "{{count}} měsíci",
"generic_count_months_2": "{{count}} měsíci",
"generic_count_weeks_0": "{{count}} týdnem",
"generic_count_weeks_1": "{{count}} týdny",
"generic_count_weeks_2": "{{count}} týdny",
"generic_count_minutes_0": "{{count}} minutou",
"generic_count_minutes_1": "{{count}} minutami",
"generic_count_minutes_2": "{{count}} minutami",
"footer_documentation": "Dokumentace",
"next_steps_error_message_refresh": "Obnovit stránku",
"Chinese": "Čínština",
"Dutch (auto-generated)": "Nizozemština (automaticky generováno)",
"Erroneous token": "Chybný token",
"tokens_count_0": "{{count}} token",
"tokens_count_1": "{{count}} tokeny",
"tokens_count_2": "{{count}} tokenů",
"Portuguese (Brazil)": "Portugalština (Brazílie)",
"Token is expired, please try again": "Token vypršel, zkuste to prosím znovu",
"English (United States)": "Angličtina (Spojené státy)",
"Cantonese (Hong Kong)": "Kantonština (Hong Kong)",
"French (auto-generated)": "Francouzština (automaticky generováno)",
"Turkish (auto-generated)": "Turečtina (automaticky generováno)",
"Vietnamese (auto-generated)": "Vietnamština (automaticky generováno)",
"Current version: ": "Aktuální verze: ",
"next_steps_error_message": "Měli byste zkusit: ",
"footer_donate_page": "Přispět",
"download_subtitles": "Titulky - `x` (.vtt)",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"YouTube comment permalink": "Permanentní odkaz YouTube komentáře",
"permalink": "permalink",
"footer_original_source_code": "Původní zdrojový kód",
"adminprefs_modified_source_code_url_label": "URL repozitáře s upraveným zdrojovým kódem",
"Video unavailable": "Video není dostupné",
"next_steps_error_message_go_to_youtube": "Jít na YouTube",
"footer_modfied_source_code": "Upravený zdrojový kód",
"none": "žádné",
"videoinfo_invidious_embed_link": "Odkaz na vložení",
"user_saved_playlists": "`x` uložených playlistů",
"crash_page_you_found_a_bug": "Vypadá to, že jste našli chybu v Invidious!",
"user_created_playlists": "`x` vytvořených playlistů",
"crash_page_search_issue": "vyhledali <a href=\"`x`\">existující problémy na GitHubu</a>",
"crash_page_report_issue": "Pokud nepomohlo nic z výše uvedeného, <a href=\"`x`\">otevřete prosím nový problém na GitHubu</a> (pokud možno v angličtině) a zahrňte do zprávy následující text (NEpřekládejte jej):",
"preferences_quality_dash_label": "Preferovaná kvalita videí DASH: ",
"preferences_quality_dash_option_auto": "Automatická",
"preferences_quality_dash_option_best": "Nejlepší",
"preferences_quality_dash_option_worst": "Nejhorší",
"preferences_quality_dash_option_480p": "480p",
"Top enabled: ": "Povoleny nejlepší: ",
"generic_count_seconds_0": "{{count}} sekundou",
"generic_count_seconds_1": "{{count}} sekundami",
"generic_count_seconds_2": "{{count}} sekundami",
"preferences_save_player_pos_label": "Uložit pozici přehrávání: ",
"Incorrect password": "Nesprávné heslo",
"View as playlist": "Zobrazit jako playlist",
"View Reddit comments": "Zobrazit komentáře z Redditu",
"No such user": "Uživatel nenalezen",
"Playlist privacy": "Soukromí playlistu",
"Wrong answer": "Špatná odpověď",
"Could not pull trending pages.": "Nepodařilo se získat trendy stránky.",
"Erroneous CAPTCHA": "Chybná CAPTCHA",
"Password is a required field": "Heslo je vyžadované pole",
"preferences_automatic_instance_redirect_label": "Automatické přesměrování instance (fallback na redirect.invidious.io): ",
"Switch Invidious Instance": "Přepnout instanci Invidious",
"Empty playlist": "Prázdný playlist",
"footer_source_code": "Zdrojový kód",
"View YouTube comments": "Zobrazit YouTube komentáře",
"Blacklisted regions: ": "Oblasti na černé listině: ",
"Wrong username or password": "Nesprávné uživatelské jméno nebo heslo",
"Password cannot be empty": "Heslo nemůže být prázné",
"preferences_category_misc": "Různá nastavení",
"preferences_show_nick_label": "Zobrazit přezdívku na vrchu: ",
"Whitelisted regions: ": "Oblasti na bílé listině: ",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Zdravíme! Zdá se, že máte vypnutý JavaScript. Klikněte sem pro zobrazení komentářů - nezapomeňte, že se mohou načítat trochu déle.",
"User ID is a required field": "ID uživatele je vyžadované pole",
"Please log in": "Přihlaste se prosím",
"Invidious Private Feed for `x`": "Soukromý kanál Invidious pro `x`",
"Deleted or invalid channel": "Smazaný nebo neplatný kanál",
"This channel does not exist.": "Tento kanál neexistuje.",
"Hidden field \"token\" is a required field": "Skryté pole \"token\" je vyžadované",
"Wilson score: ": "Skóre Wilson: ",
"Shared `x`": "Zveřejněno `x`",
"Premieres in `x`": "Premiéra za `x`",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Zobrazit `x` komentář",
"": "Zobrazit `x` komentářů"
},
"Could not get channel info.": "Nepodařilo se získat informace o kanálu.",
"Could not fetch comments": "Nepodařilo se získat komentáře",
"Could not create mix.": "Nepodařilo se vytvořit mix.",
"Hidden field \"challenge\" is a required field": "Skryté pole \"challenge\" je vyžadované",
"Released under the AGPLv3 on Github.": "Vydáno pod licencí AGPLv3 na GitHubu.",
"Hide replies": "Skrýt odpovědi",
"channel:`x`": "kanál: `x`",
"Load more": "Načíst další",
"Not a playlist.": "Není playlist.",
"Playlist does not exist.": "Playlist neexistuje.",
"Erroneous challenge": "Chybná výzva",
"Premieres `x`": "Premiéra `x`",
"CAPTCHA is a required field": "CAPTCHA je vyžadované pole",
"`x` ago": "Před `x`",
"search_message_change_filters_or_query": "Zkuste rozšířit vyhledávaný dotaz a/nebo změnit filtry.",
"search_filters_date_option_none": "Jakékoli datum",
"search_filters_date_label": "Datum nahrání",
"search_filters_type_option_all": "Jakýkoli typ",
"search_filters_duration_option_none": "Jakákoli délka",
"search_filters_type_label": "Typ",
"search_filters_duration_option_short": "Krátká (< 4 minuty)",
"search_message_no_results": "Nenalezeny žádné výsledky.",
"search_filters_title": "Filtry",
"search_filters_duration_option_medium": "Střední (4 - 20 minut)",
"search_filters_duration_option_long": "Dlouhá (> 20 minut)",
"search_message_use_another_instance": " Můžete také <a href=\"`x`\">hledat na jiné instanci</a>.",
"search_filters_features_label": "Vlastnosti",
"search_filters_features_option_three_sixty": "360°",
"search_filters_features_option_vr180": "VR180",
"search_filters_features_option_purchased": "Zakoupeno",
"search_filters_sort_label": "Řadit dle",
"search_filters_sort_option_relevance": "Relevantnost",
"search_filters_apply_button": "Použít vybrané filtry",
"Popular enabled: ": "Populární povoleno: ",
"error_video_not_in_playlist": "Požadované video v tomto playlistu neexistuje. <a href=\"`x`\">Klikněte sem pro navštívení domovské stránky playlistu.</a>",
"channel_tab_shorts_label": "Shorts",
"channel_tab_playlists_label": "Playlisty",
"channel_tab_channels_label": "Kanály",
"channel_tab_streams_label": "Živé přenosy",
"Music in this video": "Hudba v tomto videu",
"Artist: ": "Umělec: ",
"Album: ": "Album: ",
"Channel Sponsor": "Sponzor kanálu",
"Song: ": "Skladba: ",
"Standard YouTube license": "Standardní licence YouTube",
"Download is disabled": "Stahování je zakázáno",
"Import YouTube playlist (.csv)": "Importovat YouTube playlist (.csv)"
}

@ -1,456 +0,0 @@
{
"LIVE": "LIVE",
"Shared `x` ago": "Delt for `x` siden",
"Unsubscribe": "Opsig abonnement",
"Subscribe": "Abonner",
"View channel on YouTube": "Vis kanal på YouTube",
"View playlist on YouTube": "Vis afspilningsliste på YouTube",
"newest": "nyeste",
"oldest": "ældste",
"popular": "populært",
"last": "sidste",
"Next page": "Næste side",
"Previous page": "Forrige side",
"Clear watch history?": "Ryd afspilningshistorik?",
"New password": "Nyt kodeord",
"New passwords must match": "Nye kodeord skal matche",
"Authorize token?": "Godkend token?",
"Authorize token for `x`?": "Godkend token til `x`?",
"Yes": "Ja",
"No": "Nej",
"Import and Export Data": "Importer og Eksporter Data",
"Import": "Importer",
"Import Invidious data": "Importer Invidious JSON-data",
"Import YouTube subscriptions": "Importer YouTube/OPML-abonnementer",
"Import FreeTube subscriptions (.db)": "Importer FreeTube abonnementer (.db)",
"Import NewPipe subscriptions (.json)": "Importer NewPipe abonnementer (.json)",
"Import NewPipe data (.zip)": "Importer NewPipe data (.zip)",
"Export": "Exporter",
"Export subscriptions as OPML": "Exporter abonnementer som OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exporter abonnementer som OPML (til NewPipe & FreeTube)",
"Export data as JSON": "Eksporter Invidious-data som JSON",
"Delete account?": "Slet konto?",
"History": "Historik",
"An alternative front-end to YouTube": "Et alternativt front-end til YouTube",
"JavaScript license information": "JavaScript licens information",
"source": "kilde",
"Log in": "Log på",
"Log in/register": "Log på/registrer",
"User ID": "Bruger ID",
"Password": "Kodeord",
"Time (h:mm:ss):": "Tid (t:mm:ss):",
"Text CAPTCHA": "Tekst CAPTCHA",
"Image CAPTCHA": "Billede CAPTCHA",
"Sign In": "Log ind",
"Register": "Registrer",
"E-mail": "E-mail",
"Preferences": "Præferencer",
"preferences_category_player": "Afspillerindstillinger",
"preferences_video_loop_label": "Altid gentag: ",
"preferences_autoplay_label": "Auto afspil: ",
"preferences_continue_label": "Afspil næste som standard: ",
"preferences_continue_autoplay_label": "Auto afspil næste video: ",
"preferences_listen_label": "Lyt som standard: ",
"preferences_local_label": "Proxy videoer: ",
"preferences_speed_label": "Standard hastighed: ",
"preferences_quality_label": "Foretrukken video kvalitet: ",
"preferences_volume_label": "Lydstyrke: ",
"preferences_comments_label": "Standard kommentarer: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_captions_label": "Standard undertekster: ",
"Fallback captions: ": "Alternative undertekster: ",
"preferences_related_videos_label": "Vis relaterede videoer: ",
"preferences_annotations_label": "Vis annotationer som standard: ",
"preferences_extend_desc_label": "Automatisk udvid videoens beskrivelse: ",
"preferences_vr_mode_label": "Interaktive 360 graders videoer (kræver WebGL): ",
"preferences_category_visual": "Visuelle præferencer",
"preferences_player_style_label": "Afspiller stil: ",
"Dark mode: ": "Mørk tilstand: ",
"preferences_dark_mode_label": "Tema: ",
"dark": "mørk",
"light": "lys",
"preferences_thin_mode_label": "Tynd tilstand: ",
"preferences_category_subscription": "Abonnements præferencer",
"preferences_annotations_subscribed_label": "Vis annotationer som standard for abonnerede kanaler: ",
"Redirect homepage to feed: ": "Omdiriger startside til feed: ",
"preferences_max_results_label": "Antal videoer vist i feed: ",
"preferences_sort_label": "Sorter videoer efter: ",
"published": "offentliggjort",
"published - reverse": "offentliggjort - omvendt",
"alphabetically": "alfabetisk",
"alphabetically - reverse": "alfabetisk - omvendt",
"channel name": "kanalnavn",
"channel name - reverse": "kanalnavn - omvendt",
"Only show latest video from channel: ": "Vis kun seneste video fra kanal: ",
"Only show latest unwatched video from channel: ": "Vis kun seneste usete video fra kanal: ",
"preferences_unseen_only_label": "Vis kun usete: ",
"preferences_notifications_only_label": "Vis kun notifikationer (hvis der er nogle): ",
"Enable web notifications": "Aktiver webnotifikationer",
"`x` uploaded a video": "`x` uploadede en video",
"`x` is live": "`x` er live",
"preferences_category_data": "Data præferencer",
"Clear watch history": "Ryd afspilningshistorik",
"Import/export data": "Importer/exporter data",
"Change password": "Skift adgangskode",
"Manage subscriptions": "Administrer abonnementer",
"Manage tokens": "Administrer tokens",
"Watch history": "Afspilningshistorik",
"Delete account": "Slet konto",
"preferences_category_admin": "Administrator præferencer",
"preferences_default_home_label": "Standard startside: ",
"preferences_feed_menu_label": "Feed menu: ",
"Top enabled: ": "Top aktiveret: ",
"CAPTCHA enabled: ": "CAPTCHA aktiveret: ",
"Login enabled: ": "Login aktiveret: ",
"Registration enabled: ": "Registrering aktiveret: ",
"Report statistics: ": "Indsend statistik: ",
"Save preferences": "Gem præferencer",
"Subscription manager": "Abonnementsmanager",
"Token manager": "Tokenmanager",
"Token": "Token",
"Import/export": "Importer/eksporter",
"unsubscribe": "opsig abonnement",
"revoke": "tilbagekald",
"Subscriptions": "Abonnementer",
"search": "søg",
"Log out": "Log ud",
"Source available here.": "Kilde tilgængelig her.",
"View JavaScript license information.": "Vis JavaScriptlicensinformation.",
"View privacy policy.": "Vis privatpolitik.",
"Trending": "Trending",
"Public": "Offentlig",
"Unlisted": "Skjult",
"Private": "Privat",
"View all playlists": "Vis alle afspilningslister",
"Updated `x` ago": "Opdateret for `x` siden",
"Delete playlist `x`?": "Fjern spilleliste `x`?",
"Delete playlist": "Slet afspilningsliste",
"Create playlist": "Opret afspilningsliste",
"Title": "Titel",
"Playlist privacy": "Privatlivsindstillinger for afspilningsliste",
"Editing playlist `x`": "Redigerer afspilningsliste `x`",
"Show more": "Vis mere",
"Show less": "Vis mindre",
"Watch on YouTube": "Se på YouTube",
"Hide annotations": "Skjul annotationer",
"Show annotations": "Vis annotationer",
"Genre: ": "Genre: ",
"License: ": "Licens: ",
"Family friendly? ": "Familievenlig? ",
"Wilson score: ": "Wilson score: ",
"Engagement: ": "Engagement: ",
"Whitelisted regions: ": "Whitelistede regioner: ",
"Blacklisted regions: ": "Blacklistede regioner: ",
"Shared `x`": "Delt `x`",
"Premieres in `x`": "Har premiere om `x`",
"Premieres `x`": "Har premiere om `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! Det ser ud til at du har JavaScript slået fra. Klik her for at se kommentarer, vær opmærksom på at de kan tage længere om at indlæse.",
"View YouTube comments": "Vis YouTube kommentarer",
"View more comments on Reddit": "Se flere kommentarer på Reddit",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Vis `x` kommentarer.([^.,0-9]|^)1([^.,0-9]|$)",
"": "Vis `x` kommentarer"
},
"View Reddit comments": "Vis Reddit kommentarer",
"Hide replies": "Skjul svar",
"Show replies": "Vis svar",
"Incorrect password": "Forkert adgangskode",
"Wrong answer": "Forkert svar",
"Erroneous CAPTCHA": "Fejlagtig CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA er et obligatorisk felt",
"User ID is a required field": "Bruger ID er et krævet felt",
"Password is a required field": "Adgangskode er et obligatorisk felt",
"Wrong username or password": "Forkert brugernavn eller adgangskode",
"Password cannot be empty": "Adgangskoden må ikke være tom",
"Password cannot be longer than 55 characters": "Adgangskoden må ikke være længere end 55 tegn",
"Please log in": "Venligst log ind",
"channel:`x`": "kanal: 'x'",
"Deleted or invalid channel": "Slettet eller invalid kanal",
"This channel does not exist.": "Denne kanal eksisterer ikke.",
"Could not get channel info.": "Kunne ikke hente kanal info.",
"Could not fetch comments": "Kunne ikke hente kommentarer",
"`x` ago": "'x' siden",
"Load more": "Hent flere",
"Could not create mix.": "Kunne ikke skabe blanding.",
"Empty playlist": "Tom playliste",
"Not a playlist.": "Ikke en playliste.",
"Playlist does not exist.": "Playlist eksisterer ikke.",
"Esperanto": "Esperanto",
"Czech": "Tjekkisk",
"Danish": "Dansk",
"channel_tab_community_label": "Samfund",
"Afrikaans": "Afrikansk",
"Portuguese": "Portugisisk",
"Ukrainian": "Ukrainsk",
"Fallback comments: ": "Fallback kommentarer: ",
"Popular": "Populær",
"footer_donate_page": "Doner",
"Gujarati": "Gujarati",
"Punjabi": "Punjabi",
"Sundanese": "Sundanesisk",
"Urdu": "Urdu",
"preferences_region_label": "Indhold land: ",
"Hidden field \"challenge\" is a required field": "Det skjulte felt \"challenge\" er et påkrævet felt",
"Albanian": "Albansk",
"preferences_quality_dash_label": "Fortrukket DASH video kvalitet: ",
"search_filters_features_option_live": "Direkte",
"Lao": "Lao-tse",
"Filipino": "Filippinsk",
"Greek": "Græsk",
"Kurdish": "Kurdisk",
"Malay": "Malaysisk",
"Romanian": "Rumænsk",
"Somali": "Somalisk",
"preferences_locale_label": "Sprog: ",
"News": "Nyheder",
"permalink": "permalink",
"search_filters_sort_option_date": "Upload dato",
"search_filters_features_label": "Funktioner",
"Khmer": "Khmer",
"Finnish": "Finsk",
"search_filters_date_option_week": "Denne uge",
"Korean": "Koreansk",
"Telugu": "Telugu",
"Malayalam": "Malayalam",
"View as playlist": "Se som spilleliste",
"Hungarian": "Ungarsk",
"Welsh": "Walisisk",
"search_filters_features_option_subtitles": "Undertekster/CC",
"Bosnian": "Bosnisk",
"Yiddish": "Jiddisch",
"Belarusian": "Belarussisk",
"search_filters_date_option_today": "I dag",
"Shona": "Shona",
"Slovenian": "Slovensk",
"Gaming": "Gaming",
"Bangla": "Bengali",
"Swahili": "Swahili",
"`x` marked it with a ❤": "`x`markeret med et ❤",
"Kyrgyz": "Kirgisisk",
"Turkish": "Tyrkisk",
"adminprefs_modified_source_code_url_label": "URL-adresse til modificeret kildekodelager",
"Switch Invidious Instance": "Skift Invidious instans",
"Samoan": "Samoansk",
"Spanish": "Spansk",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"footer_documentation": "Dokumentation",
"Pashto": "Pashto",
"footer_modfied_source_code": "Modificeret Kildekode",
"Released under the AGPLv3 on Github.": "Udgivet under AGPLv3 på GitHub.",
"Tajik": "Tadsjikisk",
"search_filters_date_option_month": "Denne måned",
"Hebrew": "Hebraisk",
"Kannada": "Kannada",
"Current version: ": "Nuværende version: ",
"Amharic": "Amharisk",
"Swedish": "Svensk",
"Corsican": "Korsikansk",
"search_filters_type_option_movie": "Film",
"Could not pull trending pages.": "Kunne ikke hente trending sider.",
"English": "Engelsk",
"search_filters_features_option_hd": "HD",
"Hausa": "Islandsk",
"search_filters_date_option_year": "Dette år",
"Japanese": "Japansk",
"search_filters_type_label": "Type",
"Icelandic": "Islandsk",
"Basque": "Baskisk",
"search_filters_sort_option_rating": "Bedømmelse",
"Yoruba": "Yoruba",
"Erroneous token": "Fejlagtig token",
"channel_tab_videos_label": "Videoer",
"search_filters_type_option_show": "Vis",
"Luxembourgish": "Luxemboursk",
"Vietnamese": "Vietnamesisk",
"Latvian": "Lettisk",
"Indonesian": "Indonesisk",
"search_filters_duration_label": "Varighed",
"footer_original_source_code": "Original kildekode",
"Search": "Søg",
"Serbian": "Serbisk",
"Armenian": "Armensk",
"Bulgarian": "Bulgarsk",
"French": "Fransk",
"Burmese": "Burmesisk",
"Macedonian": "Makedonsk",
"Southern Sotho": "Sydlige Sotho",
"About": "Omkring",
"Malagasy": "Madagaskiske",
"Rating: ": "Bedømmelse: ",
"Movies": "Film",
"YouTube comment permalink": "Youtube kommentarer permalink",
"search_filters_features_option_location": "Lokation",
"search_filters_features_option_hdr": "HDR",
"Cebuano": "Cebuano (Sugbuanon)",
"Nyanja": "Nyanja",
"Chinese (Simplified)": "Kinesisk (forenklet)",
"Chinese (Traditional)": "Kinesisk (traditionelt)",
"Dutch": "Hollandsk",
"Estonian": "Estisk",
"preferences_automatic_instance_redirect_label": "Automatisk eksempel omdirigering (Fallback til redirect.invidious.io): ",
"Nepali": "Nepalesisk",
"Norwegian Bokmål": "Norsk Bokmål",
"(edited)": "(ændret)",
"preferences_show_nick_label": "Vis kælenavn på toppen: ",
"Galician": "Galisisk",
"German": "Tysk",
"Maori": "Maori",
"Slovak": "Slovakisk",
"search_filters_sort_option_relevance": "Relevans",
"search_filters_date_option_hour": "Sidste time",
"search_filters_type_option_playlist": "Spilleliste",
"search_filters_duration_option_long": "Lang (> 20 minutter)",
"search_filters_features_option_c_commons": "Creative Commons",
"Marathi": "Marathi",
"Sindhi": "Sindhi",
"preferences_category_misc": "Diverse indstillinger",
"Erroneous challenge": "Fejlagtig udfordring",
"Hindi": "Hindi",
"Igbo": "Igbo",
"Javanese": "Javanesisk",
"Kazakh": "Kasabhisk",
"Latin": "Latinsk",
"Lithuanian": "Lituaisk",
"Mongolian": "Mongolsk",
"Spanish (Latin America)": "Spansk (Latinamerika)",
"Uzbek": "Usbekisk",
"Western Frisian": "Vestfrisisk",
"Top": "Top",
"Music": "Musik",
"search_filters_sort_option_views": "Antal visninger",
"search_filters_sort_label": "Sorter efter",
"Zulu": "Zulu",
"Invidious Private Feed for `x`": "Invidious Privat Feed til `x`",
"English (auto-generated)": "Engelsk (autogenereret)",
"Arabic": "Arabisk",
"Croatian": "Kroatisk",
"Hawaiian": "Hawaiiansk",
"Maltese": "Maltesisk",
"Polish": "Polsk",
"Russian": "Russisk",
"Download": "Hent",
"Download as: ": "Hent som: ",
"Playlists": "Spillelister",
"next_steps_error_message_refresh": "Opdater",
"next_steps_error_message_go_to_youtube": "Gå til Youtube",
"footer_source_code": "Kildekode",
"Tamil": "Tamil",
"Xhosa": "Xhosa",
"next_steps_error_message": "Efter det burde du prøve at: ",
"Sinhala": "Singalesisk (Sinhala)",
"Thai": "Thai",
"No such user": "Brugeren findes ikke",
"Token is expired, please try again": "Token er udløbet, prøv igen",
"Catalan": "Catalansk",
"Haitian Creole": "Haitiansk",
"Irish": "Irsk",
"Persian": "Persisk",
"Scottish Gaelic": "Skotsk Gælisk",
"Default": "Standard",
"Video mode": "Videotilstand",
"search_filters_duration_option_short": "Kort (< 4 minutter)",
"Hidden field \"token\" is a required field": "Det skjulte felt \"token\" er et påkrævet felt",
"Azerbaijani": "Aserbajdsjansk",
"Georgian": "Georgisk",
"Italian": "Italiensk",
"Audio mode": "Lydtilstand",
"search_filters_type_option_video": "Video",
"search_filters_type_option_channel": "Kanal",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_four_k": "4K",
"Hmong": "Hmong",
"preferences_quality_option_medium": "Medium",
"preferences_quality_option_small": "Lille",
"preferences_quality_dash_option_best": "Bedste",
"preferences_quality_dash_option_worst": "Værste",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_144p": "144p",
"invidious": "Invidious",
"search_filters_features_option_purchased": "Købt",
"search_filters_features_option_three_sixty": "360°",
"none": "ingen",
"videoinfo_started_streaming_x_ago": "Streamen blev startet for `x`siden",
"videoinfo_watch_on_youTube": "Se på YouTube",
"videoinfo_youTube_embed_link": "Integrer",
"videoinfo_invidious_embed_link": "Integrer Link",
"download_subtitles": "Undertekster - `x`(.vtt)",
"user_created_playlists": "`x`opretede spillelister",
"user_saved_playlists": "´x`gemte spillelister",
"Video unavailable": "Video ikke tilgængelig",
"preferences_save_player_pos_label": "Gem afspilningsposition: ",
"preferences_quality_dash_option_auto": "Auto",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_option_dash": "DASH (adaptiv kvalitet)",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_240p": "240p",
"subscriptions_unseen_notifs_count": "{{count}} uset notifikation",
"subscriptions_unseen_notifs_count_plural": "{{count}} usete notifikationer",
"comments_view_x_replies": "Vis {{count}} svar",
"comments_view_x_replies_plural": "Vis {{count}} svar",
"comments_points_count": "{{count}} point",
"comments_points_count_plural": "{{count}} point",
"generic_count_years": "{{count}} år",
"generic_count_years_plural": "{{count}} år",
"generic_count_months": "{{count}} måned",
"generic_count_months_plural": "{{count}} måneder",
"generic_count_days": "{{count}} dag",
"generic_count_days_plural": "{{count}} dage",
"generic_count_minutes": "{{count}} minut",
"generic_count_minutes_plural": "{{count}} minutter",
"generic_count_seconds": "{{count}} sekund",
"generic_count_seconds_plural": "{{count}} sekunder",
"generic_subscribers_count": "{{count}} abonnent",
"generic_subscribers_count_plural": "{{count}} abonnenter",
"generic_subscriptions_count": "{{count}} abonnement",
"generic_subscriptions_count_plural": "{{count}} abonnementer",
"generic_videos_count": "{{count}} video",
"generic_videos_count_plural": "{{count}} videoer",
"English (United States)": "Engelsk (USA)",
"French (auto-generated)": "Fransk (autogenereret)",
"Spanish (auto-generated)": "Spansk (autogenereret)",
"crash_page_before_reporting": "Før du rapporterer en fejl, skal du sikre dig, at du har:",
"crash_page_refresh": "forsøgte at <a href=\"`x`\">opdatere siden</a>",
"generic_playlists_count": "{{count}} spilleliste",
"generic_playlists_count_plural": "{{count}} spillelister",
"preferences_watch_history_label": "Aktiver afspilningshistorik: ",
"tokens_count": "{{count}} token",
"tokens_count_plural": "{{count}} tokens",
"Cantonese (Hong Kong)": "Kantonesisk (Hongkong)",
"Chinese": "Kinesisk",
"Chinese (China)": "Kinesisk (Kina)",
"Chinese (Hong Kong)": "Kinesisk (Hongkong)",
"Chinese (Taiwan)": "Kinesisk (Taiwan)",
"Dutch (auto-generated)": "Hollandsk (autogenereret)",
"Indonesian (auto-generated)": "Indonesisk (autogenereret)",
"Interlingue": "Interlingue",
"Japanese (auto-generated)": "Japansk (autogenereret)",
"Korean (auto-generated)": "Koreansk (autogenereret)",
"Russian (auto-generated)": "Russisk (autogenereret)",
"Turkish (auto-generated)": "Tyrkisk (autogenereret)",
"Vietnamese (auto-generated)": "Vietnamesisk (autogenereret)",
"crash_page_report_issue": "Hvis intet af ovenstående hjalp, bedes du <a href=\"`x`\">åbne et nyt problem på GitHub</a> (helst på engelsk) og inkludere følgende tekst i din besked (oversæt IKKE denne tekst):",
"English (United Kingdom)": "Engelsk (Storbritannien)",
"Italian (auto-generated)": "Italiensk (autogenereret)",
"Portuguese (auto-generated)": "Portugisisk (autogenereret)",
"Portuguese (Brazil)": "Portugisisk (Brasilien)",
"generic_views_count": "{{count}} visning",
"generic_views_count_plural": "{{count}} visninger",
"generic_count_hours": "{{count}} time",
"generic_count_hours_plural": "{{count}} timer",
"Spanish (Spain)": "Spansk (Spanien)",
"crash_page_switch_instance": "forsøgte at <a href=\"`x`\">bruge en anden instans</a>",
"German (auto-generated)": "Tysk (autogenereret)",
"Spanish (Mexico)": "Spansk (Mexico)",
"generic_count_weeks": "{{count}} uge",
"generic_count_weeks_plural": "{{count}} uger",
"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"
}

@ -1,42 +1,47 @@
{
"`x` subscribers": "`x` Abonnenten",
"`x` videos": "`x` Videos",
"`x` playlists": "`x` Wiedergabelisten",
"LIVE": "LIVE",
"Shared `x` ago": "Vor `x` geteilt",
"Unsubscribe": "Abo beenden",
"Unsubscribe": "Abbestellen",
"Subscribe": "Abonnieren",
"View channel on YouTube": "Kanal auf YouTube anzeigen",
"View playlist on YouTube": "Wiedergabeliste auf YouTube anzeigen",
"newest": "neueste",
"oldest": "älteste",
"popular": "beliebteste",
"last": "neueste",
"popular": "beliebt",
"last": "letzte",
"Next page": "Nächste Seite",
"Previous page": "Vorherige Seite",
"Clear watch history?": "Verlauf löschen?",
"New password": "Neues Passwort",
"New passwords must match": "Neue Passwörter müssen übereinstimmen",
"New passwords must match": "Neue Passwörter müssen gleich sein",
"Cannot change password for Google accounts": "Ich kann das Passwort deines Google Kontos nicht ändern",
"Authorize token?": "Token autorisieren?",
"Authorize token for `x`?": "Token für `x` autorisieren?",
"Yes": "Ja",
"No": "Nein",
"Import and Export Data": "Daten importieren und exportieren",
"Import": "Importieren",
"Import Invidious data": "Invidious-JSON-Daten importieren",
"Import YouTube subscriptions": "YouTube-/OPML-Abonnements importieren",
"Import Invidious data": "Invidious Daten importieren",
"Import YouTube subscriptions": "YouTube Abonnements importieren",
"Import FreeTube subscriptions (.db)": "FreeTube Abonnements importieren (.db)",
"Import NewPipe subscriptions (.json)": "NewPipe Abonnements importieren (.json)",
"Import NewPipe data (.zip)": "NewPipe Daten importieren (.zip)",
"Export": "Exportieren",
"Export subscriptions as OPML": "Abonnements als OPML exportieren",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Abonnements als OPML exportieren (für NewPipe & FreeTube)",
"Export data as JSON": "Invidious-Daten als JSON exportieren",
"Delete account?": "Konto löschen?",
"Export data as JSON": "Daten als JSON exportieren",
"Delete account?": "Account löschen?",
"History": "Verlauf",
"An alternative front-end to YouTube": "Eine alternative Oberfläche für YouTube",
"JavaScript license information": "JavaScript Lizenzinformationen",
"source": "Quelle",
"Log in": "Anmelden",
"Log in/register": "Anmelden/registrieren",
"User ID": "Benutzer-ID",
"Log in": "Einloggen",
"Log in/register": "Einloggen/Registrieren",
"Log in with Google": "Mit Google einloggen",
"User ID": "Benutzer ID",
"Password": "Passwort",
"Time (h:mm:ss):": "Zeit (h:mm:ss):",
"Text CAPTCHA": "Text CAPTCHA",
@ -44,40 +49,37 @@
"Sign In": "Anmelden",
"Register": "Registrieren",
"E-mail": "E-Mail",
"Google verification code": "Google-Bestätigungscode",
"Preferences": "Einstellungen",
"preferences_category_player": "Wiedergabeeinstellungen",
"preferences_video_loop_label": "Immer wiederholen: ",
"preferences_autoplay_label": "Automatisch abspielen: ",
"preferences_continue_label": "Immer automatisch nächstes Video abspielen: ",
"preferences_continue_autoplay_label": "Nächstes Video automatisch abspielen: ",
"preferences_listen_label": "Nur Ton als Standard: ",
"preferences_local_label": "Videos durch Proxy leiten: ",
"preferences_speed_label": "Standardgeschwindigkeit: ",
"preferences_quality_label": "Bevorzugte Videoqualität: ",
"preferences_volume_label": "Wiedergabelautstärke: ",
"preferences_comments_label": "Standardkommentare: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_captions_label": "Standarduntertitel: ",
"Player preferences": "Wiedergabeeinstellungen",
"Always loop: ": "Immer wiederholen: ",
"Autoplay: ": "Automatisch abspielen: ",
"Play next by default: ": "Immer automatisch nächstes Video spielen: ",
"Autoplay next video: ": "nächstes Video automatisch abspielen: ",
"Listen by default: ": "Nur Ton als Standard: ",
"Proxy videos: ": "Proxy-Videos: ",
"Default speed: ": "Standardgeschwindigkeit: ",
"Preferred video quality: ": "Bevorzugte Videoqualität: ",
"Player volume: ": "Wiedergabelautstärke: ",
"Default comments: ": "Standardkommentare: ",
"youtube": "youtube",
"reddit": "reddit",
"Default captions: ": "Standarduntertitel: ",
"Fallback captions: ": "Ersatzuntertitel: ",
"preferences_related_videos_label": "Ähnliche Videos anzeigen: ",
"preferences_annotations_label": "Anmerkungen standardmäßig anzeigen: ",
"preferences_extend_desc_label": "Videobeschreibung automatisch erweitern: ",
"preferences_vr_mode_label": "Interaktive 360-Grad-Videos (erfordert WebGL): ",
"preferences_category_visual": "Anzeigeeinstellungen",
"preferences_player_style_label": "Player-Stil: ",
"Show related videos: ": "Ähnliche Videos anzeigen? ",
"Show annotations by default: ": "Standardmäßig Anmerkungen anzeigen? ",
"Visual preferences": "Anzeigeeinstellungen",
"Player style: ": "Abspielgeräterstil: ",
"Dark mode: ": "Nachtmodus: ",
"preferences_dark_mode_label": "Modus: ",
"Theme: ": "Modus: ",
"dark": "Nachtmodus",
"light": "hell",
"preferences_thin_mode_label": "Schlanker Modus: ",
"preferences_category_misc": "Sonstige Einstellungen",
"preferences_automatic_instance_redirect_label": "Automatische Instanzweiterleitung (über redirect.invidious.io): ",
"preferences_category_subscription": "Abonnementeinstellungen",
"preferences_annotations_subscribed_label": "Anmerkungen für abonnierte Kanäle standardmäßig anzeigen? ",
"light": "klarer Modus",
"Thin mode: ": "Schlanker Modus: ",
"Subscription preferences": "Abonnementeinstellungen",
"Show annotations by default for subscribed channels: ": "Anmerkungen für abonnierte Kanäle standardmäßig anzeigen? ",
"Redirect homepage to feed: ": "Startseite zu Feed umleiten: ",
"preferences_max_results_label": "Anzahl von Videos die im Feed angezeigt werden: ",
"preferences_sort_label": "Videos sortieren nach: ",
"Number of videos shown in feed: ": "Anzahl von Videos die im Feed angezeigt werden: ",
"Sort videos by: ": "Videos sortieren nach: ",
"published": "veröffentlicht",
"published - reverse": "veröffentlicht - invertiert",
"alphabetically": "alphabetisch",
@ -86,43 +88,45 @@
"channel name - reverse": "Kanalname - invertiert",
"Only show latest video from channel: ": "Nur neueste Videos des Kanals anzeigen: ",
"Only show latest unwatched video from channel: ": "Nur neueste ungesehene Videos des Kanals anzeigen: ",
"preferences_unseen_only_label": "Nur ungesehene anzeigen: ",
"preferences_notifications_only_label": "Nur Benachrichtigungen anzeigen (wenn es welche gibt): ",
"Only show unwatched: ": "Nur ungesehene anzeigen: ",
"Only show notifications (if there are any): ": "Nur Benachrichtigungen anzeigen (wenn es welche gibt): ",
"Enable web notifications": "Webbenachrichtigungen aktivieren",
"`x` uploaded a video": "`x` hat ein Video hochgeladen",
"`x` is live": "`x` ist live",
"preferences_category_data": "Dateneinstellungen",
"Data preferences": "Dateneinstellungen",
"Clear watch history": "Verlauf löschen",
"Import/export data": "Daten importieren/exportieren",
"Import/export data": "Daten im-/exportieren",
"Change password": "Passwort ändern",
"Manage subscriptions": "Abonnements verwalten",
"Manage tokens": "Tokens verwalten",
"Watch history": "Verlauf",
"Delete account": "Account löschen",
"preferences_category_admin": "Administrator-Einstellungen",
"preferences_default_home_label": "Standard-Startseite: ",
"preferences_feed_menu_label": "Feed-Menü: ",
"preferences_show_nick_label": "Nutzernamen oben anzeigen: ",
"Administrator preferences": "Administrator-Einstellungen",
"Default homepage: ": "Standard-Startseite: ",
"Feed menu: ": "Feed-Menü: ",
"Top enabled: ": "Top aktiviert? ",
"CAPTCHA enabled: ": "CAPTCHA aktiviert? ",
"Login enabled: ": "Anmeldung aktiviert: ",
"Login enabled: ": "Login aktiviert? ",
"Registration enabled: ": "Registrierung aktiviert? ",
"Report statistics: ": "Statistiken berichten? ",
"Save preferences": "Einstellungen speichern",
"Subscription manager": "Abonnementverwaltung",
"Token manager": "Tokenverwalter",
"Token": "Token",
"`x` subscriptions": "`x` Abonnements",
"`x` tokens": "`x` Tokens",
"Import/export": "Importieren/Exportieren",
"unsubscribe": "abbestellen",
"revoke": "widerrufen",
"Subscriptions": "Abonnements",
"`x` unseen notifications": "`x` ungesehene Benachrichtigungen",
"search": "Suchen",
"Log out": "Abmelden",
"Released under the AGPLv3 on Github.": "Auf GitHub unter der AGPLv3 Lizenz veröffentlicht.",
"Released under the AGPLv3 by Omar Roth.": "Veröffentlicht unter AGPLv3 von Omar Roth.",
"Source available here.": "Quellcode verfügbar hier.",
"View JavaScript license information.": "Javascript Lizenzinformationen anzeigen.",
"View privacy policy.": "Datenschutzerklärung einsehen.",
"Trending": "Angesagt",
"Trending": "Trending",
"Public": "Öffentlich",
"Unlisted": "Nicht aufgeführt",
"Private": "Privat",
@ -134,10 +138,7 @@
"Title": "Titel",
"Playlist privacy": "Vertrauliche Wiedergabeliste",
"Editing playlist `x`": "Wiedergabeliste bearbeiten `x`",
"Show more": "Mehr anzeigen",
"Show less": "Weniger anzeigen",
"Watch on YouTube": "Video auf YouTube ansehen",
"Switch Invidious Instance": "Invidious Instanz wechseln",
"Hide annotations": "Anmerkungen ausblenden",
"Show annotations": "Anmerkungen anzeigen",
"Genre: ": "Genre: ",
@ -148,25 +149,28 @@
"Whitelisted regions: ": "Erlaubte Regionen: ",
"Blacklisted regions: ": "Unerlaubte Regionen: ",
"Shared `x`": "Geteilt `x`",
"`x` views": "`x` Aufrufe",
"Premieres in `x`": "Zuerst gesehen 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",
"View more comments on Reddit": "Mehr Kommentare auf Reddit anzeigen",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` Kommentare anzeigen",
"": "`x` Kommentare anzeigen"
},
"View `x` comments": "`x` Kommentare anzeigen",
"View Reddit comments": "Reddit Kommentare anzeigen",
"Hide replies": "Antworten verstecken",
"Show replies": "Antworten anzeigen",
"Incorrect password": "Falsches Passwort",
"Quota exceeded, try again in a few hours": "Kontingent überschritten, versuche es in ein paar Stunden erneut",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Login nicht möglich, stellen Sie sicher dass two-factor Authentifikation (Authentifizierung oder SMS) aktiviert ist.",
"Invalid TFA code": "Ungültiger TFA Code",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Login fehlgeschlagen. Das kann daran liegen dass two-factor Authentifizierung in ihrem Account nicht aktiviert ist.",
"Wrong answer": "Ungültige Antwort",
"Erroneous CAPTCHA": "Ungültiges CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA ist eine erforderliche Eingabe",
"User ID is a required field": "Benutzer ID ist eine erforderliche Eingabe",
"Password is a required field": "Passwort ist eine erforderliche Eingabe",
"Wrong username or password": "Ungültiger Benutzername oder Passwort",
"Please sign in using 'Log in with Google'": "Bitte melden sie sich mit 'Mit Google anmelden' an",
"Password cannot be empty": "Passwort darf nicht leer sein",
"Password cannot be longer than 55 characters": "Passwort darf nicht länger als 55 Zeichen sein",
"Please log in": "Bitte anmelden",
@ -176,15 +180,17 @@
"This channel does not exist.": "Dieser Kanal existiert nicht.",
"Could not get channel info.": "Kanalinformationen konnten nicht geladen werden.",
"Could not fetch comments": "Kommentare konnten nicht geladen werden",
"View `x` replies": "Zeige `x` Antworten",
"`x` ago": "vor `x`",
"Load more": "Mehr laden",
"`x` points": "`x` Punkte",
"Could not create mix.": "Mix konnte nicht erstellt werden.",
"Empty playlist": "Wiedergabeliste ist leer",
"Not a playlist.": "Ungültige Wiedergabeliste.",
"Playlist does not exist.": "Wiedergabeliste existiert nicht.",
"Could not pull trending pages.": "Trendenz-Seiten konnten nicht geladen werden.",
"Hidden field \"challenge\" is a required field": "Verstecktes Feld „challenge“ ist eine erforderliche Eingabe",
"Hidden field \"token\" is a required field": "Verstecktes Feld „token“ ist eine erforderliche Eingabe",
"Empty playlist": "Playlist ist leer",
"Not a playlist.": "Ungültige Playlist.",
"Playlist does not exist.": "Playlist existiert nicht.",
"Could not pull trending pages.": "Trending Seiten konnten nicht geladen werden.",
"Hidden field \"challenge\" is a required field": "Verstecktes Feld \"challenge\" ist eine erforderliche Eingabe",
"Hidden field \"token\" is a required field": "Verstecktes Feld \"token\" ist eine erforderliche Eingabe",
"Erroneous challenge": "Ungültiger Test",
"Erroneous token": "Ungültiger Token",
"No such user": "Ungültiger Benutzer",
@ -295,13 +301,19 @@
"Yiddish": "Jiddisch",
"Yoruba": "Joruba",
"Zulu": "Zulu",
"`x` years": "`x` Jahre",
"`x` months": "`x` Monate",
"`x` weeks": "`x` Wochen",
"`x` days": "`x` Tage",
"`x` hours": "`x` Stunden",
"`x` minutes": "`x` Minuten",
"`x` seconds": "`x` Sekunden",
"Fallback comments: ": "Alternative Kommentare: ",
"Popular": "Populär",
"Search": "Suchen",
"Top": "Top",
"About": "Über",
"Rating: ": "Bewertung: ",
"preferences_locale_label": "Sprache: ",
"Language: ": "Sprache: ",
"View as playlist": "Als Wiedergabeliste anzeigen",
"Default": "Standard",
"Music": "Musik",
@ -310,170 +322,15 @@
"Movies": "Filme",
"Download": "Herunterladen",
"Download as: ": "Herunterladen als: ",
"%A %B %-d, %Y": "%A %-d %B %Y",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"(edited)": "(editiert)",
"YouTube comment permalink": "YouTube-Kommentar Permalink",
"permalink": "Permalink",
"`x` marked it with a ❤": "`x` markierte es mit einem ❤",
"Audio mode": "Audiomodus",
"Video mode": "Videomodus",
"channel_tab_videos_label": "Videos",
"Videos": "Videos",
"Playlists": "Wiedergabelisten",
"channel_tab_community_label": "Gemeinschaft",
"search_filters_sort_option_relevance": "Relevanz",
"search_filters_sort_option_rating": "Bewertung",
"search_filters_sort_option_date": "Datum",
"search_filters_sort_option_views": "Aufrufe",
"search_filters_type_label": "Inhaltstyp",
"search_filters_duration_label": "Dauer",
"search_filters_features_label": "Eigenschaften",
"search_filters_sort_label": "sortieren",
"search_filters_date_option_hour": "Letzte Stunde",
"search_filters_date_option_today": "Heute",
"search_filters_date_option_week": "Diese Woche",
"search_filters_date_option_month": "Diesen Monat",
"search_filters_date_option_year": "Dieses Jahr",
"search_filters_type_option_video": "Video",
"search_filters_type_option_channel": "Kanal",
"search_filters_type_option_playlist": "Wiedergabeliste",
"search_filters_type_option_movie": "Film",
"search_filters_type_option_show": "Anzeigen",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_subtitles": "Untertitel / 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": "Standort",
"search_filters_features_option_hdr": "HDR",
"Current version: ": "Aktuelle Version: ",
"next_steps_error_message": "Danach folgendes versuchen: ",
"next_steps_error_message_refresh": "Aktualisieren",
"next_steps_error_message_go_to_youtube": "Zu YouTube gehen",
"footer_donate_page": "Spende",
"search_filters_duration_option_long": "Lang (> 20 Minuten)",
"footer_original_source_code": "Original Quellcode",
"footer_modfied_source_code": "Modifizierter Quellcode",
"footer_documentation": "Dokumentation",
"footer_source_code": "Quellcode",
"adminprefs_modified_source_code_url_label": "URL zum Repositorie des modifizierten Quellcodes",
"search_filters_duration_option_short": "Kurz (< 4 Minuten)",
"preferences_region_label": "Land der Inhalte: ",
"preferences_quality_option_dash": "DASH (adaptive Qualität)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Mittel",
"preferences_quality_option_small": "Niedrig",
"preferences_quality_dash_option_auto": "Auto",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_144p": "144p",
"invidious": "Invidious",
"videoinfo_invidious_embed_link": "Link zum Einbetten",
"download_subtitles": "Untertitel - `x` (.vtt)",
"Video unavailable": "Video nicht verfügbar",
"user_created_playlists": "`x` Wiedergabelisten erstellt",
"user_saved_playlists": "`x` Wiedergabelisten gespeichert",
"preferences_save_player_pos_label": "Wiedergabeposition speichern: ",
"search_filters_features_option_three_sixty": "360°",
"preferences_quality_dash_option_best": "Höchste",
"preferences_quality_dash_option_worst": "Niedrigste",
"preferences_quality_dash_option_1440p": "1440p",
"videoinfo_youTube_embed_link": "Eingebettet",
"search_filters_features_option_purchased": "Gekauft",
"none": "keine",
"videoinfo_started_streaming_x_ago": "Stream begann vor `x`",
"videoinfo_watch_on_youTube": "Auf YouTube ansehen",
"preferences_quality_dash_label": "Bevorzugte DASH-Videoqualität: ",
"generic_subscribers_count": "{{count}} Abonnent",
"generic_subscribers_count_plural": "{{count}} Abonnenten",
"generic_videos_count": "{{count}} Video",
"generic_videos_count_plural": "{{count}} Videos",
"subscriptions_unseen_notifs_count": "{{count}} ungesehene Benachrichtung",
"subscriptions_unseen_notifs_count_plural": "{{count}} ungesehene Benachrichtungen",
"crash_page_refresh": "Versucht haben, <a href=\"`x`\">die Seite neu zu laden</a>",
"comments_view_x_replies": "{{count}} Antwort anzeigen",
"comments_view_x_replies_plural": "{{count}} Antworten anzeigen",
"generic_count_years": "{{count}} Jahr",
"generic_count_years_plural": "{{count}} Jahre",
"generic_count_weeks": "{{count}} Woche",
"generic_count_weeks_plural": "{{count}} Wochen",
"generic_count_days": "{{count}} Tag",
"generic_count_days_plural": "{{count}} Tage",
"crash_page_before_reporting": "Bevor Sie einen Bug melden, stellen Sie sicher, dass Sie:",
"crash_page_switch_instance": "Eine <a href=\"`x`\">andere Instanz</a> versucht haben",
"generic_count_hours": "{{count}} Stunde",
"generic_count_hours_plural": "{{count}} Stunden",
"generic_count_minutes": "{{count}} Minute",
"generic_count_minutes_plural": "{{count}} Minuten",
"crash_page_read_the_faq": "Das <a href=\"`x`\">FAQ</a> gelesen haben",
"crash_page_search_issue": "Nach <a href=\"`x`\">bereits gemeldeten Bugs auf GitHub</a> gesucht haben",
"crash_page_report_issue": "Wenn all dies nicht geholfen hat, <a href=\"`x`\">öffnen Sie bitte ein neues Problem (issue) auf Github</a> (vorzugsweise auf Englisch) und fügen Sie den folgenden Text in Ihre Nachricht ein (bitte übersetzen Sie diesen Text NICHT):",
"generic_views_count": "{{count}} Aufruf",
"generic_views_count_plural": "{{count}} Aufrufe",
"generic_count_seconds": "{{count}} Sekunde",
"generic_count_seconds_plural": "{{count}} Sekunden",
"generic_subscriptions_count": "{{count}} Abo",
"generic_subscriptions_count_plural": "{{count}} Abos",
"tokens_count": "{{count}} Token",
"tokens_count_plural": "{{count}} Tokens",
"comments_points_count": "{{count}} Punkt",
"comments_points_count_plural": "{{count}} Punkte",
"crash_page_you_found_a_bug": "Anscheinend haben Sie einen Fehler in Invidious gefunden!",
"generic_count_months": "{{count}} Monat",
"generic_count_months_plural": "{{count}} Monaten",
"Cantonese (Hong Kong)": "Kantonesisch (Hong Kong)",
"Chinese (Hong Kong)": "Chinesisch (Hong Kong)",
"generic_playlists_count": "{{count}} Wiedergabeliste",
"generic_playlists_count_plural": "{{count}} Wiedergabelisten",
"preferences_watch_history_label": "Wiedergabeverlauf aktivieren: ",
"English (United Kingdom)": "Englisch (Vereinigtes Königreich)",
"English (United States)": "Englisch (Vereinigte Staaten)",
"Dutch (auto-generated)": "Niederländisch (automatisch generiert)",
"French (auto-generated)": "Französisch (automatisch generiert)",
"German (auto-generated)": "Deutsch (automatisch generiert)",
"Indonesian (auto-generated)": "Indonesisch (automatisch generiert)",
"Interlingue": "Interlingue",
"Italian (auto-generated)": "Italienisch (automatisch generiert)",
"Japanese (auto-generated)": "Japanisch (automatisch generiert)",
"Spanish (Mexico)": "Spanisch (Mexiko)",
"Spanish (Spain)": "Spanisch (Spanien)",
"Vietnamese (auto-generated)": "Vietnamesisch (automatisch generiert)",
"Russian (auto-generated)": "Russisch (automatisch generiert)",
"Chinese": "Chinesisch",
"Portuguese (Brazil)": "Portugiesisch (Brasilien)",
"Spanish (auto-generated)": "Spanisch (automatisch generiert)",
"Turkish (auto-generated)": "Türkisch (automatisch generiert)",
"Chinese (China)": "Chinesisch (China)",
"Chinese (Taiwan)": "Chinesisch (Taiwan)",
"Korean (auto-generated)": "Koreanisch (automatisch generiert)",
"Portuguese (auto-generated)": "Portugiesisch (automatisch generiert)",
"search_filters_title": "Filtern",
"search_message_change_filters_or_query": "Versuchen Sie, Ihre Suchanfrage zu erweitern und/oder die Filter zu ändern.",
"search_message_use_another_instance": " Sie können auch <a href=\"`x`\">auf einer anderen Instanz suchen</a>.",
"Popular enabled: ": "„Beliebt“-Seite aktiviert: ",
"search_message_no_results": "Keine Ergebnisse gefunden.",
"search_filters_duration_option_medium": "Mittel (4 - 20 Minuten)",
"search_filters_features_option_vr180": "VR180",
"search_filters_type_option_all": "Beliebiger Typ",
"search_filters_apply_button": "Ausgewählte Filter anwenden",
"search_filters_duration_option_none": "Beliebige Länge",
"search_filters_date_label": "Upload-Datum",
"search_filters_date_option_none": "Beliebiges Datum",
"error_video_not_in_playlist": "Das angeforderte Video existiert nicht in dieser Wiedergabeliste. <a href=\"`x`\">Klicken Sie hier, um zur Startseite der Wiedergabeliste zu gelangen.</a>",
"channel_tab_shorts_label": "Shorts",
"channel_tab_streams_label": "Livestreams",
"Music in this video": "Musik in diesem Video",
"Artist: ": "Künstler: ",
"Album: ": "Album: ",
"channel_tab_playlists_label": "Wiedergabelisten",
"channel_tab_channels_label": "Kanäle",
"Channel Sponsor": "Kanalsponsor",
"Standard YouTube license": "Standard YouTube-Lizenz",
"Song: ": "Musik: ",
"Download is disabled": "Herunterladen ist deaktiviert"
}
"Community": "Gemeinschaft",
"Current version: ": "Aktuelle Version: "
}

@ -1,10 +1,19 @@
{
"`x` subscribers": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` συνδρομητής",
"": "`x` συνδρομητές"
},
"`x` videos": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` βίντεο",
"": "`x` βίντεο"
},
"`x` playlists": "",
"LIVE": "ΖΩΝΤΑΝΑ",
"Shared `x` ago": "Μοιράστηκε πριν από `x`",
"Shared `x` ago": "Μοιράστηκε πριν `x`",
"Unsubscribe": "Απεγγραφή",
"Subscribe": "Εγγραφή",
"View channel on YouTube": "Προβολή καναλιού στο YouTube",
"View playlist on YouTube": "Προβολή καταλόγου αναπαραγωγής στο YouTube",
"View playlist on YouTube": "",
"newest": "νεότερα",
"oldest": "παλιότερα",
"popular": "δημοφιλή",
@ -14,21 +23,22 @@
"Clear watch history?": "Διαγραφή ιστορικού προβολής;",
"New password": "Νέος κωδικός πρόσβασης",
"New passwords must match": "Οι νέοι κωδικοί πρόσβασης πρέπει να ταιριάζουν",
"Cannot change password for Google accounts": "Δεν επιτρέπεται η αλλαγή κωδικού πρόσβασης λογαριασμών Google",
"Authorize token?": "Εξουσιοδότηση διασύνδεσης;",
"Authorize token for `x`?": "Εξουσιοδότηση διασύνδεσης με `x`;",
"Yes": "Ναι",
"No": "Όχι",
"Import and Export Data": "Εισαγωγή και Εξαγωγή Δεδομένων",
"Import": "Εισαγωγή",
"Import Invidious data": "Εsαγωγή δεδομένων Invidious JSON",
"Import YouTube subscriptions": "Εισαγωγή συνδρομών YouTube/OPML",
"Import Invidious data": "Εισαγωγή δεδομένων Invidious",
"Import YouTube subscriptions": "Εισαγωγή συνδρομών YouTube",
"Import FreeTube subscriptions (.db)": "Εισαγωγή συνδρομών FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Εισαγωγή συνδρομών NewPipe (.json)",
"Import NewPipe data (.zip)": "Εισαγωγή δεδομένων NewPipe (.zip)",
"Export": "Εξαγωγή",
"Export subscriptions as OPML": "Εξαγωγή συνδρομών ως OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Εξαγωγή συνδρομών ως OPML (για NewPipe & FreeTube)",
"Export data as JSON": "Εξαγωγή δεδομένων Invidious ως JSON",
"Export data as JSON": "Εξαγωγή δεδομένων ως JSON",
"Delete account?": "Διαγραφή λογαριασμού;",
"History": "Ιστορικό",
"An alternative front-end to YouTube": "Μία εναλλακτική πλατφόρμα για το YouTube",
@ -36,6 +46,7 @@
"source": "πηγή",
"Log in": "Σύνδεση",
"Log in/register": "Σύνδεση/εγγραφή",
"Log in with Google": "Σύνδεση με Google",
"User ID": "Ταυτότητα χρήστη",
"Password": "Κωδικός πρόσβασης",
"Time (h:mm:ss):": "Ώρα (ω:λλ:δδ):",
@ -43,37 +54,38 @@
"Image CAPTCHA": "Εικόνα CAPTCHA",
"Sign In": "Σύνδεση",
"Register": "Εγγραφή",
"E-mail": "Ηλεκτρονικό ταχυδρομείο",
"E-mail": "E-mail",
"Google verification code": "Κωδικός επαλήθευσης Google",
"Preferences": "Προτιμήσεις",
"preferences_category_player": "Προτιμήσεις αναπαραγωγής",
"preferences_video_loop_label": "Αυτόματη επανάληψη: ",
"preferences_autoplay_label": "Αυτόματη αναπαραγωγή: ",
"preferences_continue_label": "Αναπαραγωγή επόμενου: ",
"preferences_continue_autoplay_label": "Αυτόματη αναπαραγωγή επόμενου: ",
"preferences_listen_label": "Φόρτωση μόνο ήχου: ",
"preferences_local_label": "Αναπαραγωγή με διακομιστή μεσολάβησης (proxy): ",
"preferences_speed_label": "Προεπιλεγμένη ταχύτητα: ",
"preferences_quality_label": "Προτιμώμενη ανάλυση: ",
"preferences_volume_label": "Ένταση αναπαραγωγής: ",
"preferences_comments_label": "Προεπιλεγμένα σχόλια: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_captions_label": "Προεπιλεγμένοι υπότιτλοι: ",
"Player preferences": "Προτιμήσεις αναπαραγωγής",
"Always loop: ": "Αυτόματη επανάληψη: ",
"Autoplay: ": "Αυτόματη αναπαραγωγή: ",
"Play next by default: ": "Αναπαραγωγή επόμενου: ",
"Autoplay next video: ": "Αυτόματη αναπαραγωγή επόμενου: ",
"Listen by default: ": "Φόρτωση μόνο ήχου: ",
"Proxy videos: ": "Αναπαραγωγή με διακομιστή μεσολάβησης (proxy): ",
"Default speed: ": "Προεπιλεγμένη ταχύτητα: ",
"Preferred video quality: ": "Προτιμώμενη ανάλυση: ",
"Player volume: ": "Ένταση αναπαραγωγής: ",
"Default comments: ": "Προεπιλεγμένα σχόλια: ",
"youtube": "youtube",
"reddit": "reddit",
"Default captions: ": "Προεπιλεγμένοι υπότιτλοι: ",
"Fallback captions: ": "Εναλλακτικοί υπότιτλοι: ",
"preferences_related_videos_label": "Προβολή σχετικών βίντεο; ",
"preferences_annotations_label": "Αυτόματη προβολή σημειώσεων: ",
"preferences_category_visual": "Προτιμήσεις εμφάνισης",
"preferences_player_style_label": "Τεχνοτροπία της συσκευής αναπαραγωγης: ",
"Show related videos: ": "Προβολή σχετικών βίντεο; ",
"Show annotations by default: ": "Αυτόματη προβολή σημειώσεων; :",
"Visual preferences": "Προτιμήσεις εμφάνισης",
"Player style: ": "",
"Dark mode: ": "Σκοτεινή λειτουργία: ",
"preferences_dark_mode_label": "Θέμα: ",
"dark": "σκοτεινό",
"light": "φωτεινό",
"preferences_thin_mode_label": "Ελαφριά λειτουργία: ",
"preferences_category_subscription": "Προτιμήσεις συνδρομών",
"preferences_annotations_subscribed_label": "Προβολή σημειώσεων μόνο για κανάλια στα οποία είστε συνδρομητής; ",
"Theme: ": "",
"dark": "",
"light": "",
"Thin mode: ": "Ελαφριά λειτουργία: ",
"Subscription preferences": "Προτιμήσεις συνδρομών",
"Show annotations by default for subscribed channels: ": "Προβολή σημειώσεων μόνο για κανάλια στα οποία είστε συνδρομητής; ",
"Redirect homepage to feed: ": "Ανακατεύθυνση αρχικής στη ροή συνδρομών: ",
"preferences_max_results_label": "Αριθμός βίντεο ανά σελίδα ροής συνδρομών: ",
"preferences_sort_label": "Ταξινόμηση ανά: ",
"Number of videos shown in feed: ": "Αριθμός βίντεο ανά σελίδα ροής συνδρομών: ",
"Sort videos by: ": "Ταξινόμηση ανά: ",
"published": "ημερομηνία δημοσίευσης",
"published - reverse": "ημερομηνία δημοσίευσης - ανάποδα",
"alphabetically": "αλφαβητικά",
@ -82,12 +94,12 @@
"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` κάνει live",
"preferences_category_data": "Προτιμήσεις δεδομένων",
"Only show unwatched: ": "Προβολή μόνο μη-προβεβλημένων: ",
"Only show notifications (if there are any): ": "Προβολή μόνο ειδοποιήσεων (αν υπάρχουν): ",
"Enable web notifications": "",
"`x` uploaded a video": "",
"`x` is live": "",
"Data preferences": "Προτιμήσεις δεδομένων",
"Clear watch history": "Εκκαθάριση ιστορικού προβολής",
"Import/export data": "Εισαγωγή/εξαγωγή δεδομένων",
"Change password": "Αλλαγή κωδικού πρόσβασης",
@ -95,9 +107,9 @@
"Manage tokens": "Διαχείριση διασυνδέσεων",
"Watch history": "Ιστορικό προβολής",
"Delete account": "Διαγραφή λογαριασμού",
"preferences_category_admin": "Προτιμήσεις διαχειριστή",
"preferences_default_home_label": "Προεπιλεγμένη αρχική: ",
"preferences_feed_menu_label": "Μενού ροής συνδρομών: ",
"Administrator preferences": "Προτιμήσεις διαχειριστή",
"Default homepage: ": "Προεπιλεγμένη αρχική: ",
"Feed menu: ": "Μενού ροής συνδρομών: ",
"Top enabled: ": "Ενεργοποίηση κορυφαίων; ",
"CAPTCHA enabled: ": "Ενεργοποίηση CAPTCHA; ",
"Login enabled: ": "Ενεργοποίηση σύνδεσης; ",
@ -107,57 +119,76 @@
"Subscription manager": "Διαχειριστής συνδρομών",
"Token manager": "Διαχειριστής διασυνδέσεων",
"Token": "Διασύνδεση",
"`x` subscriptions": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` συνδρομή",
"": "`x` συνδρομές"
},
"`x` tokens": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` διασύνδεση",
"": "`x` διασυνδέσεις"
},
"Import/export": "Εισαγωγή/εξαγωγή",
"unsubscribe": "κατάργηση συνδρομής",
"revoke": "ανάκληση",
"Subscriptions": "Συνδρομές",
"`x` unseen notifications": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` καινούρια ειδοποίηση",
"": "`x` καινούριες ειδοποιήσεις"
},
"search": "αναζήτηση",
"Log out": "Αποσύνδεση",
"Released under the AGPLv3 by Omar Roth.": "Κυκλοφορεί υπό την άδεια AGPLv3 από τον Omar Roth.",
"Source available here.": "Προβολή πηγαίου κώδικα εδώ.",
"View JavaScript license information.": "Προβολή πληροφοριών άδειας JavaScript.",
"View privacy policy.": "Προβολή πολιτικής απορρήτου.",
"Trending": "Τάσεις",
"Public": "Δημόσιο",
"Public": "",
"Unlisted": "Κρυφό",
"Private": "Ιδιωτικό",
"View all playlists": "Προβολή όλων των καταλόγων αναπαραγωγής",
"Updated `x` ago": "Ενημερώθηκε πριν από `x`",
"Delete playlist `x`?": "Διαγραφή `x` καταλόγου αναπαραγωγής;",
"Delete playlist": "Διαγραφή καταλόγου αναπαραγωγής",
"Create playlist": "Δημιουργία καταλόγου αναπαραγωγής",
"Title": "Τίτλος",
"Playlist privacy": "Ιδιωτικότητα καταλόγων αναπαραγωγής",
"Editing playlist `x`": "Επεξεργασία `x` καταλόγου αναπαραγωγής",
"Private": "",
"View all playlists": "",
"Updated `x` ago": "",
"Delete playlist `x`?": "",
"Delete playlist": "",
"Create playlist": "",
"Title": "",
"Playlist privacy": "",
"Editing playlist `x`": "",
"Watch on YouTube": "Προβολή στο YouTube",
"Hide annotations": "Απόκρυψη σημειώσεων",
"Show annotations": "Προβολή σημειώσεων",
"Genre: ": "Είδος: ",
"License: ": "Άδεια: ",
"Family friendly? ": "Φιλικό προς την οικογένεια; ",
"Wilson score: ": "Αποτελέσματα Wilson: ",
"Wilson score: ": "Wilson score: ",
"Engagement: ": "Ενδιαφέρον: ",
"Whitelisted regions: ": "Επιτρεπτές περιοχές: ",
"Blacklisted regions: ": "Μη-επιτρεπτές περιοχές: ",
"Shared `x`": "Μοιράστηκε το `x`",
"`x` views": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` προβολή",
"": "`x` προβολές"
},
"Premieres in `x`": "Πρώτη προβολή σε `x`",
"Premieres `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. Πατήστε εδώ για προβολή σχολίων, αλλά έχετε υπ'όψιν σας πως ίσως φορτώσουν πιο αργά.",
"Premieres `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 `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Προβολή `x` σχολίων",
"": "Προβολή `x` σχολίων"
},
"View `x` comments": "Προβολή `x` σχολίων",
"View Reddit comments": "Προβολή σχολίων από το Reddit",
"Hide replies": "Απόκρυψη απαντήσεων",
"Show replies": "Προβολή απαντήσεων",
"Incorrect password": "Λανθασμένος κωδικός πρόσβασης",
"Quota exceeded, try again in a few hours": "Έχετε υπερβεί το όριο προσπαθειών, δοκιμάστε ξανα σε λίγες ώρες",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Αδυναμία σύνδεσης, βεβαιωθείτε πως ο έλεγχος ταυτότητας δύο παραγόντων (με Authenticator ή SMS) είναι ενεργοποιημένος.",
"Invalid TFA code": "Μη έγκυρος κωδικός ελέγχου ταυτότητας δύο παραγόντων",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Αποτυχία σύνδεσης. Ίσως ευθύνεται η έλλειψη ελέγχου ταυτότητας δύο παραγόντων για το λογαριασμό σας.",
"Wrong answer": "Λανθασμένη απάντηση",
"Erroneous CAPTCHA": "Λανθασμένο CAPTCHA",
"CAPTCHA is a required field": "Το CAPTCHA είναι απαιτούμενο πεδίο",
"User ID is a required field": "Η ταυτότητα χρήστη είναι απαιτούμενο πεδίο",
"Password is a required field": "Ο κωδικός πρόσβασης είναι απαιτούμενο πεδίο",
"Wrong username or password": "Λανθασμένο όνομα χρήστη ή κωδικός πρόσβασης",
"Please sign in using 'Log in with Google'": "Συνδεθείτε με την επιλογή 'Σύνδεση με Google'",
"Password cannot be empty": "Ο κωδικός πρόσβασης δεν γίνεται να είναι κενός",
"Password cannot be longer than 55 characters": "Ο κωδικός πρόσβασης δεν γίνεται να υπερβαίνει τους 55 χαρακτήρες",
"Please log in": "Συνδεθείτε",
@ -167,11 +198,19 @@
"This channel does not exist.": "Αυτό το κανάλι δεν υπάρχει.",
"Could not get channel info.": "Αδύναμια εύρεσης πληροφοριών καναλιού.",
"Could not fetch comments": "Αδυναμία λήψης σχολίων",
"View `x` replies": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Προβολή `x` απάντησης",
"": "Προβολή `x` απαντήσεων"
},
"`x` ago": "Πριν `x`",
"Load more": "Φόρτωση περισσότερων",
"`x` points": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` βαθμός",
"": "`x` βαθμοί"
},
"Could not create mix.": "Αδυναμία δημιουργίας μίξης.",
"Empty playlist": "Κενή λίστα αναπαραγωγής",
"Not a playlist.": "Μη έγκυρη λίστα αναπαραγωγής.",
"Not a playlist.": "Μη έγκυρη λίστα αναπαραγωγής",
"Playlist does not exist.": "Μη υπαρκτή λίστα αναπαραγωγής.",
"Could not pull trending pages.": "Αδυναμία λήψης σελίδας τάσεων.",
"Hidden field \"challenge\" is a required field": "Το Κρυφό πεδίο \"δοκιμασία\" είναι απαραίτητο",
@ -286,12 +325,40 @@
"Yiddish": "Γίντις",
"Yoruba": "Γιορούμπα",
"Zulu": "Ζουλού",
"`x` years": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` χρόνο",
"": "`x` χρόνια"
},
"`x` months": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` μήνα",
"": "`x` μήνες"
},
"`x` weeks": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` εβδομάδα",
"": "`x` εβδομάδες"
},
"`x` days": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` ημέρα",
"": "`x` ημέρες"
},
"`x` hours": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` ώρα",
"": "`x` ώρες"
},
"`x` minutes": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` λεπτό",
"": "`x` λεπτά"
},
"`x` seconds": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` δευτερόλεπτο",
"": "`x` δευτερόλεπτα"
},
"Fallback comments: ": "Εναλλακτικά σχόλια: ",
"Popular": "Δημοφιλή",
"Top": "Κορυφαία",
"About": "Σχετικά",
"Rating: ": "Aξιολόγηση: ",
"preferences_locale_label": "Γλώσσα: ",
"Language: ": "Γλώσσα: ",
"View as playlist": "Προβολή ως λίστα αναπαραγωγής",
"Default": "Προεπιλογή",
"Music": "Μουσική",
@ -303,144 +370,12 @@
"%A %B %-d, %Y": "%A %B %-d, %Y",
"(edited)": "(τροποποιημένο)",
"YouTube comment permalink": "Σύνδεσμος YouTube σχολίου",
"permalink": "μόνιμος σύνδεσμος",
"permalink": "",
"`x` marked it with a ❤": "Ο χρηστης `x` έβαλε ❤",
"Audio mode": "Λειτουργία ήχου",
"Video mode": "Λειτουργία βίντεο",
"channel_tab_videos_label": "Βίντεο",
"Videos": "Βίντεο",
"Playlists": "Λίστες Αναπαραγωγής",
"channel_tab_community_label": "Κοινότητα",
"Current version: ": "Τρέχουσα έκδοση: ",
"generic_playlists_count": "{{count}} λίστα αναπαραγωγής",
"generic_playlists_count_plural": "{{count}} λίστες αναπαραγωγής",
"preferences_quality_dash_option_worst": "Χειρότερη",
"preferences_quality_dash_option_2160p": "2160 p",
"Video unavailable": "Το βίντεο δεν είναι διαθέσιμο",
"preferences_quality_dash_option_auto": "Αυτόματη",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_1080p": "1080p",
"comments_view_x_replies": "Προβολή {{count}} απάντησης",
"comments_view_x_replies_plural": "Προβολή {{count}} απαντήσεων",
"crash_page_report_issue": "Εάν κανένα από τα παραπάνω δεν βοήθησε, παρακαλούμε <a href=\"`x`\">ανοίξτε ένα νέο θέμα στο GitHub</a> (κατά προτίμηση στα αγγλικά) και συμπεριλάβετε το ακόλουθο κείμενο στο μήνυμά σας (ΜΗΝ μεταφράζετε αυτό το κείμενο):",
"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}} δευτερόλεπτα",
"preferences_quality_dash_label": "Προτιμώμενη ποιότητα βίντεο DASH: ",
"preferences_quality_dash_option_best": "Καλύτερη",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_144p": "144p",
"generic_subscribers_count": "{{count}} συνδρομητής",
"generic_subscribers_count_plural": "{{count}} συνδρομητές",
"generic_subscriptions_count": "{{count}} συνδρομή",
"generic_subscriptions_count_plural": "{{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}} ημέρες",
"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_search_issue": "αναζητήσει για <a href=\"`x`\">υπάρχοντα θέματα στο GitHub</a>",
"generic_views_count": "{{count}} προβολή",
"generic_views_count_plural": "{{count}} προβολές",
"generic_videos_count": "{{count}} βίντεο",
"generic_videos_count_plural": "{{count}} βίντεο",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Μεσαία",
"preferences_quality_option_small": "Μικρό",
"preferences_quality_option_dash": "DASH (προσαρμόσιμη ποιότητα)",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_720p": "720p",
"invidious": "Invidious",
"preferences_region_label": "Χώρα περιεχομένου: ",
"preferences_category_misc": "Διάφορες προτιμήσεις",
"Show more": "Εμφάνιση περισσότερων",
"search_filters_date_option_today": "Σήμερα",
"search_filters_features_option_three_sixty": "360°",
"videoinfo_started_streaming_x_ago": "Ξεκίνησε η ροή `x` πριν από",
"videoinfo_watch_on_youTube": "Παρακολουθήστε στο YouTube",
"download_subtitles": "Υπότιτλοι - `x` (.vtt)",
"user_created_playlists": "`x` δημιουργημένες λίστες αναπαραγωγής",
"user_saved_playlists": "`x` αποθηκευμένες λίστες αναπαραγωγής",
"search_filters_sort_option_rating": "Αξιολόγηση",
"search_filters_sort_option_relevance": "Συνάφεια",
"search_filters_features_option_purchased": "Αγορασμένο",
"search_filters_sort_option_date": "Ημερομηνία μεταφόρτωσης",
"search_filters_type_label": "Τύπος",
"search_filters_duration_label": "Διάρκεια",
"search_filters_date_option_week": "Αυτή την εβδομάδα",
"search_filters_date_option_year": "Φέτος",
"search_filters_type_option_channel": "Κανάλι",
"search_filters_type_option_playlist": "Λίστα αναπαραγωγής",
"search_filters_duration_option_long": "Μεγάλο (> 20 λεπτά)",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_location": "Τοποθεσία",
"search_filters_features_option_three_d": "3D",
"next_steps_error_message": "Μετά από αυτό θα πρέπει να προσπαθήσετε να: ",
"next_steps_error_message_go_to_youtube": "Μεταβείτε στο YouTube",
"footer_donate_page": "Δωρεά",
"footer_original_source_code": "Πρωτότυπος πηγαίος κώδικας",
"preferences_show_nick_label": "Εμφάνιση ψευδώνυμου στην κορυφή: ",
"search_filters_date_option_hour": "Τελευταία ώρα",
"adminprefs_modified_source_code_url_label": "URL σε αποθετήριο τροποποιημένου πηγαίου κώδικα",
"search_filters_features_option_subtitles": "Υπότιτλοι/CC",
"search_filters_date_option_month": "Αυτόν τον μήνα",
"Released under the AGPLv3 on Github.": "Κυκλοφορεί υπό την AGPLv3 στο GitHub.",
"search_filters_sort_label": "Ταξινόμηση κατά",
"search_filters_type_option_movie": "Ταινία",
"footer_modfied_source_code": "Τροποποιημένος πηγαίος κώδικας",
"search_filters_features_label": "Χαρακτηριστικά",
"search_filters_features_option_four_k": "4K",
"footer_documentation": "Τεκμηρίωση",
"search_filters_duration_option_short": "Σύντομο (< 4 λεπτά)",
"next_steps_error_message_refresh": "Ανανέωση",
"search_filters_type_option_video": "Βίντεο",
"search_filters_features_option_live": "Ζωντανά",
"search_filters_features_option_c_commons": "Creative Commons",
"Search": "Αναζήτηση",
"search_filters_features_option_hdr": "HDR",
"preferences_extend_desc_label": "Αυτόματη επέκταση της περιγραφής του βίντεο: ",
"preferences_vr_mode_label": "Διαδραστικά βίντεο 360 μοιρών (απαιτεί WebGL): ",
"Show less": "Εμφάνιση λιγότερων",
"footer_source_code": "Πηγαίος κώδικας",
"Chinese (Taiwan)": "Κινέζικα (Ταϊβάν)",
"Portuguese (Brazil)": "Πορτογαλικά (Βραζιλία)",
"German (auto-generated)": "Γερμανικά (αυτόματη παραγωγή)",
"Korean (auto-generated)": "Κορεάτικα (αυτόματη παραγωγή)",
"Russian (auto-generated)": "Ρωσικά (αυτόματη παραγωγή)",
"Spanish (auto-generated)": "Ισπανικά (αυτόματη παραγωγή)",
"Vietnamese (auto-generated)": "Βιετναμέζικα (αυτόματη παραγωγή)",
"English (United Kingdom)": "Αγγλικά (Ηνωμένο Βασίλειο)",
"English (United States)": "Αγγλικά (Ηνωμένων Πολιτειών)",
"Cantonese (Hong Kong)": "Καντονέζικα (Χονγκ Κονγκ)",
"Chinese": "Κινεζικά",
"Chinese (China)": "Κινέζικα (Κίνα)",
"Chinese (Hong Kong)": "Κινεζικά (Χονγκ Κονγκ)",
"Dutch (auto-generated)": "Ολαμδικά (αυτόματη παραγωγή)",
"French (auto-generated)": "Γαλλικά (αυτόματη παραγωγή)",
"Interlingue": "Ιντερλίνγκουα",
"Indonesian (auto-generated)": "Ινδονησιακά (αυτόματη παραγωγή)",
"Italian (auto-generated)": "Ιταλικά (αυτόματη παραγωγή)",
"Japanese (auto-generated)": "Ιαπωνικά (αυτόματη παραγωγή)",
"Portuguese (auto-generated)": "Πορτογαλικά (αυτόματη παραγωγή)",
"Spanish (Mexico)": "Ισπανικά (Μεξικό)",
"Spanish (Spain)": "Ισπανικά (Ισπανία)",
"Turkish (auto-generated)": "Τούρκικα (αυτόματη παραγωγή)",
"none": "κανένα",
"videoinfo_youTube_embed_link": "Ενσωμάτωση",
"videoinfo_invidious_embed_link": "Σύνδεσμος Ενσωμάτωσης",
"search_filters_type_option_show": "Μπάρα προόδου διαβάσματος",
"preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: ",
"search_filters_title": "Φίλτρο",
"search_message_no_results": "Δε βρέθηκαν αποτελέσματα."
}
"Community": "",
"Current version: ": "Τρέχουσα έκδοση: "
}

@ -1,14 +1,16 @@
{
"generic_views_count": "{{count}} view",
"generic_views_count_plural": "{{count}} views",
"generic_videos_count": "{{count}} video",
"generic_videos_count_plural": "{{count}} videos",
"generic_playlists_count": "{{count}} playlist",
"generic_playlists_count_plural": "{{count}} playlists",
"generic_subscribers_count": "{{count}} subscriber",
"generic_subscribers_count_plural": "{{count}} subscribers",
"generic_subscriptions_count": "{{count}} subscription",
"generic_subscriptions_count_plural": "{{count}} subscriptions",
"`x` subscribers": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` subscriber",
"": "`x` subscribers"
},
"`x` videos": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` video",
"": "`x` videos"
},
"`x` playlists": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` playlist",
"": "`x` playlists"
},
"LIVE": "LIVE",
"Shared `x` ago": "Shared `x` ago",
"Unsubscribe": "Unsubscribe",
@ -24,22 +26,22 @@
"Clear watch history?": "Clear watch history?",
"New password": "New password",
"New passwords must match": "New passwords must match",
"Cannot change password for Google accounts": "Cannot change password for Google accounts",
"Authorize token?": "Authorize token?",
"Authorize token for `x`?": "Authorize token for `x`?",
"Yes": "Yes",
"No": "No",
"Import and Export Data": "Import and Export Data",
"Import": "Import",
"Import Invidious data": "Import Invidious JSON data",
"Import YouTube subscriptions": "Import YouTube/OPML subscriptions",
"Import YouTube playlist (.csv)": "Import YouTube playlist (.csv)",
"Import Invidious data": "Import Invidious data",
"Import YouTube subscriptions": "Import YouTube subscriptions",
"Import FreeTube subscriptions (.db)": "Import FreeTube subscriptions (.db)",
"Import NewPipe subscriptions (.json)": "Import NewPipe subscriptions (.json)",
"Import NewPipe data (.zip)": "Import NewPipe data (.zip)",
"Export": "Export",
"Export subscriptions as OPML": "Export subscriptions as OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Export subscriptions as OPML (for NewPipe & FreeTube)",
"Export data as JSON": "Export Invidious data as JSON",
"Export data as JSON": "Export data as JSON",
"Delete account?": "Delete account?",
"History": "History",
"An alternative front-end to YouTube": "An alternative front-end to YouTube",
@ -47,6 +49,7 @@
"source": "source",
"Log in": "Log in",
"Log in/register": "Log in/register",
"Log in with Google": "Log in with Google",
"User ID": "User ID",
"Password": "Password",
"Time (h:mm:ss):": "Time (h:mm:ss):",
@ -55,60 +58,37 @@
"Sign In": "Sign In",
"Register": "Register",
"E-mail": "E-mail",
"Google verification code": "Google verification code",
"Preferences": "Preferences",
"preferences_category_player": "Player preferences",
"preferences_video_loop_label": "Always loop: ",
"preferences_autoplay_label": "Autoplay: ",
"preferences_continue_label": "Play next by default: ",
"preferences_continue_autoplay_label": "Autoplay next video: ",
"preferences_listen_label": "Listen by default: ",
"preferences_local_label": "Proxy videos: ",
"preferences_watch_history_label": "Enable watch history: ",
"preferences_speed_label": "Default speed: ",
"preferences_quality_label": "Preferred video quality: ",
"preferences_quality_option_dash": "DASH (adaptive quality)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Medium",
"preferences_quality_option_small": "Small",
"preferences_quality_dash_label": "Preferred DASH video quality: ",
"preferences_quality_dash_option_auto": "Auto",
"preferences_quality_dash_option_best": "Best",
"preferences_quality_dash_option_worst": "Worst",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_144p": "144p",
"preferences_volume_label": "Player volume: ",
"preferences_comments_label": "Default comments: ",
"youtube": "YouTube",
"reddit": "Reddit",
"invidious": "Invidious",
"preferences_captions_label": "Default captions: ",
"Player preferences": "Player preferences",
"Always loop: ": "Always loop: ",
"Autoplay: ": "Autoplay: ",
"Play next by default: ": "Play next by default: ",
"Autoplay next video: ": "Autoplay next video: ",
"Listen by default: ": "Listen by default: ",
"Proxy videos: ": "Proxy videos: ",
"Default speed: ": "Default speed: ",
"Preferred video quality: ": "Preferred video quality: ",
"Player volume: ": "Player volume: ",
"Default comments: ": "Default comments: ",
"youtube": "youtube",
"reddit": "reddit",
"Default captions: ": "Default captions: ",
"Fallback captions: ": "Fallback captions: ",
"preferences_related_videos_label": "Show related videos: ",
"preferences_annotations_label": "Show annotations by default: ",
"preferences_extend_desc_label": "Automatically extend video description: ",
"preferences_vr_mode_label": "Interactive 360 degree videos (requires WebGL): ",
"preferences_category_visual": "Visual preferences",
"preferences_region_label": "Content country: ",
"preferences_player_style_label": "Player style: ",
"Show related videos: ": "Show related videos: ",
"Show annotations by default: ": "Show annotations by default: ",
"Visual preferences": "Visual preferences",
"Player style: ": "Player style: ",
"Dark mode: ": "Dark mode: ",
"preferences_dark_mode_label": "Theme: ",
"Theme: ": "Theme: ",
"dark": "dark",
"light": "light",
"preferences_thin_mode_label": "Thin mode: ",
"preferences_category_misc": "Miscellaneous preferences",
"preferences_automatic_instance_redirect_label": "Automatic instance redirection (fallback to redirect.invidious.io): ",
"preferences_category_subscription": "Subscription preferences",
"preferences_annotations_subscribed_label": "Show annotations by default for subscribed channels? ",
"Thin mode: ": "Thin mode: ",
"Subscription preferences": "Subscription preferences",
"Show annotations by default for subscribed channels: ": "Show annotations by default for subscribed channels? ",
"Redirect homepage to feed: ": "Redirect homepage to feed: ",
"preferences_max_results_label": "Number of videos shown in feed: ",
"preferences_sort_label": "Sort videos by: ",
"Number of videos shown in feed: ": "Number of videos shown in feed: ",
"Sort videos by: ": "Sort videos by: ",
"published": "published",
"published - reverse": "published - reverse",
"alphabetically": "alphabetically",
@ -117,12 +97,12 @@
"channel name - reverse": "channel name - reverse",
"Only show latest video from channel: ": "Only show latest video from channel: ",
"Only show latest unwatched video from channel: ": "Only show latest unwatched video from channel: ",
"preferences_unseen_only_label": "Only show unwatched: ",
"preferences_notifications_only_label": "Only show notifications (if there are any): ",
"Only show unwatched: ": "Only show unwatched: ",
"Only show notifications (if there are any): ": "Only show notifications (if there are any): ",
"Enable web notifications": "Enable web notifications",
"`x` uploaded a video": "`x` uploaded a video",
"`x` is live": "`x` is live",
"preferences_category_data": "Data preferences",
"Data preferences": "Data preferences",
"Clear watch history": "Clear watch history",
"Import/export data": "Import/export data",
"Change password": "Change password",
@ -130,11 +110,9 @@
"Manage tokens": "Manage tokens",
"Watch history": "Watch history",
"Delete account": "Delete account",
"preferences_category_admin": "Administrator preferences",
"preferences_default_home_label": "Default homepage: ",
"preferences_feed_menu_label": "Feed menu: ",
"preferences_show_nick_label": "Show nickname on top: ",
"Popular enabled: ": "Popular enabled: ",
"Administrator preferences": "Administrator preferences",
"Default homepage: ": "Default homepage: ",
"Feed menu: ": "Feed menu: ",
"Top enabled: ": "Top enabled: ",
"CAPTCHA enabled: ": "CAPTCHA enabled: ",
"Login enabled: ": "Login enabled: ",
@ -144,17 +122,25 @@
"Subscription manager": "Subscription manager",
"Token manager": "Token manager",
"Token": "Token",
"tokens_count": "{{count}} token",
"tokens_count_plural": "{{count}} tokens",
"`x` subscriptions": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` subscription",
"": "`x` subscriptions"
},
"`x` tokens": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` token",
"": "`x` tokens"
},
"Import/export": "Import/export",
"unsubscribe": "unsubscribe",
"revoke": "revoke",
"Subscriptions": "Subscriptions",
"subscriptions_unseen_notifs_count": "{{count}} unseen notification",
"subscriptions_unseen_notifs_count_plural": "{{count}} unseen notifications",
"`x` unseen notifications": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` unseen notification",
"": "`x` unseen notifications"
},
"search": "search",
"Log out": "Log out",
"Released under the AGPLv3 on Github.": "Released under the AGPLv3 on GitHub.",
"Released under the AGPLv3 by Omar Roth.": "Released under the AGPLv3 by Omar Roth.",
"Source available here.": "Source available here.",
"View JavaScript license information.": "View JavaScript license information.",
"View privacy policy.": "View privacy policy.",
@ -170,28 +156,21 @@
"Title": "Title",
"Playlist privacy": "Playlist privacy",
"Editing playlist `x`": "Editing playlist `x`",
"Show more": "Show more",
"Show less": "Show less",
"Watch on YouTube": "Watch on YouTube",
"Switch Invidious Instance": "Switch Invidious Instance",
"search_message_no_results": "No results found.",
"search_message_change_filters_or_query": "Try widening your search query and/or changing the filters.",
"search_message_use_another_instance": " You can also <a href=\"`x`\">search on another instance</a>.",
"Hide annotations": "Hide annotations",
"Show annotations": "Show annotations",
"Genre: ": "Genre: ",
"License: ": "License: ",
"Standard YouTube license": "Standard YouTube license",
"Family friendly? ": "Family friendly? ",
"Wilson score: ": "Wilson score: ",
"Engagement: ": "Engagement: ",
"Whitelisted regions: ": "Whitelisted regions: ",
"Blacklisted regions: ": "Blacklisted regions: ",
"Music in this video": "Music in this video",
"Artist: ": "Artist: ",
"Song: ": "Song: ",
"Album: ": "Album: ",
"Shared `x`": "Shared `x`",
"`x` views": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` view",
"": "`x` views"
},
"Premieres in `x`": "Premieres in `x`",
"Premieres `x`": "Premieres `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.": "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.",
@ -205,12 +184,17 @@
"Hide replies": "Hide replies",
"Show replies": "Show replies",
"Incorrect password": "Incorrect password",
"Quota exceeded, try again in a few hours": "Quota exceeded, try again in a few hours",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.",
"Invalid TFA code": "Invalid TFA code",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Login failed. This may be because two-factor authentication is not turned on for your account.",
"Wrong answer": "Wrong answer",
"Erroneous CAPTCHA": "Erroneous CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA is a required field",
"User ID is a required field": "User ID is a required field",
"Password is a required field": "Password is a required field",
"Wrong username or password": "Wrong username or password",
"Please sign in using 'Log in with Google'": "Please sign in using 'Log in with Google'",
"Password cannot be empty": "Password cannot be empty",
"Password cannot be longer than 55 characters": "Password cannot be longer than 55 characters",
"Please log in": "Please log in",
@ -220,12 +204,16 @@
"This channel does not exist.": "This channel does not exist.",
"Could not get channel info.": "Could not get channel info.",
"Could not fetch comments": "Could not fetch comments",
"comments_view_x_replies": "View {{count}} reply",
"comments_view_x_replies_plural": "View {{count}} replies",
"View `x` replies": {
"([^.,0-9]|^)1([^.,0-9]|$)": "View `x` reply",
"": "View `x` replies"
},
"`x` ago": "`x` ago",
"Load more": "Load more",
"comments_points_count": "{{count}} point",
"comments_points_count_plural": "{{count}} points",
"`x` points": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` point",
"": "`x` points"
},
"Could not create mix.": "Could not create mix.",
"Empty playlist": "Empty playlist",
"Not a playlist.": "Not a playlist.",
@ -238,8 +226,6 @@
"No such user": "No such user",
"Token is expired, please try again": "Token is expired, please try again",
"English": "English",
"English (United Kingdom)": "English (United Kingdom)",
"English (United States)": "English (United States)",
"English (auto-generated)": "English (auto-generated)",
"Afrikaans": "Afrikaans",
"Albanian": "Albanian",
@ -253,31 +239,23 @@
"Bosnian": "Bosnian",
"Bulgarian": "Bulgarian",
"Burmese": "Burmese",
"Cantonese (Hong Kong)": "Cantonese (Hong Kong)",
"Catalan": "Catalan",
"Cebuano": "Cebuano",
"Chinese": "Chinese",
"Chinese (China)": "Chinese (China)",
"Chinese (Hong Kong)": "Chinese (Hong Kong)",
"Chinese (Simplified)": "Chinese (Simplified)",
"Chinese (Taiwan)": "Chinese (Taiwan)",
"Chinese (Traditional)": "Chinese (Traditional)",
"Corsican": "Corsican",
"Croatian": "Croatian",
"Czech": "Czech",
"Danish": "Danish",
"Dutch": "Dutch",
"Dutch (auto-generated)": "Dutch (auto-generated)",
"Esperanto": "Esperanto",
"Estonian": "Estonian",
"Filipino": "Filipino",
"Finnish": "Finnish",
"French": "French",
"French (auto-generated)": "French (auto-generated)",
"Galician": "Galician",
"Georgian": "Georgian",
"German": "German",
"German (auto-generated)": "German (auto-generated)",
"Greek": "Greek",
"Gujarati": "Gujarati",
"Haitian Creole": "Haitian Creole",
@ -290,19 +268,14 @@
"Icelandic": "Icelandic",
"Igbo": "Igbo",
"Indonesian": "Indonesian",
"Indonesian (auto-generated)": "Indonesian (auto-generated)",
"Interlingue": "Interlingue",
"Irish": "Irish",
"Italian": "Italian",
"Italian (auto-generated)": "Italian (auto-generated)",
"Japanese": "Japanese",
"Japanese (auto-generated)": "Japanese (auto-generated)",
"Javanese": "Javanese",
"Kannada": "Kannada",
"Kazakh": "Kazakh",
"Khmer": "Khmer",
"Korean": "Korean",
"Korean (auto-generated)": "Korean (auto-generated)",
"Kurdish": "Kurdish",
"Kyrgyz": "Kyrgyz",
"Lao": "Lao",
@ -325,12 +298,9 @@
"Persian": "Persian",
"Polish": "Polish",
"Portuguese": "Portuguese",
"Portuguese (auto-generated)": "Portuguese (auto-generated)",
"Portuguese (Brazil)": "Portuguese (Brazil)",
"Punjabi": "Punjabi",
"Romanian": "Romanian",
"Russian": "Russian",
"Russian (auto-generated)": "Russian (auto-generated)",
"Samoan": "Samoan",
"Scottish Gaelic": "Scottish Gaelic",
"Serbian": "Serbian",
@ -342,10 +312,7 @@
"Somali": "Somali",
"Southern Sotho": "Southern Sotho",
"Spanish": "Spanish",
"Spanish (auto-generated)": "Spanish (auto-generated)",
"Spanish (Latin America)": "Spanish (Latin America)",
"Spanish (Mexico)": "Spanish (Mexico)",
"Spanish (Spain)": "Spanish (Spain)",
"Sundanese": "Sundanese",
"Swahili": "Swahili",
"Swedish": "Swedish",
@ -354,39 +321,50 @@
"Telugu": "Telugu",
"Thai": "Thai",
"Turkish": "Turkish",
"Turkish (auto-generated)": "Turkish (auto-generated)",
"Ukrainian": "Ukrainian",
"Urdu": "Urdu",
"Uzbek": "Uzbek",
"Vietnamese": "Vietnamese",
"Vietnamese (auto-generated)": "Vietnamese (auto-generated)",
"Welsh": "Welsh",
"Western Frisian": "Western Frisian",
"Xhosa": "Xhosa",
"Yiddish": "Yiddish",
"Yoruba": "Yoruba",
"Zulu": "Zulu",
"generic_count_years": "{{count}} year",
"generic_count_years_plural": "{{count}} years",
"generic_count_months": "{{count}} month",
"generic_count_months_plural": "{{count}} months",
"generic_count_weeks": "{{count}} week",
"generic_count_weeks_plural": "{{count}} weeks",
"generic_count_days": "{{count}} day",
"generic_count_days_plural": "{{count}} days",
"generic_count_hours": "{{count}} hour",
"generic_count_hours_plural": "{{count}} hours",
"generic_count_minutes": "{{count}} minute",
"generic_count_minutes_plural": "{{count}} minutes",
"generic_count_seconds": "{{count}} second",
"generic_count_seconds_plural": "{{count}} seconds",
"`x` years": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` year",
"": "`x` years"
},
"`x` months": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` month",
"": "`x` months"
},
"`x` weeks": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` week",
"": "`x` weeks"
},
"`x` days": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` day",
"": "`x` days"
},
"`x` hours": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` hour",
"": "`x` hours"
},
"`x` minutes": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` minute",
"": "`x` minutes"
},
"`x` seconds": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` second",
"": "`x` seconds"
},
"Fallback comments: ": "Fallback comments: ",
"Popular": "Popular",
"Search": "Search",
"Top": "Top",
"About": "About",
"Rating: ": "Rating: ",
"preferences_locale_label": "Language: ",
"Language: ": "Language: ",
"View as playlist": "View as playlist",
"Default": "Default",
"Music": "Music",
@ -395,86 +373,15 @@
"Movies": "Movies",
"Download": "Download",
"Download as: ": "Download as: ",
"Download is disabled": "Download is disabled",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"(edited)": "(edited)",
"YouTube comment permalink": "YouTube comment permalink",
"permalink": "permalink",
"`x` marked it with a ❤": "`x` marked it with a ❤",
"Channel Sponsor": "Channel Sponsor",
"Audio mode": "Audio mode",
"Video mode": "Video mode",
"Videos": "Videos",
"Playlists": "Playlists",
"search_filters_title": "Filters",
"search_filters_date_label": "Upload date",
"search_filters_date_option_none": "Any date",
"search_filters_date_option_hour": "Last Hour",
"search_filters_date_option_today": "Today",
"search_filters_date_option_week": "This week",
"search_filters_date_option_month": "This month",
"search_filters_date_option_year": "This year",
"search_filters_type_label": "Type",
"search_filters_type_option_all": "Any type",
"search_filters_type_option_video": "Video",
"search_filters_type_option_channel": "Channel",
"search_filters_type_option_playlist": "Playlist",
"search_filters_type_option_movie": "Movie",
"search_filters_type_option_show": "Show",
"search_filters_duration_label": "Duration",
"search_filters_duration_option_none": "Any duration",
"search_filters_duration_option_short": "Short (< 4 minutes)",
"search_filters_duration_option_medium": "Medium (4 - 20 minutes)",
"search_filters_duration_option_long": "Long (> 20 minutes)",
"search_filters_features_label": "Features",
"search_filters_features_option_live": "Live",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_subtitles": "Subtitles/CC",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_three_sixty": "360°",
"search_filters_features_option_vr180": "VR180",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_hdr": "HDR",
"search_filters_features_option_location": "Location",
"search_filters_features_option_purchased": "Purchased",
"search_filters_sort_label": "Sort By",
"search_filters_sort_option_relevance": "Relevance",
"search_filters_sort_option_rating": "Rating",
"search_filters_sort_option_date": "Upload Date",
"search_filters_sort_option_views": "View count",
"search_filters_apply_button": "Apply selected filters",
"Current version: ": "Current version: ",
"next_steps_error_message": "After which you should try to: ",
"next_steps_error_message_refresh": "Refresh",
"next_steps_error_message_go_to_youtube": "Go to YouTube",
"footer_donate_page": "Donate",
"footer_documentation": "Documentation",
"footer_source_code": "Source code",
"footer_original_source_code": "Original source code",
"footer_modfied_source_code": "Modified source code",
"adminprefs_modified_source_code_url_label": "URL to modified source code repository",
"none": "none",
"videoinfo_started_streaming_x_ago": "Started streaming `x` ago",
"videoinfo_watch_on_youTube": "Watch on YouTube",
"videoinfo_youTube_embed_link": "Embed",
"videoinfo_invidious_embed_link": "Embed Link",
"download_subtitles": "Subtitles - `x` (.vtt)",
"user_created_playlists": "`x` created playlists",
"user_saved_playlists": "`x` saved playlists",
"Video unavailable": "Video unavailable",
"preferences_save_player_pos_label": "Save playback position: ",
"crash_page_you_found_a_bug": "It looks like you found a bug in Invidious!",
"crash_page_before_reporting": "Before reporting a bug, make sure that you have:",
"crash_page_refresh": "tried to <a href=\"`x`\">refresh the page</a>",
"crash_page_switch_instance": "tried to <a href=\"`x`\">use another instance</a>",
"crash_page_read_the_faq": "read the <a href=\"`x`\">Frequently Asked Questions (FAQ)</a>",
"crash_page_search_issue": "searched for <a href=\"`x`\">existing issues on GitHub</a>",
"crash_page_report_issue": "If none of the above helped, please <a href=\"`x`\">open a new issue on GitHub</a> (preferably in English) and include the following text in your message (do NOT translate that text):",
"error_video_not_in_playlist": "The requested video doesn't exist in this playlist. <a href=\"`x`\">Click here for the playlist home page.</a>",
"channel_tab_videos_label": "Videos",
"channel_tab_shorts_label": "Shorts",
"channel_tab_streams_label": "Livestreams",
"channel_tab_playlists_label": "Playlists",
"channel_tab_community_label": "Community",
"channel_tab_channels_label": "Channels"
}
"Community": "Community",
"Current version: ": "Current version: "
}

@ -1,12 +1,15 @@
{
"`x` subscribers": "`x` abonantoj",
"`x` videos": "`x` filmetoj",
"`x` playlists": "`x` ludlistoj",
"LIVE": "NUNA",
"Shared `x` ago": "Konigita antaŭ `x`",
"Unsubscribe": "Malabonu",
"Subscribe": "Abonu",
"View channel on YouTube": "Vidu kanalon en JuTubo",
"View playlist on YouTube": "Vidu ludliston en JuTubo",
"newest": "plej novaj",
"oldest": "plej malnovaj",
"Unsubscribe": "Malaboni",
"Subscribe": "Aboni",
"View channel on YouTube": "Vidi kanalon en JuTubo",
"View playlist on YouTube": "Vidi ludliston en JuTubo",
"newest": "pli novaj",
"oldest": "pli malnovaj",
"popular": "popularaj",
"last": "lasta",
"Next page": "Sekva paĝo",
@ -14,21 +17,22 @@
"Clear watch history?": "Ĉu forigi vidohistorion?",
"New password": "Nova pasvorto",
"New passwords must match": "Novaj pasvortoj devas kongrui",
"Cannot change password for Google accounts": "Ne eblas ŝanĝi pasvorton por kontoj de Google",
"Authorize token?": "Ĉu rajtigi ĵetonon?",
"Authorize token for `x`?": "Ĉu rajtigi ĵetonon por `x`?",
"Yes": "Jes",
"No": "Ne",
"Import and Export Data": "Importi kaj Eksporti Datumojn",
"Import": "Importi",
"Import Invidious data": "Importi JSON-datumojn de Invidious",
"Import YouTube subscriptions": "Importi abonojn de YouTube/OPML",
"Import Invidious data": "Importi datumojn de Invidious",
"Import YouTube subscriptions": "Importi abonojn de JuTubo",
"Import FreeTube subscriptions (.db)": "Importi abonojn de FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importi abonojn de NewPipe (.json)",
"Import NewPipe data (.zip)": "Importi datumojn de NewPipe (.zip)",
"Export": "Eksporti",
"Export subscriptions as OPML": "Eksporti abonojn kiel OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporti abonojn kiel OPML (por NewPipe kaj FreeTube)",
"Export data as JSON": "Eksporti Invidious-datumojn kiel JSON",
"Export data as JSON": "Eksporti datumojn kiel JSON",
"Delete account?": "Ĉu forigi konton?",
"History": "Historio",
"An alternative front-end to YouTube": "Alternativa fasado al JuTubo",
@ -36,6 +40,7 @@
"source": "fonto",
"Log in": "Ensaluti",
"Log in/register": "Ensaluti/Registriĝi",
"Log in with Google": "Ensaluti al Google",
"User ID": "Uzula identigilo",
"Password": "Pasvorto",
"Time (h:mm:ss):": "Horo (h:mm:ss):",
@ -44,40 +49,37 @@
"Sign In": "Ensaluti",
"Register": "Registriĝi",
"E-mail": "Retpoŝto",
"Google verification code": "Kontrolkodo de Google",
"Preferences": "Agordoj",
"preferences_category_player": "Spektilaj agordoj",
"preferences_video_loop_label": "Ĉiam ripeti: ",
"preferences_autoplay_label": "Aŭtomate ludi: ",
"preferences_continue_label": "Ludi sekvan defaŭlte: ",
"preferences_continue_autoplay_label": "Aŭtomate ludi sekvan filmeton: ",
"preferences_listen_label": "Aŭskulti defaŭlte: ",
"preferences_local_label": "Ĉu uzi prokuran servilon por filmetojn? ",
"preferences_speed_label": "Defaŭlta rapido: ",
"preferences_quality_label": "Preferita filmetkvalito: ",
"preferences_volume_label": "Ludila sonforteco: ",
"preferences_comments_label": "Defaŭltaj komentoj: ",
"Player preferences": "Spektilaj agordoj",
"Always loop: ": "Ĉiam ripeti: ",
"Autoplay: ": "Aŭtomate ludi: ",
"Play next by default: ": "Ludi sekvan defaŭlte: ",
"Autoplay next video: ": "Aŭtomate ludi sekvan filmeton: ",
"Listen by default: ": "Aŭskulti defaŭlte: ",
"Proxy videos: ": "Ĉu uzi prokuran servilon por filmetojn? ",
"Default speed: ": "Defaŭlta rapido: ",
"Preferred video quality: ": "Preferita filmetkvalito: ",
"Player volume: ": "Ludila sonforteco: ",
"Default comments: ": "Defaŭltaj komentoj: ",
"youtube": "JuTubo",
"reddit": "Reddit",
"preferences_captions_label": "Defaŭltaj subtekstoj: ",
"Default captions: ": "Defaŭltaj subtekstoj: ",
"Fallback captions: ": "Retrodefaŭltaj subtekstoj: ",
"preferences_related_videos_label": "Ĉu montri rilatajn filmetojn? ",
"preferences_annotations_label": "Ĉu montri prinotojn defaŭlte? ",
"preferences_extend_desc_label": "Aŭtomate etendi priskribon de filmeto: ",
"preferences_vr_mode_label": "Interagaj 360-gradaj filmoj (postulas WebGL-n): ",
"preferences_category_visual": "Vidaj preferoj",
"preferences_player_style_label": "Ludila stilo: ",
"Show related videos: ": "Ĉu montri rilatajn filmetojn? ",
"Show annotations by default: ": "Ĉu montri prinotojn defaŭlte? ",
"Visual preferences": "Vidaj preferoj",
"Player style: ": "Ludila stilo: ",
"Dark mode: ": "Malhela reĝimo: ",
"preferences_dark_mode_label": "Etoso: ",
"Theme: ": "Etoso: ",
"dark": "malhela",
"light": "hela",
"preferences_thin_mode_label": "Maldika reĝimo: ",
"preferences_category_misc": "Aliaj agordoj",
"preferences_automatic_instance_redirect_label": "Aŭtomata alidirektado de nodo (retropaŝo al redirect.invidious.io): ",
"preferences_category_subscription": "Abonaj agordoj",
"preferences_annotations_subscribed_label": "Ĉu montri prinotojn defaŭlte por abonitaj kanaloj? ",
"Thin mode: ": "Maldika reĝimo: ",
"Subscription preferences": "Abonaj agordoj",
"Show annotations by default for subscribed channels: ": "Ĉu montri prinotojn defaŭlte por abonitaj kanaloj? ",
"Redirect homepage to feed: ": "Alidirekti hejmpâgon al fluo: ",
"preferences_max_results_label": "Nombro da filmetoj montritaj en fluo: ",
"preferences_sort_label": "Ordi filmetojn per: ",
"Number of videos shown in feed: ": "Nombro da filmetoj montritaj en fluo: ",
"Sort videos by: ": "Ordi filmetojn per: ",
"published": "publikigo",
"published - reverse": "publitigo - renverse",
"alphabetically": "alfabete",
@ -86,12 +88,12 @@
"channel name - reverse": "kanala nombro - renverse",
"Only show latest video from channel: ": "Nur montri pli novan filmeton el kanalo: ",
"Only show latest unwatched video from channel: ": "Nur montri pli novan malviditan filmeton el kanalo: ",
"preferences_unseen_only_label": "Nur montri malviditajn: ",
"preferences_notifications_only_label": "Nur montri sciigojn (se estas): ",
"Only show unwatched: ": "Nur montri malviditajn: ",
"Only show notifications (if there are any): ": "Nur montri sciigojn (se estas): ",
"Enable web notifications": "Ebligi retejajn sciigojn",
"`x` uploaded a video": "`x` alŝutis filmeton",
"`x` is live": "`x` estas nuna",
"preferences_category_data": "Datumagordoj",
"Data preferences": "Datumagordoj",
"Clear watch history": "Forigi vidohistorion",
"Import/export data": "Importi/Eksporti datumojn",
"Change password": "Ŝanĝi pasvorton",
@ -99,10 +101,9 @@
"Manage tokens": "Administri ĵetonojn",
"Watch history": "Vidohistorio",
"Delete account": "Forigi konton",
"preferences_category_admin": "Agordoj de administranto",
"preferences_default_home_label": "Defaŭlta hejmpaĝo: ",
"preferences_feed_menu_label": "Flua menuo: ",
"preferences_show_nick_label": "Montri kromnomon supre: ",
"Administrator preferences": "Agordoj de administranto",
"Default homepage: ": "Defaŭlta hejmpaĝo: ",
"Feed menu: ": "Flua menuo: ",
"Top enabled: ": "Ĉu pli bonaj ŝaltitaj? ",
"CAPTCHA enabled: ": "Ĉu CAPTCHA ŝaltita? ",
"Login enabled: ": "Ĉu ensaluto aktivita? ",
@ -112,13 +113,16 @@
"Subscription manager": "Administrilo de abonoj",
"Token manager": "Ĵetona administrilo",
"Token": "Ĵetono",
"`x` subscriptions": "`x` abonoj",
"`x` tokens": "`x` ĵetonoj",
"Import/export": "Importi/Eksporti",
"unsubscribe": "malabonu",
"unsubscribe": "malaboni",
"revoke": "senvalidigi",
"Subscriptions": "Abonoj",
"`x` unseen notifications": "`x` neviditaj sciigoj",
"search": "serĉi",
"Log out": "Elsaluti",
"Released under the AGPLv3 on Github.": "Eldonita sub la AGPLv3 en GitHub.",
"Released under the AGPLv3 by Omar Roth.": "Eldonita sub la AGPLv3 de Omar Roth.",
"Source available here.": "Fonto havebla ĉi tie.",
"View JavaScript license information.": "Vidi Ĝavoskriptan licencan informon.",
"View privacy policy.": "Vidi regularon pri privateco.",
@ -134,10 +138,7 @@
"Title": "Titolo",
"Playlist privacy": "Privateco de ludlisto",
"Editing playlist `x`": "Redaktante ludlisto `x`",
"Show more": "Montri pli",
"Show less": "Montri malpli",
"Watch on YouTube": "Vidi filmeton en JuTubo",
"Switch Invidious Instance": "Ŝanĝi nodon de Indivious",
"Hide annotations": "Kaŝi prinotojn",
"Show annotations": "Montri prinotojn",
"Genre: ": "Ĝenro: ",
@ -148,25 +149,28 @@
"Whitelisted regions: ": "Regionoj listigitaj en blanka listo: ",
"Blacklisted regions: ": "Regionoj listigitaj en nigra listo: ",
"Shared `x`": "Konigita `x`",
"`x` views": "`x` spektaĵoj",
"Premieres in `x`": "Premieras en `x`",
"Premieres `x`": "Premieras `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.": "Saluton! Ŝajnas, ke vi havas Ĝavoskripton malebligitan. Klaku ĉi tie por vidi komentojn, memoru, ke la ŝargado povus daŭri iom pli.",
"View YouTube comments": "Vidi komentojn de JuTubo",
"View more comments on Reddit": "Vidi pli komentoj en Reddit",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Vidi `x` komentojn",
"": "Vidi `x` komentojn"
},
"View `x` comments": "Vidi `x` komentojn",
"View Reddit comments": "Vidi komentojn de Reddit",
"Hide replies": "Kaŝi respondojn",
"Show replies": "Montri respondojn",
"Incorrect password": "Malbona pasvorto",
"Quota exceeded, try again in a few hours": "Kvoto transpasita, provu denove post iuj horoj",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Ne povas ensaluti, certigu, ke dufaktora aŭtentigo (Authenticator aŭ SMS) estas ebligita.",
"Invalid TFA code": "Nevalida TFA-kodo",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Ensalutado fiaskis. Eble ĉar la dufaktora aŭtentigo estas malebligita en via konto.",
"Wrong answer": "Nevalida respondo",
"Erroneous CAPTCHA": "Nevalida CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA estas deviga kampo",
"User ID is a required field": "Uzula identigilo estas deviga kampo",
"Password is a required field": "Pasvorto estas deviga kampo",
"Wrong username or password": "Nevalida uzantnomo aŭ pasvorto",
"Please sign in using 'Log in with Google'": "Bonvolu ensaluti per 'Ensaluti per Google'",
"Password cannot be empty": "Pasvorto ne povas esti malplena",
"Password cannot be longer than 55 characters": "Pasvorto ne povas esti pli longa ol 55 signoj",
"Please log in": "Bonvolu ensaluti",
@ -176,8 +180,10 @@
"This channel does not exist.": "Ĉi tiu kanalo ne ekzistas.",
"Could not get channel info.": "Ne povis havigi kanalan informon.",
"Could not fetch comments": "Ne povis venigi komentojn",
"View `x` replies": "Vidi `x` respondojn",
"`x` ago": "antaŭ `x`",
"Load more": "Ŝarĝi pli",
"`x` points": "`x` poentoj",
"Could not create mix.": "Ne povis krei mikson.",
"Empty playlist": "Ludlisto estas malplena",
"Not a playlist.": "Nevalida ludlisto.",
@ -295,16 +301,22 @@
"Yiddish": "Jida",
"Yoruba": "Joruba",
"Zulu": "Zulua",
"`x` years": "`x` jaroj",
"`x` months": "`x` monatoj",
"`x` weeks": "`x` semajnoj",
"`x` days": "`x` tagoj",
"`x` hours": "`x` horoj",
"`x` minutes": "`x` minutoj",
"`x` seconds": "`x` sekundoj",
"Fallback comments: ": "Retrodefaŭltaj komentoj: ",
"Popular": "Popularaj",
"Search": "Serĉi",
"Top": "Supraj",
"About": "Pri",
"Rating: ": "Takso: ",
"preferences_locale_label": "Lingvo: ",
"Language: ": "Lingvo: ",
"View as playlist": "Vidi kiel ludlisto",
"Default": "Defaŭlte",
"Music": "Muziko",
"Music": "Musiko",
"Gaming": "Komputiloludoj",
"News": "Novaĵoj",
"Movies": "Filmoj",
@ -317,164 +329,8 @@
"`x` marked it with a ❤": "`x` markis ĝin per ❤",
"Audio mode": "Aŭda reĝimo",
"Video mode": "Videa reĝimo",
"channel_tab_videos_label": "Videoj",
"Videos": "Filmetoj",
"Playlists": "Ludlistoj",
"channel_tab_community_label": "Komunumo",
"search_filters_sort_option_relevance": "rilateco",
"search_filters_sort_option_rating": "takso",
"search_filters_sort_option_date": "dato",
"search_filters_sort_option_views": "vidoj",
"search_filters_type_label": "enhavtipo",
"search_filters_duration_label": "daŭro",
"search_filters_features_label": "trajtoj",
"search_filters_sort_label": "ordigi",
"search_filters_date_option_hour": "horo",
"search_filters_date_option_today": "hodiaŭ",
"search_filters_date_option_week": "semajno",
"search_filters_date_option_month": "monato",
"search_filters_date_option_year": "jaro",
"search_filters_type_option_video": "filmeto",
"search_filters_type_option_channel": "kanalo",
"search_filters_type_option_playlist": "ludlisto",
"search_filters_type_option_movie": "filmo",
"search_filters_type_option_show": "spektaĵo",
"search_filters_features_option_hd": "altdistingiva",
"search_filters_features_option_subtitles": "subtekstoj",
"search_filters_features_option_c_commons": "Krea Komunaĵo",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_live": "nuna",
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "loko",
"search_filters_features_option_hdr": "granddinamikgama",
"Current version: ": "Nuna versio: ",
"next_steps_error_message": "Poste, vi provu: ",
"next_steps_error_message_refresh": "Reŝargi",
"next_steps_error_message_go_to_youtube": "Iri al JuTubo",
"search_filters_duration_option_long": "Longa (> 20 minutos)",
"search_filters_duration_option_short": "Mallonga (< 4 minutos)",
"footer_documentation": "Dokumentaro",
"footer_source_code": "Fontkodo",
"adminprefs_modified_source_code_url_label": "URL al modifita deponejo de fontkodo",
"footer_modfied_source_code": "Modifita Fontkodo",
"footer_original_source_code": "Originala fontkodo",
"footer_donate_page": "Donaci",
"preferences_region_label": "Lando de la enhavo: ",
"preferences_quality_dash_label": "Preferata DASH-a videkvalito: ",
"search_filters_title": "Filtri",
"preferences_quality_dash_option_best": "Plej bona",
"preferences_quality_dash_option_worst": "Malplej bona",
"Popular enabled: ": "Populara sekcio ebligita: ",
"search_message_no_results": "Neniu rezulto trovita.",
"search_message_use_another_instance": " Vi ankaŭ povas <a href=\"`x`\">serĉi en alia nodo</a>.",
"tokens_count": "{{count}} ĵetono",
"tokens_count_plural": "{{count}} ĵetonoj",
"subscriptions_unseen_notifs_count": "{{count}} nevidita sciigo",
"subscriptions_unseen_notifs_count_plural": "{{count}} neviditaj sciigoj",
"Indonesian (auto-generated)": "Indonezia (aŭtomate generita)",
"Interlingue": "Interlingvo",
"Italian (auto-generated)": "Itala (aŭtomate generita)",
"Korean (auto-generated)": "Korea (aŭtomate generita)",
"Portuguese (Brazil)": "Portugala (Brazilo)",
"Portuguese (auto-generated)": "Portugala (aŭtomate generita)",
"Russian (auto-generated)": "Rusa (aŭtomate generita)",
"Spanish (Spain)": "Hispana (Hispanio)",
"generic_count_years": "{{count}} jaro",
"generic_count_years_plural": "{{count}} jaroj",
"Turkish (auto-generated)": "Turka (aŭtomate generita)",
"Vietnamese (auto-generated)": "Vjetnama (aŭtomate generita)",
"generic_count_hours": "{{count}} horo",
"generic_count_hours_plural": "{{count}} horoj",
"generic_count_minutes": "{{count}} minuto",
"generic_count_minutes_plural": "{{count}} minutoj",
"search_filters_date_label": "Alŝutdato",
"search_filters_date_option_none": "Ajna dato",
"search_filters_duration_option_medium": "Meza (4 - 20 minutoj)",
"search_filters_features_option_three_sixty": "360º",
"search_filters_features_option_vr180": "VR180",
"user_created_playlists": "`x`kreitaj ludlistoj",
"user_saved_playlists": "`x`konservitaj ludlistoj",
"crash_page_switch_instance": "klopodis <a href=\"`x`\">uzi alian nodon</a>",
"crash_page_read_the_faq": "legis la <a href=\"`x`\">oftajn demandojn</a>",
"error_video_not_in_playlist": "La petita video ne ekzistas en ĉi tiu ludlisto. <a href=\"`x`\">Alklaku ĉi tie por iri al la ludlista hejmpaĝo.</a>",
"crash_page_search_issue": "serĉis por <a href=\"`x`\">ekzistantaj problemoj en GitHub</a>",
"generic_count_seconds": "{{count}} sekundo",
"generic_count_seconds_plural": "{{count}} sekundoj",
"preferences_quality_dash_option_144p": "144p",
"comments_view_x_replies": "Vidi {{count}} respondon",
"comments_view_x_replies_plural": "Vidi {{count}} respondojn",
"preferences_quality_dash_option_360p": "360p",
"invidious": "Invidious",
"Chinese (Taiwan)": "Ĉina (Tajvano)",
"English (United Kingdom)": "Angla (Britio)",
"search_filters_features_option_purchased": "Aĉetita",
"Japanese (auto-generated)": "Japana (aŭtomate generita)",
"search_message_change_filters_or_query": "Provu vastigi vian serĉpeton kaj/aŭ ŝanĝi la filtrilojn.",
"preferences_quality_dash_option_1080p": "1080p",
"generic_count_weeks": "{{count}} semajno",
"generic_count_weeks_plural": "{{count}} semajnoj",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_auto": "Aŭtomate",
"preferences_quality_dash_option_2160p": "2160p",
"English (United States)": "Angla (Usono)",
"Chinese": "Ĉina",
"videoinfo_watch_on_youTube": "Vidi en YouTube",
"crash_page_you_found_a_bug": "Ŝajnas, ke vi trovis eraron en Invidious!",
"comments_points_count": "{{count}} poento",
"comments_points_count_plural": "{{count}} poentoj",
"Cantonese (Hong Kong)": "Kantona (Honkongo)",
"preferences_watch_history_label": "Ebligi vidohistorion: ",
"preferences_quality_option_small": "Eta",
"generic_playlists_count": "{{count}} ludlisto",
"generic_playlists_count_plural": "{{count}} ludlistoj",
"videoinfo_youTube_embed_link": "Enigi",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Meza",
"generic_subscriptions_count": "{{count}} abono",
"generic_subscriptions_count_plural": "{{count}} abonoj",
"videoinfo_started_streaming_x_ago": "Komercis elsendi antaŭ `x`",
"download_subtitles": "Subtitoloj - `x` (.vtt)",
"videoinfo_invidious_embed_link": "Enigi Ligilon",
"crash_page_report_issue": "Se neniu el la antaŭaj agoj helpis, bonvolu <a href=\"`x`\">estigi novan problemon en GitHub</a> (prefere angle) kaj inkludi la jenan tekston en via mesaĝo (NE traduku tiun tekston):",
"preferences_quality_option_dash": "DASH (adapta kvalito)",
"Chinese (Hong Kong)": "Ĉina (Honkongo)",
"Chinese (China)": "Ĉina (Ĉinio)",
"Dutch (auto-generated)": "Nederlanda (aŭtomate generita)",
"German (auto-generated)": "Germana (aŭtomate generita)",
"French (auto-generated)": "Franca (aŭtomate generita)",
"Spanish (Mexico)": "Hispana (Meksiko)",
"Spanish (auto-generated)": "Hispana (aŭtomate generita)",
"generic_count_days": "{{count}} jaro",
"generic_count_days_plural": "{{count}} jaroj",
"search_filters_type_option_all": "Ajna speco",
"search_filters_duration_option_none": "Ajna daŭro",
"search_filters_apply_button": "Uzi elektitajn filtrilojn",
"none": "neniu",
"Video unavailable": "Nedisponebla video",
"crash_page_before_reporting": "Antaŭ ol informi pri eraro certigu, ke vi:",
"crash_page_refresh": "klopodis <a href=\"`x`\">reŝarĝi la paĝon</a>",
"generic_views_count": "{{count}} spekto",
"generic_views_count_plural": "{{count}} spektoj",
"generic_videos_count": "{{count}} video",
"generic_videos_count_plural": "{{count}} videoj",
"generic_subscribers_count": "{{count}} abonanto",
"generic_subscribers_count_plural": "{{count}} abonantoj",
"generic_count_months": "{{count}} monato",
"generic_count_months_plural": "{{count}} monatoj",
"preferences_save_player_pos_label": "Konservi ludadan pozicion: ",
"channel_tab_streams_label": "Tujelsendoj",
"channel_tab_playlists_label": "Ludlistoj",
"channel_tab_channels_label": "Kanaloj",
"channel_tab_shorts_label": "Mallongaj",
"Music in this video": "Muziko en ĉi tiu video",
"Artist: ": "Artisto: ",
"Album: ": "Albumo: ",
"Channel Sponsor": "Kanala sponsoro",
"Song: ": "Muzikaĵo: ",
"Standard YouTube license": "Implicita YouTube-licenco",
"Download is disabled": "Elŝuto estas malebligita",
"Import YouTube playlist (.csv)": "Importi YouTube-ludliston (.csv)"
}
"Community": "Komunumo",
"Current version: ": "Nuna versio: "
}

@ -1,4 +1,7 @@
{
"`x` subscribers": "`x` suscriptores",
"`x` videos": "`x` vídeos",
"`x` playlists": "`x` listas de reproducción",
"LIVE": "DIRECTO",
"Shared `x` ago": "Compartido hace `x`",
"Unsubscribe": "Desuscribirse",
@ -14,21 +17,22 @@
"Clear watch history?": "¿Quiere borrar el historial de reproducción?",
"New password": "Nueva contraseña",
"New passwords must match": "Las nuevas contraseñas deben coincidir",
"Cannot change password for Google accounts": "No se puede cambiar la contraseña de la cuenta de Google",
"Authorize token?": "¿Autorizar el token?",
"Authorize token for `x`?": "¿Autorizar el token para `x`?",
"Yes": "Sí",
"No": "No",
"Import and Export Data": "Importación y exportación de datos",
"Import": "Importar",
"Import Invidious data": "Importar datos JSON de Invidious",
"Import YouTube subscriptions": "Importar suscripciones de YouTube/OPML",
"Import Invidious data": "Importar datos de Invidious",
"Import YouTube subscriptions": "Importar suscripciones de YouTube",
"Import FreeTube subscriptions (.db)": "Importar suscripciones de FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importar suscripciones de NewPipe (.json)",
"Import NewPipe data (.zip)": "Importar datos de NewPipe (.zip)",
"Export": "Exportar",
"Export subscriptions as OPML": "Exportar suscripciones como OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportar suscripciones como OPML (para NewPipe y FreeTube)",
"Export data as JSON": "Exportar datos de Invidious como JSON",
"Export data as JSON": "Exportar datos como JSON",
"Delete account?": "¿Quiere borrar la cuenta?",
"History": "Historial",
"An alternative front-end to YouTube": "Una interfaz alternativa para YouTube",
@ -36,6 +40,7 @@
"source": "código fuente",
"Log in": "Iniciar sesión",
"Log in/register": "Iniciar sesión/Registrarse",
"Log in with Google": "Iniciar sesión en Google",
"User ID": "Nombre",
"Password": "Contraseña",
"Time (h:mm:ss):": "Hora (h:mm:ss):",
@ -44,54 +49,51 @@
"Sign In": "Iniciar sesión",
"Register": "Registrarse",
"E-mail": "Correo",
"Google verification code": "Código de verificación de Google",
"Preferences": "Preferencias",
"preferences_category_player": "Preferencias del reproductor",
"preferences_video_loop_label": "Repetir siempre: ",
"preferences_autoplay_label": "Reproducción automática: ",
"preferences_continue_label": "Reproducir siguiente por defecto: ",
"preferences_continue_autoplay_label": "Reproducir automáticamente el video siguiente: ",
"preferences_listen_label": "Activar el sonido por defecto: ",
"preferences_local_label": "¿Usar un proxy para los videos? ",
"preferences_speed_label": "Velocidad por defecto: ",
"preferences_quality_label": "Calidad de video preferida: ",
"preferences_volume_label": "Volumen del reproductor: ",
"preferences_comments_label": "Comentarios por defecto: ",
"Player preferences": "Preferencias del reproductor",
"Always loop: ": "Repetir siempre: ",
"Autoplay: ": "Reproducción automática: ",
"Play next by default: ": "Reproducir siguiente por defecto: ",
"Autoplay next video: ": "Reproducir automáticamente el vídeo siguiente: ",
"Listen by default: ": "Activar el sonido por defecto: ",
"Proxy videos: ": "¿Usar un proxy para los vídeos? ",
"Default speed: ": "Velocidad por defecto: ",
"Preferred video quality: ": "Calidad de vídeo preferida: ",
"Player volume: ": "Volumen del reproductor: ",
"Default comments: ": "Comentarios por defecto: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_captions_label": "Subtítulos por defecto: ",
"Default captions: ": "Subtítulos por defecto: ",
"Fallback captions: ": "Subtítulos alternativos: ",
"preferences_related_videos_label": "¿Mostrar videos relacionados? ",
"preferences_annotations_label": "¿Mostrar anotaciones por defecto? ",
"preferences_extend_desc_label": "Extender automáticamente la descripción del video: ",
"preferences_vr_mode_label": "Videos interactivos de 360 grados (necesita WebGL): ",
"preferences_category_visual": "Preferencias visuales",
"preferences_player_style_label": "Estilo de reproductor: ",
"Show related videos: ": "¿Mostrar vídeos relacionados? ",
"Show annotations by default: ": "¿Mostrar anotaciones por defecto? ",
"Visual preferences": "Preferencias visuales",
"Player style: ": "Estilo de reproductor: ",
"Dark mode: ": "Modo oscuro: ",
"preferences_dark_mode_label": "Tema: ",
"Theme: ": "Tema: ",
"dark": "oscuro",
"light": "claro",
"preferences_thin_mode_label": "Modo compacto: ",
"preferences_category_misc": "Preferencias misceláneas",
"preferences_automatic_instance_redirect_label": "Redirección automática de instancia (segunda opción a redirect.invidious.io): ",
"preferences_category_subscription": "Preferencias de la suscripción",
"preferences_annotations_subscribed_label": "¿Mostrar anotaciones por defecto para los canales suscritos? ",
"Thin mode: ": "Modo compacto: ",
"Subscription preferences": "Preferencias de la suscripción",
"Show annotations by default for subscribed channels: ": "¿Mostrar anotaciones por defecto para los canales suscritos? ",
"Redirect homepage to feed: ": "Redirigir la página de inicio a la fuente: ",
"preferences_max_results_label": "Número de videos mostrados en la fuente: ",
"preferences_sort_label": "Ordenar los videos por: ",
"Number of videos shown in feed: ": "Número de vídeos mostrados en la fuente: ",
"Sort videos by: ": "Ordenar los vídeos por: ",
"published": "fecha de publicación",
"published - reverse": "fecha de publicación: orden inverso",
"alphabetically": "alfabéticamente",
"alphabetically - reverse": "alfabéticamente: orden inverso",
"channel name": "nombre del canal",
"channel name - reverse": "nombre del canal: orden inverso",
"Only show latest video from channel: ": "Mostrar solo el último video del canal: ",
"Only show latest unwatched video from channel: ": "Mostrar solo el último video sin ver del canal: ",
"preferences_unseen_only_label": "Mostrar solo los no vistos: ",
"preferences_notifications_only_label": "Mostrar solo notificaciones (si hay alguna): ",
"Only show latest video from channel: ": "Mostrar solo el último vídeo del canal: ",
"Only show latest unwatched video from channel: ": "Mostrar solo el último vídeo sin ver del canal: ",
"Only show unwatched: ": "Mostrar solo los no vistos: ",
"Only show notifications (if there are any): ": "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",
"preferences_category_data": "Preferencias de los datos",
"Data preferences": "Preferencias de los datos",
"Clear watch history": "Borrar el historial de reproducción",
"Import/export data": "Importar/Exportar datos",
"Change password": "Cambiar contraseña",
@ -99,10 +101,9 @@
"Manage tokens": "Gestionar tokens",
"Watch history": "Historial de reproducción",
"Delete account": "Borrar cuenta",
"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: ",
"Administrator preferences": "Preferencias de administrador",
"Default homepage: ": "Página de inicio por defecto: ",
"Feed menu: ": "Menú de fuentes: ",
"Top enabled: ": "¿Habilitar los destacados? ",
"CAPTCHA enabled: ": "¿Habilitar los CAPTCHA? ",
"Login enabled: ": "¿Habilitar el inicio de sesión? ",
@ -111,14 +112,17 @@
"Save preferences": "Guardar las preferencias",
"Subscription manager": "Gestor de suscripciones",
"Token manager": "Gestor de tokens",
"Token": "Ficha",
"Token": "Token",
"`x` subscriptions": "`x` suscripciones",
"`x` tokens": "`x` tokens",
"Import/export": "Importar/Exportar",
"unsubscribe": "Desuscribirse",
"revoke": "revocar",
"Subscriptions": "Suscripciones",
"`x` unseen notifications": "`x` notificaciones sin ver",
"search": "buscar",
"Log out": "Cerrar la sesión",
"Released under the AGPLv3 on Github.": "Publicado bajo la AGPLv3 en GitHub.",
"Released under the AGPLv3 by Omar Roth.": "Publicado bajo licencia AGPLv3 por Omar Roth.",
"Source available here.": "Código fuente disponible aquí.",
"View JavaScript license information.": "Ver información de licencia de JavaScript.",
"View privacy policy.": "Ver la política de privacidad.",
@ -128,16 +132,13 @@
"Private": "Privado",
"View all playlists": "Ver todas las listas de reproducción",
"Updated `x` ago": "Actualizado hace `x`",
"Delete playlist `x`?": "¿Borrar la lista de reproducción `x`?",
"Delete playlist": "Borrar lista de reproducción",
"Delete playlist `x`?": "¿Eliminar la lista de reproducción `x`?",
"Delete playlist": "Eliminar lista de reproducción",
"Create playlist": "Crear lista de reproducción",
"Title": "Título",
"Playlist privacy": "Privacidad de la lista de reproducción",
"Editing playlist `x`": "Editando la lista de reproducción 'x'",
"Show more": "Mostrar más",
"Show less": "Mostrar menos",
"Watch on YouTube": "Ver en YouTube",
"Switch Invidious Instance": "Cambiar Instancia de Invidious",
"Watch on YouTube": "Ver el vídeo en Youtube",
"Hide annotations": "Ocultar anotaciones",
"Show annotations": "Mostrar anotaciones",
"Genre: ": "Género: ",
@ -148,27 +149,30 @@
"Whitelisted regions: ": "Regiones permitidas: ",
"Blacklisted regions: ": "Regiones bloqueadas: ",
"Shared `x`": "Compartido `x`",
"`x` views": "`x` visualizaciones",
"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 tiene JavaScript desactivado. Haga clic aquí para ver los comentarios, pero tenga en cuenta que pueden tardar un poco más en cargarse.",
"View YouTube comments": "Ver los comentarios de YouTube",
"View more comments on Reddit": "Ver más comentarios en Reddit",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Ver `x` comentarios",
"": "Ver `x` comentarios"
},
"View `x` comments": "Ver `x` comentarios",
"View Reddit comments": "Ver los comentarios de Reddit",
"Hide replies": "Ocultar las respuestas",
"Show replies": "Mostrar las respuestas",
"Incorrect password": "Contraseña incorrecta",
"Quota exceeded, try again in a few hours": "Cuota excedida, pruebe otra vez en unas horas",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "No se puede iniciar sesión, asegúrese de que la autentificación de dos factores (autentificador o SMS) esté habilitada.",
"Invalid TFA code": "Código TFA no válido",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Error de inicio de sesion. Puede deberse a que la autentificación de dos factores no está habilitada en su cuenta.",
"Wrong answer": "Respuesta no válida",
"Erroneous CAPTCHA": "CAPTCHA no válido",
"CAPTCHA is a required field": "El CAPTCHA es un campo obligatorio",
"User ID is a required field": "El nombre es un campo obligatorio",
"Password is a required field": "La contraseña es un campo obligatorio",
"Wrong username or password": "Nombre o contraseña incorrecto",
"Please sign in using 'Log in with Google'": "Inicie sesión con «Iniciar sesión con Google»",
"Password cannot be empty": "La contraseña no puede estar en blanco",
"Password cannot be longer than 55 characters": "La contraseña no debe tener más de 55 caracteres",
"Password cannot be longer than 55 characters": "La contraseña no puede tener más de 55 caracteres",
"Please log in": "Inicie sesión, por favor",
"Invidious Private Feed for `x`": "Fuente privada de Invidious para `x`",
"channel:`x`": "canal: `x`",
@ -176,8 +180,10 @@
"This channel does not exist.": "El canal no existe.",
"Could not get channel info.": "No se ha podido obtener información del canal.",
"Could not fetch comments": "No se han podido recuperar los comentarios",
"View `x` replies": "Ver `x` respuestas",
"`x` ago": "hace `x`",
"Load more": "Cargar más",
"`x` points": "`x` puntos",
"Could not create mix.": "No se ha podido crear la mezcla.",
"Empty playlist": "La lista de reproducción está vacía",
"Not a playlist.": "Lista de reproducción no válida.",
@ -187,10 +193,10 @@
"Hidden field \"token\" is a required field": "El campo oculto «símbolo» es un campo obligatorio",
"Erroneous challenge": "Desafío no válido",
"Erroneous token": "Símbolo no válido",
"No such user": "Usuario no existe",
"No such user": "Usuario no válido",
"Token is expired, please try again": "El símbolo ha caducado, inténtelo de nuevo",
"English": "Inglés",
"English (auto-generated)": "Inglés (generado automáticamente)",
"English (auto-generated)": "Inglés (autogenerado)",
"Afrikaans": "Afrikáans",
"Albanian": "Albanés",
"Amharic": "Amárico",
@ -295,13 +301,19 @@
"Yiddish": "Yidis",
"Yoruba": "Yoruba",
"Zulu": "Zulú",
"`x` years": "`x` años",
"`x` months": "`x` meses",
"`x` weeks": "`x` semanas",
"`x` days": "`x` días",
"`x` hours": "`x` horas",
"`x` minutes": "`x` minutos",
"`x` seconds": "`x` segundos",
"Fallback comments: ": "Comentarios alternativos: ",
"Popular": "Populares",
"Search": "Buscar",
"Top": "Destacados",
"About": "Acerca de",
"Rating: ": "Valoración: ",
"preferences_locale_label": "Idioma: ",
"Language: ": "Idioma: ",
"View as playlist": "Ver como lista de reproducción",
"Default": "Por defecto",
"Music": "Música",
@ -313,168 +325,12 @@
"%A %B %-d, %Y": "%A %B %-d, %Y",
"(edited)": "(editado)",
"YouTube comment permalink": "Enlace permanente de YouTube del comentario",
"permalink": "enlace permanente",
"permalink": "permalink",
"`x` marked it with a ❤": "`x` lo ha marcado con un ❤",
"Audio mode": "Modo de audio",
"Video mode": "Modo de video",
"channel_tab_videos_label": "Videos",
"Video mode": "Modo de vídeo",
"Videos": "Vídeos",
"Playlists": "Listas de reproducción",
"channel_tab_community_label": "Comunidad",
"search_filters_sort_option_relevance": "Relevancia",
"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_date_option_hour": "Última hora",
"search_filters_date_option_today": "Hoy",
"search_filters_date_option_week": "Esta semana",
"search_filters_date_option_month": "Este mes",
"search_filters_date_option_year": "Este año",
"search_filters_type_option_video": "Video",
"search_filters_type_option_channel": "Canal",
"search_filters_type_option_playlist": "Lista de reproducción",
"search_filters_type_option_movie": "Película",
"search_filters_type_option_show": "Programa",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_subtitles": "Subtítulos",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_live": "En directo",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Ubicación",
"search_filters_features_option_hdr": "HDR",
"Current version: ": "Versión actual: ",
"next_steps_error_message": "Después de lo cual debes intentar: ",
"next_steps_error_message_refresh": "Recargar la página",
"next_steps_error_message_go_to_youtube": "Ir a YouTube",
"search_filters_duration_option_short": "Menos de 4 minutos",
"search_filters_duration_option_medium": "De 4 a 20 minutos",
"search_filters_duration_option_long": "Más de 20 minutos",
"footer_documentation": "Documentación",
"footer_original_source_code": "Código fuente original",
"adminprefs_modified_source_code_url_label": "Enlace al repositorio de código fuente modificado",
"footer_source_code": "Código fuente",
"footer_modfied_source_code": "Código fuente modificado",
"footer_donate_page": "Donar",
"preferences_region_label": "País del contenido: ",
"preferences_quality_dash_label": "Calidad de video DASH preferida: ",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Intermedia",
"preferences_quality_dash_option_auto": "Automática",
"none": "ninguno",
"videoinfo_started_streaming_x_ago": "Comenzó difusión hace `x`",
"download_subtitles": "Subtítulos- `x` (.vtt)",
"user_created_playlists": "`x` listas de reproducción creadas",
"user_saved_playlists": "`x` listas de reproducción guardadas",
"Video unavailable": "Video no disponible",
"videoinfo_youTube_embed_link": "Insertar",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_4320p": "4320p",
"invidious": "Invidious",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_option_dash": "DASH (calidad adaptativa)",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_144p": "144p",
"preferences_quality_option_small": "Pequeña",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_best": "La mejor",
"preferences_quality_dash_option_worst": "La peor",
"videoinfo_invidious_embed_link": "Enlace para Insertar",
"preferences_quality_dash_option_1080p": "1080p",
"search_filters_features_option_purchased": "Comprado",
"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",
"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:",
"English (United States)": "Inglés (Estados Unidos)",
"Cantonese (Hong Kong)": "Cantonés (Hong Kong)",
"Dutch (auto-generated)": "Neerlandés (generados automáticamente)",
"French (auto-generated)": "Francés (generados automáticamente)",
"Interlingue": "Occidental",
"Japanese (auto-generated)": "Japonés (generados automáticamente)",
"Russian (auto-generated)": "Ruso (generados automáticamente)",
"Spanish (Spain)": "Español (España)",
"Vietnamese (auto-generated)": "Vietnamita (generados automáticamente)",
"English (United Kingdom)": "Inglés (Reino Unido)",
"Chinese (Taiwan)": "Chino (Taiwán)",
"German (auto-generated)": "Alemán (generados automáticamente)",
"Italian (auto-generated)": "Italiano (generados automáticamente)",
"Turkish (auto-generated)": "Turco (generados automáticamente)",
"Portuguese (Brazil)": "Portugués (Brasil)",
"Indonesian (auto-generated)": "Indonesio (generados automáticamente)",
"Portuguese (auto-generated)": "Portugués (generados automáticamente)",
"Chinese": "Chino",
"Chinese (Hong Kong)": "Chino (Hong Kong)",
"Chinese (China)": "Chino (China)",
"Korean (auto-generated)": "Coreano (generados automáticamente)",
"Spanish (Mexico)": "Español (Méjico)",
"Spanish (auto-generated)": "Español (generados automáticamente)",
"preferences_watch_history_label": "Habilitar historial de reproducciones: ",
"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_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",
"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>",
"channel_tab_streams_label": "Directos",
"channel_tab_channels_label": "Canales",
"channel_tab_shorts_label": "Cortos",
"channel_tab_playlists_label": "Listas de reproducción",
"Music in this video": "Música en este video",
"Artist: ": "Artista: ",
"Album: ": "Álbum: ",
"Song: ": "Canción: ",
"Channel Sponsor": "Patrocinador del canal",
"Standard YouTube license": "Licencia de YouTube estándar",
"Download is disabled": "La descarga está deshabilitada",
"Import YouTube playlist (.csv)": "Importar lista de reproducción de YouTube (.csv)"
}
"Community": "Comunidad",
"Current version: ": "Versión actual: "
}

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

Loading…
Cancel
Save