Compare commits

...

188 Commits

Author SHA1 Message Date
Emilien
f3540d7e7f Clarify release notes for proxy header stripping 2026-02-07 21:05:48 +01:00
Émilien (perso)
0693f663cd fix comment about memory leaks
Co-authored-by: Fijxu <fijxu@nadeko.net>
2026-02-07 21:04:07 +01:00
Émilien (perso)
35ee713cd8 fix comment about pr #5566, #5338
Co-authored-by: Fijxu <fijxu@nadeko.net>
2026-02-07 21:03:34 +01:00
Emilien
e76e46ad67 Fix release notes for Crystal/OpenSSL 2026-02-07 21:01:52 +01:00
Emilien
e35531f181 Release v2.20260207.0 2026-02-07 20:07:50 +01:00
Fijxu
29c29f7c8d Update src/invidious/routes/routes.cr
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-06 18:50:36 +01:00
Fijxu
067a426235 refactor: Move top level constants to it's own modules 2026-02-06 18:50:36 +01:00
Samantaz Fox
ffd9f4b112 pages/watch: HTML escape 'action' in download widget
Caught in the review of PR 5224, but forgot to click on "send review" in time.
I realized that too late, after the PR was already merged.
2026-02-06 18:44:37 +01:00
Fijxu
cc7cb94095 Document use of unix sockets for db 2026-02-06 18:39:53 +01:00
Fijxu
0ee92e3298 Update src/invidious/routes/before_all.cr
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-06 18:35:20 +01:00
Fijxu
a3a97ccf07 Only generate companion CSP one time to reuse it 2026-02-06 18:35:20 +01:00
ThatMatrix
ce9494133d fix(user/imports.cr): double header removal caused first video to be skipped 2026-02-06 18:33:58 +01:00
ThatMatrix
e4beb00413 fix(user/imports.cr): splitting error fixed 2026-02-06 18:33:58 +01:00
ThatMatrix
050032b188 fix(docker-compose.yml): removed hmac_key (randomly generated) used for testing 2026-02-06 18:33:58 +01:00
ThatMatrix
471857ce8b Fix(user/importers): Fixed typos 2026-02-06 18:33:58 +01:00
ThatMatrix
7be6fbd75c Fix(user/importers): Fixed youtube csv playlist importer 2026-02-06 18:33:58 +01:00
Cameron Radmore
84a699f7b7 Playlist API: return empty author url if ucid is empty (#5618)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
2026-02-05 11:59:27 -03:00
Cameron Radmore
864893f4c7 Channels: parse pronouns and display them on channel page (#5617) 2026-02-05 11:58:52 -03:00
Cameron Radmore
ecbc21b067 playlist: parse playlist thumbnails for topic autogenerated playlists (#5616)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
2026-02-04 12:57:16 -03:00
Fijxu
a9f812799c fix: add missing embedded protobuf message in continuation token for channel videos (#5614)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
* fix: add missing embedded protobuf message in continuation token for channel videos

* fix: add missing embedded protobuf message in continuation token for channel shorts

* fix: add missing embedded protobuf message in continuation token for channel livestreams
2026-02-03 16:18:15 -03:00
Harm133
48be830544 Update shard.yml to include target (#5608)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
[shard.yml]
- Include a target for LSPs to use as an entrypoint:
  (https://github.com/elbywan/crystalline?tab=readme-ov-file#entry-point)
2026-01-30 23:39:07 +01:00
Fijxu
b521e3be6c chore: Do not convert thin_mode preference to string to compare it (#5568) 2026-01-30 18:01:16 -03:00
Fijxu
abb0aa436c Fix thin_mode preference for channel community page (#5567)
thin_mode only took in account the query param because
env.get("preferences").as(Preferences).thin_mode returned a boolean and
not a string to be able to compare it with the string `"true"`
2026-01-30 18:01:04 -03:00
Kiril Isakov
d51a7a44ad Fix commit command in README instructions, as per #5606 (#5607)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
2026-01-23 13:18:41 +01:00
Émilien (perso)
7e36cfb667 Revert "Bump crystallang/crystal from 1.16.3-alpine to 1.19.0-alpine in /dock…" (#5604)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
This reverts commit d25cc9570c.
2026-01-19 23:39:01 +01:00
dependabot[bot]
d25cc9570c Bump crystallang/crystal from 1.16.3-alpine to 1.19.0-alpine in /docker (#5603)
Bumps crystallang/crystal from 1.16.3-alpine to 1.19.0-alpine.

---
updated-dependencies:
- dependency-name: crystallang/crystal
  dependency-version: 1.19.0-alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-19 22:59:44 +01:00
Émilien (perso)
66c67f4c7a doc: Update HTTP proxy configuration comments (#5586)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
* doc: Update HTTP proxy configuration comments

Added information about proxy configuration for YouTube streams.

* Document supported proxy types in config.example.yml

Added note about supported proxy types in configuration.
2026-01-17 00:15:32 +01:00
Fijxu
344bc2d8e9 Strip unwanted headers from response headers in images and videoplayback (#5595)
Image responses contained the following unwanted headers that should not
be passed to the clients:

```
"Cross-Origin-Resource-Policy"
["cross-origin"]
"Cross-Origin-Opener-Policy-Report-Only"
["same-origin; report-to=\"youtube\""]
"Report-To"
["{\"group\":\"youtube\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube\"}]}"]
"Timing-Allow-Origin"
["*"]
```
2026-01-16 19:39:44 -03:00
Fijxu
5f84a5b353 Generate companion check id one time and add missing companion check id on captions (#5575)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
* Only generate companion check id one time

* Add missing check id for companion captions
2025-12-22 17:14:59 +01:00
Fijxu
9603f5151d Downgrade Crystal to 1.16.3 in OCI (#5577)
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
* downgrade to 1.16.3

* Downgrade Alpine base image from 3.23 to 3.22

---------

Co-authored-by: Émilien (perso) <4016501+unixfox@users.noreply.github.com>
2025-12-22 11:19:13 +01:00
Fijxu
f7a31aa3de fix lint
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
2025-12-21 00:50:37 -03:00
Jeroen Boersma
dbbaf51f1f Allow downloading via companion (#5561)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.17.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.18.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
* Allow downloading via companion

* post request where not proxied for the download companion which made
  it impossible to download with the companion enabled

* Re-apply Channel to Channels rename which was pulled in

* Update src/invidious/routes/companion.cr

* doc: better comments for each route

---------

Co-authored-by: Fijxu <fijxu@nadeko.net>
Co-authored-by: Émilien (perso) <4016501+unixfox@users.noreply.github.com>
2025-12-19 15:09:22 +01:00
Émilien (perso)
7a4b901846 chore: update crystal 1.18.2 + alpine 3.23 (#5574) 2025-12-19 15:08:07 +01:00
Fijxu
bf17d53068 Replace deprecated blocking property of Socket (#5538)
* Replace deprecated `blocking` property of `Socket`

This replaces the deprecated argument `blocking` and uses
`Socket.set_blocking(fd, value)` instead.

Fixes a warning in the compiler

https://github.com/crystal-lang/crystal/pull/16033

* Upgrade to upstream

* chore: only Socket.set_blocking for > 1.18

---------

Co-authored-by: Emilien <4016501+unixfox@users.noreply.github.com>
2025-12-19 14:59:42 +01:00
syeopite
1f5685ef92 Reduce indent in StaticAssetsHandler#serve_file 2025-12-19 12:35:00 +01:00
syeopite
21049518d6 Improve cache size check to be more performant
Summing the sizes of each cached file every time is very inefficient.
Instead we can simply store the cache size in an constant and increase
it everytime a file is added into the cache.
2025-12-19 12:35:00 +01:00
syeopite
7f9cfe1aa2 Refactor logic for updating temp files in tests 2025-12-19 12:35:00 +01:00
syeopite
89a0761a19 Fix Ameba Lint/UselessAssign 2025-12-19 12:35:00 +01:00
syeopite
7749ea1956 Isolate static assets handler spec from others
Running `crystal spec` without a file argument essentially produces one
big program that combines every single spec file, their imports, and
the files that those imports themselves depend on. Most of the types
within this combined program will get ignored by the compiler due to a
lack of any calls to them from the spec files.

But for some types, partially the HTTP module ones, using them within
the spec files will suddenly make the compiler enable a bunch of
previously ignored code. And those code will suddenly require the
presence of additional types, constants, etc. This not only make it
annoying for getting the specs working but also makes it difficult to
isolate behaviors for testing.

The `static_assets_handler_spec.cr` causes this issue and so will be
marked as an isolated spec for now. In the future all of the tests
should be organized into independent groupings similar to how the
Crystal compiler splits their tests into std, compiler, primitives and
interpreter.
2025-12-19 12:35:00 +01:00
syeopite
9e482b4807 Add specs for the new StaticAssetsHandler 2025-12-19 12:35:00 +01:00
syeopite
6fd1cb3585 Compare against 1.17.0-dev until full release 2025-12-19 12:35:00 +01:00
syeopite
ddfbed68f7 Simplify StaticAssetsHandler implementation
Overriding `#call` or patching out `serve_file_compressed` provides
only minimal benefits over the ease of maintenance granted by only
overriding what we need to for the caching behavior.
2025-12-19 12:35:00 +01:00
syeopite
d2be57a454 Replace Kemal::StaticFileHandler on Crystal < 1.17.0
Kemal's subclass of the stdlib `HTTP::StaticFileHandler` is not as
maintained as its parent, and so misses out on many enhancements and bug
fixes from upstream, which unfortunately also includes the patches for
security vulnerabilities...

Though this isn't necessarily Kemal's fault since the bulk of the stdlib
handler's logic was done in a single big method, making any changes hard
to maintain. This was fixed in Crystal 1.17.0 where the handler
was refactored into many private methods, making it easier for an
inheriting type to implement custom behaviors while still leveraging
much of the pre-existing code.

Since we don't actually use any of the Kemal specific features added by
`Kemal::StaticFileHandler`, there really isn't a reason to not just
create a new handler based upon the stdlib implementation instead which
will address the problems mentioned above.

This PR implements a new handler which inherits from the stdlib variant
and overrides the helper methods added in Crystal 1.17.0 to add the
caching behavior with minimal code changes. Since this new handler
depends on the code in Crystal 1.17.0, it will only be applied on
versions greater than or equal to 1.17.0. On older versions we'll
fallback to the current monkey patched `Kemal::StaticFileHandler`
2025-12-19 12:35:00 +01:00
Fijxu
eed8f25a3d dockerfile: compile openssl instead of using the one bundled on the crystal alpine image. (#5441)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
* dockerfile: compile openssl instead of using the one bundled on the crystal alpine image.

* fix formatting

* CI: add --no-cache to openssl-builder

* CI: add Dockerfile.arm64 version

* add comment why we compile openssl ourselves

* fix wrong position of comment

* oopsie

* verify openssl checksums

* set nproc for openssl make

* use ARG for openssl sha256 checksum
2025-12-18 10:16:15 +01:00
dependabot[bot]
cf52a35366 Bump actions/cache from 4 to 5 (#5569)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 22:49:01 +01:00
Fijxu
aba31a8e20 Set Kemal max_request_line_size to 16384 for large channel continuation query parameters. (#5566)
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
* feat: Add configurable max_request_line_size to handle long URLs

This commit adds a new configuration option `max_request_line_size` that allows
users to increase the HTTP request line size limit. This is particularly useful
for handling very long continuation tokens that can cause 414 (URI Too Long) errors.

Changes:
- Add `max_request_line_size` property to Config class
- Configure Kemal server to use the custom limit if specified
- Document the option in config.example.yml with recommendations
- Add examples in docker-compose.yml for both YAML and env var configuration

The default behavior remains unchanged (8KB limit) unless explicitly configured.
This provides a solution for users experiencing 414 errors without affecting
existing installations.

* Hardcode max_request_line_size to 16384

---------

Co-authored-by: Sunghyun Kim <hello@sunghyun.me>
2025-12-15 08:21:55 +01:00
Sebastian Hädrich
994c25de2e Add link to GitHub release/tag/commit in footer (#4702)
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
* Add link to GitHub release/tag/commit in footer

* Only show tag if there is one

Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>

---------

Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>
2025-12-14 19:30:52 -03:00
Fijxu
65463333f3 Display "Erroneous CAPTCHA" for invalid captchas (#5508)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
2025-12-11 17:28:20 -03:00
Fijxu
ef2290c1fd Fix channel name overflow (#5553)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
2025-12-06 20:20:42 -03:00
Fijxu
3944d2490c Fix trending page by leaving livestream and gaming trending pages (#5555)
The livestream trending page is now the default.

Adds `content_container = special_category_container["gridRenderer"]?` in the `CategoryRendererParser`
needed for the gaming trending page. The JSON structure of the gaming
trending page looked like this:

```json
"contents": {
 "twoColumnBrowseResultsRenderer": {
  "tabs": [
   {
    "tabRenderer": {
     "selected": true,
     "content": {
      "sectionListRenderer": {
       "contents": [
        {
         "itemSectionRenderer": {
          "contents": [
           {
            "shelfRenderer": {
             "title": {
              "runs": [
               {
                "text": "Trending videos"
               }
              ]
             },
             "content": {
              "gridRenderer": { // <- This was added to the CategoryRendererParser
               "items": [
                {
                 "gridVideoRenderer": {
                  "videoId": "sTWztaLjD20",
                  // More video data
                  // ...
                 }
                }
               ]
              }
             }
            }
           }
          ]
         }
        }
       ]
      }
     }
    }
   }
  ]
 }
}
```

Thanks to
ae2755bf71/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/kiosk/YoutubeTrendingGamingVideosExtractor.java (L11-L13)
for the `browse_id` and `params` needed for the gaming trending page.
2025-12-06 20:19:38 -03:00
Fijxu
a7935bc378 fix: restore dmca_content functionality (#5228)
* fix: restore dmca_content functionality

This restores (or adds) the functionality of the `dmca_content` config
option that at this date, has been unused and makes no effect.

* only disable download widget for dmca video ids
2025-12-06 17:15:25 -03:00
Fijxu
07f3894a71 Remove signature helper completely from Invidious (#5550)
* Remove signature helper completely from Invidious

The official way to reproduce video with Invidious now is by using
Invidious Companion which uses Youtube.JS with a Javascript Interpreter
that can successfully decrypt youtube video URLs.

Sig helper has not been used for a long time, is beyond broken and no
one has plans to fix it and maintain it.

* Remove DECRYPT_FUNCTION and shrink player function

* remove `sp = cfr[sp]`

* Improve message
2025-12-06 16:50:59 -03:00
Fijxu
46a9c933be Fix community posts when there is a unavailable video in a post (#5549)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Posts with a video that has been removed returned
`ProblematicTimelineItem` type which was not taken in account for
community posts.

Now community posts with a broken video will not display an embedded
video.
2025-12-04 12:00:58 -03:00
Fijxu
48765f759d chore: Update shard.yml to use SPDX license identifier (#5552) 2025-12-04 11:59:55 -03:00
Fijxu
35d1d499bc chore: Store preferences in a variable when reused and rename prefs to preferences (#5450)
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
A little code cleanup on places where `preferences` is used more than one time and rename `prefs` to `preferences` to maintain consistency.
2025-12-02 18:20:15 -03:00
Émilien (perso)
b2ecd8abc3 chore: update healthcheck for /api/v1/stats
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
since /api/v1/trending doesn't work anymore
2025-11-25 14:32:15 +01:00
dependabot[bot]
bb9c4a01a1 Bump actions/checkout from 5 to 6
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-25 08:40:27 +01:00
dependabot[bot]
c250b9c0b1 Bump crystal-lang/install-crystal from 1.8.3 to 1.9.1
Some checks failed
Stale issue handler / stale (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
Bumps [crystal-lang/install-crystal](https://github.com/crystal-lang/install-crystal) from 1.8.3 to 1.9.1.
- [Release notes](https://github.com/crystal-lang/install-crystal/releases)
- [Commits](https://github.com/crystal-lang/install-crystal/compare/v1.8.3...v1.9.1)

---
updated-dependencies:
- dependency-name: crystal-lang/install-crystal
  dependency-version: 1.9.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 23:00:49 +01:00
shiny-comic
5cfe294063 Fix 0 view count on related videos section (#5446)
Some checks failed
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Invidious CI / build - crystal: 1.12.2, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.13.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.14.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.15.1, stable: true (push) Has been cancelled
Invidious CI / build - crystal: 1.16.3, stable: true (push) Has been cancelled
Invidious CI / build - crystal: nightly, stable: false (push) Has been cancelled
Invidious CI / Test AMD64 Docker build (push) Has been cancelled
Invidious CI / Test ARM64 Docker build (push) Has been cancelled
* Fix 0 view count on related videos

* Remove view_count variable since it's unused by Innertube

* Remove view_count from specs and API

---------

Co-authored-by: Fijxu <fijxu@nadeko.net>
2025-10-16 21:59:34 -03:00
Fijxu
0c13c4fab1 Prevent timestamp from being set for Livestreams on "Watch on Youtube" links (#5481)
Some checks are pending
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Waiting to run
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Waiting to run
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Waiting to run
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Waiting to run
Invidious CI / lint (push) Waiting to run
2025-10-16 17:32:01 -03:00
Fijxu
fdf0a25b9e Add Livestreams to trending page (#5480) 2025-10-16 17:31:48 -03:00
Fijxu
3226e17953 Fix button overflow (#5452) 2025-10-16 17:31:33 -03:00
dependabot[bot]
710b3f250b Bump crystal-lang/install-crystal from 1.8.2 to 1.8.3
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Bumps [crystal-lang/install-crystal](https://github.com/crystal-lang/install-crystal) from 1.8.2 to 1.8.3.
- [Release notes](https://github.com/crystal-lang/install-crystal/releases)
- [Commits](https://github.com/crystal-lang/install-crystal/compare/v1.8.2...v1.8.3)

---
updated-dependencies:
- dependency-name: crystal-lang/install-crystal
  dependency-version: 1.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-30 19:56:16 +02:00
Alex
42d34cd084 Removed specific section from hyperlink in config.cr
Some checks failed
Invidious CI / lint (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Has been cancelled
2025-09-24 18:54:27 +02:00
Alex
18a8490587 Fixed broken companion hyperlink 2025-09-24 18:54:27 +02:00
syeopite
325e013e0d Prepare for the next release
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
2025-09-13 11:55:10 -07:00
syeopite
cf019e3b45 Release v2.20250913.0 (#5463)
This release primarily marks Invidious companion's ascend out of beta and its stable integration thereof into Invidious!

For those unaware Invidious companion is the successor to the `inv-sig-helper` tool, designed to securely pass YouTube's attestation checks and allow for the efficient retrieval and playback of video streams reliably.

Companion delivers YouTube fixes faster since it’s built on the community-driven [YouTube.js](https://github.com/LuanRT/YouTube.js) project, used by many open source projects such as [FreeTube](https://github.com/FreeTubeApp/FreeTube).

For more information see https://github.com/iv-org/invidious-companion and https://docs.invidious.io/installation/

But companion isn't the only new thing in this release!

Invidious will no longer error out completely as soon as a single item failed to parse in search results, channel pages, etc. Instead it now handles it gracefully by substituting those problematic items with an error card and rendering the page normally.

The player has gained some quality of life features such as being able to choose a default playlist for videos to be added to, or persisting caption appearance settings across the session.

Base Invidious video retrieval without Invidious companion has also been made more stable.

And finally a significant amount of bugs were fixed alongside many other minor improvements.

Co-authored-by: Émilien (perso) <4016501+unixfox@users.noreply.github.com>
2025-09-13 17:44:38 +00:00
Samantaz Fox
c8b4325b4f Translations update from Hosted Weblate (#5313)
Some checks are pending
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Waiting to run
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Waiting to run
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Waiting to run
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Waiting to run
Invidious CI / lint (push) Waiting to run
2025-09-13 14:43:17 +02:00
Hosted Weblate
36086ce083 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:45 +00:00
Hosted Weblate
5405320b96 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:42 +00:00
Hosted Weblate
6ce4717ed0 Update Alemannic translation
Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Update Alemannic translation

Add Alemannic translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Lenny Angst <lenny@familie-angst.ch>
2025-09-12 15:06:37 +00:00
Hosted Weblate
fce446c10e Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:32 +00:00
Hosted Weblate
d047a686a4 Update Serbian (Cyrillic script) translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: NEXI <nexiphotographer@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:28 +00:00
Hosted Weblate
87d3bd0ab8 Update Chinese (Simplified Han script) translation
Update Chinese (Simplified Han script) translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:24 +00:00
Hosted Weblate
a35fa2bd3c Update Chinese (Traditional Han script) translation
Update Chinese (Traditional Han script) translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff Huang <s8321414@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:22 +00:00
Hosted Weblate
4ce4faec13 Update Portuguese (Portugal) translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: ssantos <ssantos@web.de>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:19 +00:00
Hosted Weblate
aa1f8d0e63 Update Slovenian translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Damjan Gerl <damjan@damjan.net>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:16 +00:00
Hosted Weblate
4c57aba1f3 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:12 +00:00
Hosted Weblate
5643cb1c4d Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:09 +00:00
Hosted Weblate
5a1d39683b Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:06 +00:00
Hosted Weblate
8ffbed0d38 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:03 +00:00
Hosted Weblate
6928be1298 Update Serbian translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: NEXI <nexiphotographer@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:06:00 +00:00
Hosted Weblate
39c930145a Update Finnish translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jiri Grönroos <jiri.gronroos@iki.fi>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:58 +00:00
Hosted Weblate
7ab925e45b Update Persian translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Atur <aturaseman9@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:55 +00:00
Hosted Weblate
fc76964c87 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:49 +00:00
Hosted Weblate
2c7e513c63 Update French translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Tristan B <tristanb0@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:46 +00:00
Hosted Weblate
28cf74e321 Update Spanish translation
Update Spanish translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: TransGecko <sigekos824@mongrec.com>
Co-authored-by: Álvaro Alonso Ramírez <aalonsoramirez@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:42 +00:00
Hosted Weblate
692a12336a Update Dutch translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Dick Groskamp <dikgro@yahoo.co.uk>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:36 +00:00
Hosted Weblate
a2d48051e3 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:28 +00:00
Hosted Weblate
928b290fdb Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:21 +00:00
Hosted Weblate
d4b8801bbf Update Arabic translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:16 +00:00
Hosted Weblate
003f462580 Update Italian translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:11 +00:00
Hosted Weblate
0eff8c8cd9 Update Polish translation
Update Polish translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Matthaiks <kitynska@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:05:05 +00:00
Hosted Weblate
deee01fe7b Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:57 +00:00
Hosted Weblate
2e59a50c24 Update Hindi translation
Update Hindi translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Saurmanđal <saurmandal@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:54 +00:00
Hosted Weblate
fb2fcc48d5 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:46 +00:00
Hosted Weblate
1176ac59e5 Update Croatian translation
Update Croatian translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Vid <vid.maricic@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:43 +00:00
Hosted Weblate
22ee5573e3 Update Icelandic translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Sveinn í Felli <sv1@fellsnet.is>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:39 +00:00
Hosted Weblate
1d671b6117 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:34 +00:00
Hosted Weblate
166435b26d Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:30 +00:00
Hosted Weblate
90269a5d09 Update Czech translation
Update Czech translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fjuro <git@alius.cz>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:24 +00:00
Hosted Weblate
019a5cdb7d Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:18 +00:00
Hosted Weblate
9b3c46e74e Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:11 +00:00
Hosted Weblate
5b3cb5268b Update Japanese translation
Update Japanese translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Himmel <himmel@users.noreply.hosted.weblate.org>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: maboroshin <maboroshin@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:04:05 +00:00
Hosted Weblate
f38742e4e7 Update Ukrainian translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:03:59 +00:00
Hosted Weblate
dd03b16ff9 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:03:53 +00:00
Hosted Weblate
5a1e86ddb6 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:03:42 +00:00
Hosted Weblate
abd5bfb7b7 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:03:31 +00:00
Hosted Weblate
2c5a3a9538 Update Russian translation
Update Russian translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Artyom Rybakov <rib.artem@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Yurt Page <yurtpage@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:03:22 +00:00
Hosted Weblate
d68d01315a Update Estonian translation
Update Estonian translation

Update Estonian translation

Update Estonian translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Priit Jõerüüt <hwlate@joeruut.com>
Co-authored-by: Priit Jõerüüt <jrthwlate@users.noreply.hosted.weblate.org>
Co-authored-by: kovabait12 <gamingugandaknuckles@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:03:11 +00:00
Hosted Weblate
f6d2920479 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:59 +00:00
Hosted Weblate
5d7a60ba38 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:45 +00:00
Hosted Weblate
eee9d8441c Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:38 +00:00
Hosted Weblate
0ae262c396 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:29 +00:00
Hosted Weblate
bda898d7fb Update German translation
Update German translation

Update German translation

Update German translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Lenny Angst <lenny@familie-angst.ch>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:22 +00:00
Hosted Weblate
08821d7897 Update Portuguese (Brazil) translation
Update Portuguese (Brazil) translation

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Juzé <dedakir923@exoular.com>
Co-authored-by: joaooliva <joaooliva@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:20 +00:00
Hosted Weblate
f9821d08ee Update Tamil translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:18 +00:00
Hosted Weblate
d38f5d0ab7 Update Turkmen translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hydyr Sopyyew <sopyyewhydyr46@gmail.com>
2025-09-12 15:02:17 +00:00
Hosted Weblate
c28fb22db5 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:14 +00:00
Hosted Weblate
ae75c142d0 Update Latvian translation
Update Latvian translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: ℂ𝕠𝕠𝕠𝕝 (𝕘𝕚𝕥𝕙𝕦𝕓.𝕔𝕠𝕞/ℂ𝕠𝕠𝕠𝕝) <coool@mail.lv>
2025-09-12 15:02:12 +00:00
Hosted Weblate
97783f84c1 Update Turkish translation
Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Bora Atıcı <boratici.acc@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2025-09-12 15:02:11 +00:00
Fijxu
f9cf70f9d7 Add default playlist preference (#5449)
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
* Add default playlist preference

Closes https://github.com/iv-org/invidious/issues/5421

* Add option to set default playlist to none

* Move it to player preferences
2025-09-11 11:05:09 -03:00
Émilien (perso)
14a629a4e8 Better documentation for the specific case public_url with companion 2025-09-11 11:19:40 +02:00
dependabot[bot]
9e160d45d3 Bump actions/stale from 9 to 10
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Has been cancelled
Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9...v10)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: '10'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 23:36:07 +02:00
Fijxu
ba02a4cdf5 Prevent player microformat from being overwritten by the next microformat (#5453)
Some checks are pending
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Waiting to run
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Waiting to run
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Waiting to run
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Waiting to run
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Waiting to run
Invidious CI / lint (push) Waiting to run
* Prevent player microformat from being overwritten by the next microformat

Closes https://github.com/iv-org/invidious/issues/5443

The player microformat is what we need to get the published date,
premiere timestamp, allowed regions and more information of the video.

Youtube introduced a new `microformat.microformatDataRenderer` in the
next endpoint which overwrote the player microformat
`microformat.playerMicroformatRenderer` when merged

* Update src/invidious/videos/parser.cr

Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>

---------

Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>
2025-09-08 17:16:22 -03:00
Emilien
cf2dfbb75d chore: remove debug 2025-09-08 21:34:47 +02:00
Emilien
21c13bba9d chore: use api captions from companion when available 2025-09-08 21:34:47 +02:00
syeopite
5e9d51c06e Refactor FilteredCompressHandler to inherit from stdlib
This changes its behavior to align with the stdlib variant in that
compression is now delayed till the moment that the server begins to
send a response.

This allows the handler to avoid compressing empty responses,and
safeguards against any double compression of content that may occur
if another handler decides to compressi ts response.

This does however come at the drawback(?) of it now removing
`content-length` headers on requests if it exists; since compression
makes the value inaccurate anyway.

See: https://github.com/crystal-lang/crystal/pull/9625
2025-09-08 21:34:47 +02:00
Emilien
1653dd629e fix formatting 2025-09-08 21:34:47 +02:00
Emilien
cba2adc6ef fix csp + progress proxy + allow omit public_url 2025-09-08 21:34:47 +02:00
Emilien
42b955d713 chore: add the suggestions 2025-09-08 21:34:47 +02:00
Emilien
324a416fd4 initial support for base_url with invidious companion + proxy invidious_companion 2025-09-08 21:34:47 +02:00
Fijxu
89c8b1b901 CI: fix wrong if statement for build-docker job (#5442)
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
2025-09-02 16:57:29 +02:00
syeopite
fd8dc93569 Show message when connection to the database is not possible (#5346)
Some checks failed
Build and release container directly from master / release (docker/Dockerfile, AMD64, ubuntu-latest, linux/amd64, ) (push) Has been cancelled
Build and release container directly from master / release (docker/Dockerfile.arm64, ARM64, ubuntu-24.04-arm, linux/arm64/v8, -arm64) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (AMD64, ubuntu-latest) (push) Has been cancelled
Invidious CI / Test ${{ matrix.name }} Docker build (ARM64, ubuntu-24.04-arm) (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
2025-08-23 04:04:06 -07:00
syeopite
67f93e55d8 Fix "ex" variable collision in invidious.cr
The exception handling for database connections results in an
`ex` variable which Ameba sees as overshadowing the `ex` used by the
`ex` block arg used to define the HTTP status code 500 handler below.

Although this is a non-issue since the db connection exception handling
will cause Invidious to exit, Ameba's nature as a static checker means
that it isn't aware of this.

The simplest fix without a dirty ameba ignore comment is to rename `ex`
within the Kemal handler block below, since `ex` within a begin rescue
block is a Crystal convention that will also cause Ameba to raise when
not adhered to.
2025-08-23 03:35:59 -07:00
syeopite
f35f529adc Videos: Fix missing .id to retrieve first playlist video ID (#5366) 2025-08-23 03:30:00 -07:00
syeopite
b32b077a80 Player: Persist caption settings (#5417) 2025-08-23 03:29:07 -07:00
syeopite
6badb80082 Channels: Fix fetching channel playlists (#5418) 2025-08-23 03:26:49 -07:00
syeopite
15099ac1dd Frontend: Fix notification count of TRUE (#5391) 2025-08-23 03:26:11 -07:00
syeopite
adc83f1c09 Documentation: Fix typo (effet -> effect) (#5369) 2025-08-23 03:23:42 -07:00
syeopite
41e0e77d33 HTML: Add Missing Noreferrers (#5368) 2025-08-23 03:23:05 -07:00
syeopite
9ebc76462f Channels: Fix fetching of individual community posts (#5361) 2025-08-23 03:20:04 -07:00
syeopite
0308acb624 Videos: Add fallback to TvSimply client (#5345) 2025-08-23 03:18:41 -07:00
syeopite
cac2397494 YTAPI: Add TvSimply client (#5344) 2025-08-23 03:17:28 -07:00
syeopite
cf640d808e YtAPI: Bump client versions (#5325) 2025-08-23 03:16:55 -07:00
syeopite
80ec027c8f CI: Fix docker ci job not checking if Invidious starts successfully or not (#5306) 2025-08-23 03:16:32 -07:00
syeopite
6f5f0dceca CI: Use public ARM64 Github actions runners for ARM64 builds (#5305) 2025-08-23 03:16:05 -07:00
syeopite
a8ab7b61f7 Player: Add keyboard shortcuts to configure captions (#5188) 2025-08-23 03:15:28 -07:00
Kristian Vos
dd8086e6d9 fix: fetching channel playlists returned 500 error 2025-08-13 15:43:54 +02:00
Eugene Pakhomov
875d8e7e41 Persist caption settings 2025-08-13 14:39:58 +03:00
dependabot[bot]
1ae0f45b0e Bump actions/checkout from 4 to 5 (#5415)
Some checks failed
Build and release container directly from master / release (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.12.2, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.13.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.14.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.15.1, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (1.16.3, true) (push) Has been cancelled
Invidious CI / build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }} (nightly, false) (push) Has been cancelled
Invidious CI / build-docker (push) Has been cancelled
Invidious CI / build-docker-arm64 (push) Has been cancelled
Invidious CI / lint (push) Has been cancelled
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 15:06:16 +02:00
fieryhenry
3335bc8c38 Get a count of 0 if STORAGE_KEY_NOTIF_COUNT is not present in storage
Not sure if this is necessary as I think it should always be present in storage, but just in case it isn't
2025-07-18 19:07:41 +00:00
fieryhenry
a84bb1d22e Fix TRUE number of notifications
`update_ticker_count` used to use STORAGE_KEY_STREAM to get the number of notifications which is a boolean value, now it uses STORAGE_KEY_NOTIF_COUNT which is an integer
2025-07-18 19:02:50 +00:00
epicsam123
24252b836c add back semicolon 2025-06-30 22:38:30 -04:00
Nami Sunami
227c041b86 fix(config.example.yml): Fix typo (effet -> effect) 2025-06-28 11:38:31 +02:00
ChunkyProgrammer
803311713d make sort_by code more legible 2025-06-27 11:38:08 -04:00
epicsam123
64ac3b5203 add missing noreferrers 2025-06-26 18:40:06 -04:00
ChunkyProgrammer
f8febbe2b2 format changes 2025-06-25 23:53:07 -04:00
ChunkyProgrammer
436f955e0f update fetch_community_post_comments protobuf to match currently used protobuf, add sort_by option 2025-06-25 23:34:30 -04:00
ChunkyProgrammer
4155f15bf7 update resolve_url api to better support new post endpoint 2025-06-25 23:33:28 -04:00
ChunkyProgrammer
b9171d9dab Update protobuf for individual community post 2025-06-25 22:35:16 -04:00
ChunkyProgrammer
f3f6937ffc Fix community tab not loading 2025-06-25 22:22:30 -04:00
Fijxu
8723fdca06 Update src/invidious.cr
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2025-06-21 12:02:32 -04:00
Fijxu
d51e1cb051 remove fallback to TV client 2025-06-15 17:45:53 -04:00
Fijxu
cf0a68bd77 store adaptiveFormats data into a variable 2025-06-15 17:43:07 -04:00
Fijxu
8cd9d53fb1 show message when connection to the database is not possible 2025-06-12 18:44:01 -04:00
Fijxu
01cdb384e0 add suggestions from syeopite 2025-06-12 17:25:19 -04:00
Fijxu
b1e7e0c45e replace url by signatureCipher if url is not present 2025-06-12 16:18:01 -04:00
Fijxu
0c96e0977f check for signatureCipher too 2025-06-12 16:07:58 -04:00
Fijxu
37be513e14 Add fallback to TvSimply client 2025-06-12 01:25:59 -04:00
Fijxu
4daf1f0818 Add TvSimply client
Data taken from: 8cf658151f, 689fb0b90e and b15f623dab
2025-06-12 01:24:45 -04:00
Fijxu
09d342b84d Update src/invidious/yt_backend/youtube_api.cr
Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>
2025-05-22 17:55:46 -04:00
Fijxu
3a8d4f333f update IOS_APP_VERSION 2025-05-22 17:17:01 -04:00
Fijxu
97354adf0f Update src/invidious/yt_backend/youtube_api.cr
Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>
2025-05-22 17:15:45 -04:00
Fijxu
6497e1c418 YtAPI: Bump client versions 2025-05-22 16:06:13 -04:00
epicsam123
f9472e4e4b revert format 2025-05-19 22:34:59 -04:00
Fijxu
cc643f209a CI: Fix build-docker job not checking if Invidious starts successfully or not 2025-05-15 19:57:46 -04:00
Fijxu
381074fce1 CI: Replace Dockerfile path depending of the os used 2025-05-15 19:38:21 -04:00
Fijxu
033a44fab5 CI: Also use matrix.docker_compose_file for Run Docker step 2025-05-15 17:58:24 -04:00
Fijxu
a3375e512e CI: Add name attribute to build-docker job 2025-05-15 17:43:03 -04:00
Fijxu
1d664c759f CI: Use matrix for build-docker on ci.yml 2025-05-15 16:33:03 -04:00
Fijxu
94f0a7a9d2 CI: remove --build-arg
Dockerfile and Dockerfile.arm64 already build Invidious without release mode if
`release` argument is not present.
2025-05-15 15:31:17 -04:00
Fijxu
1d2f4b6813 CI: fix typo on comment about the os used on the ARM64 builder 2025-05-15 15:29:24 -04:00
Fijxu
cef0097a30 CI: fix typo on matrix platforms 2025-05-15 15:28:14 -04:00
Fijxu
bef2d7b6b5 CI: Use public ARM64 Github actions runners for ARM64 builds.
Currently, Invidious uses QEMU to build it's ARM64 Invidious image,
which is slow (since we are basically using a virtual machine).

This helps with the speed of building ARM64 binaries for Invidious
on each release/commit.

More information about the public ARM64 runners here:
https://github.com/orgs/community/discussions/148648

CI: Use ARM64 compose file for build-docker-arm64
2025-05-15 01:49:17 -04:00
epicsam123
e67a30b124 formatting 2025-03-20 10:29:26 -04:00
epicsam123
bc3b3f6d69 updated caption features to use videojs interface 2025-03-20 10:09:43 -04:00
epicsam123
73bf956af5 captions: provide "w", "o", "-", "+" keydowns for player from YT 2025-02-19 21:08:45 -05:00
127 changed files with 2496 additions and 1277 deletions

View File

@@ -17,16 +17,26 @@ on:
jobs:
release:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- os: ubuntu-latest
platform: linux/amd64
name: "AMD64"
dockerfile: "docker/Dockerfile"
tag_suffix: ""
# GitHub doesn't have a ubuntu-latest-arm runner
- os: ubuntu-24.04-arm
platform: linux/arm64/v8
name: "ARM64"
dockerfile: "docker/Dockerfile.arm64"
tag_suffix: "-arm64"
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -43,45 +53,22 @@ jobs:
uses: docker/metadata-action@v5
with:
images: quay.io/invidious/invidious
flavor: |
suffix=${{ matrix.tag_suffix }}
tags: |
type=sha,format=short,prefix={{date 'YYYY.MM.DD'}}-,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
type=raw,value=master,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
labels: |
quay.expires-after=12w
- name: Build and push Docker AMD64 image for Push Event
- name: Build and push Docker ${{ matrix.name }} image for Push Event
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
file: ${{ matrix.dockerfile }}
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
push: true
tags: ${{ steps.meta.outputs.tags }}
build-args: |
"release=1"
- name: Docker meta
id: meta-arm64
uses: docker/metadata-action@v5
with:
images: quay.io/invidious/invidious
flavor: |
suffix=-arm64
tags: |
type=sha,format=short,prefix={{date 'YYYY.MM.DD'}}-,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
type=raw,value=master,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
labels: |
quay.expires-after=12w
- name: Build and push Docker ARM64 image for Push Event
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile.arm64
platforms: linux/arm64/v8
labels: ${{ steps.meta-arm64.outputs.labels }}
push: true
tags: ${{ steps.meta-arm64.outputs.tags }}
build-args: |
"release=1"

View File

@@ -8,16 +8,26 @@ on:
jobs:
release:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- os: ubuntu-latest
platform: linux/amd64
name: "AMD64"
dockerfile: "docker/Dockerfile"
tag_suffix: ""
# GitHub doesn't have a ubuntu-latest-arm runner
- os: ubuntu-24.04-arm
platform: linux/arm64/v8
name: "ARM64"
dockerfile: "docker/Dockerfile.arm64"
tag_suffix: "-arm64"
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -36,46 +46,21 @@ jobs:
images: quay.io/invidious/invidious
flavor: |
latest=false
suffix=${{ matrix.tag_suffix }}
tags: |
type=semver,pattern={{version}}
type=raw,value=latest
labels: |
quay.expires-after=12w
- name: Build and push Docker AMD64 image for Push Event
- name: Build and push Docker ${{ matrix.name }} image for Push Event
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
file: ${{ matrix.dockerfile }}
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
push: true
tags: ${{ steps.meta.outputs.tags }}
build-args: |
"release=1"
- name: Docker meta
id: meta-arm64
uses: docker/metadata-action@v5
with:
images: quay.io/invidious/invidious
flavor: |
latest=false
suffix=-arm64
tags: |
type=semver,pattern={{version}}
type=raw,value=latest
labels: |
quay.expires-after=12w
- name: Build and push Docker ARM64 image for Push Event
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile.arm64
platforms: linux/arm64/v8
labels: ${{ steps.meta-arm64.outputs.labels }}
push: true
tags: ${{ steps.meta-arm64.outputs.tags }}
build-args: |
"release=1"

View File

@@ -38,17 +38,17 @@ jobs:
matrix:
stable: [true]
crystal:
- 1.12.2
- 1.13.3
- 1.14.1
- 1.15.1
- 1.16.3
- 1.17.1
- 1.18.2
include:
- crystal: nightly
stable: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: true
@@ -58,12 +58,12 @@ jobs:
shell: bash
- name: Install Crystal
uses: crystal-lang/install-crystal@v1.8.2
uses: crystal-lang/install-crystal@v1.9.1
with:
crystal: ${{ matrix.crystal }}
- name: Cache Shards
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
./lib
@@ -83,46 +83,43 @@ jobs:
run: crystal build --warnings all --error-on-warnings --error-trace src/invidious.cr
build-docker:
strategy:
matrix:
include:
- os: ubuntu-latest
name: "AMD64"
# GitHub doesn't have a ubuntu-latest-arm runner
- os: ubuntu-24.04-arm
name: "ARM64"
runs-on: ubuntu-latest
name: Test ${{ matrix.name }} Docker build
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Use ARM64 Dockerfile if ARM64
if: ${{ matrix.name == 'ARM64' }}
run: sed -i 's/Dockerfile/Dockerfile.arm64/' docker-compose.yml
- name: Build Docker
run: docker compose build --build-arg release=0
run: docker compose build
- name: Change hmac_key on docker-compose.yml
run: sed -i '/hmac_key/s/CHANGE_ME!!/docker-build-hmac-key/' docker-compose.yml
- name: Run Docker
run: docker compose up -d
- name: Test Docker
run: while curl -Isf http://localhost:3000; do sleep 1; done
id: test
run: curl -If http://localhost:3000 --retry 5 --retry-delay 1 --retry-all-errors
build-docker-arm64:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker ARM64 image
uses: docker/build-push-action@v6
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
- name: Print Invidious container logs
# Tells Github Actions to always run this step regardless of whether the previous step has failed
# Without this expression this step would simply be skipped when the previous step fails.
if: success() || steps.test.conclusion == 'failure'
run: docker compose logs
lint:
@@ -131,18 +128,18 @@ jobs:
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: true
- name: Install Crystal
id: lint_step_install_crystal
uses: crystal-lang/install-crystal@v1.8.2
uses: crystal-lang/install-crystal@v1.9.1
with:
crystal: latest
- name: Cache Shards
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
./lib

View File

@@ -10,7 +10,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
- uses: actions/stale@v10
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 730

View File

@@ -1,6 +1,228 @@
# CHANGELOG
## vX.Y.0 (future)
## v2.20260207.0
### Wrap-up
This release hardens the Invidious companion pipeline and cleans up a long list of UI papercuts. Companion downloads now work end-to-end, CSP headers and check identifiers are generated once and reused, proxy responses strip stray headers, and the final traces of the legacy signature helper are gone so the helper can be rolled out safely.
Livestream navigation, playlists, and channel metadata also see overdue fixes: Trending once again lists livestreams, "Watch on YouTube" buttons stop jumping to arbitrary timestamps, playlist imports/API calls handle missing data, and channel pages now display creator pronouns and playlist thumbnails. Deployments benefit from compiling OpenSSL into docker images to mitigate a long-standing memory leak observed with Alpine-provided OpenSSL, Crystal pinned back to 1.16.3 for docker and OCI builds, a rewritten static file handler, clarified README/HTTP proxy/unix socket docs, and dozens of smaller cleanups.
### New features & important changes
#### For Users
- Livestream experiences are restored: Trending shows livestreams again, the gaming feed remains accessible, and "Watch on YouTube" links stop carrying stale timestamps (#5480, #5555, #5481)
- Channel and playlist metadata is richer thanks to pronoun support, topic playlist thumbnails, and accurate related video counts (#5617, #5616, #5446)
- Downloads get smoother because download actions are URL-safe and downloads can flow through Invidious companion when available (#5367, #5561)
- Users see clearer feedback with Erroneous CAPTCHA messages, DMCA controls restored, and a footer link pointing at the current release (#5508, #5228, #4702)
#### For instance owners
- Companion integration is sturdier: CSP is generated once, check identifiers persist, and the helper hyperlink is fixed (#5497, #5575, #5491)
- Proxied images and videoplayback strip unwanted response headers (shared header-strip list) (#5595)
- Runtime and packaging updates pin docker/OCI builds to Crystal 1.16.3, bring an optional Crystal 1.18.2 + Alpine 3.23 image, and compile OpenSSL from source to mitigate the memory leak seen with Alpine-provided OpenSSL (#5604, #5577, #5574, #5441)
- Configuration docs saw polish with unix socket instructions, refreshed HTTP proxy comments, and corrected README commands (#5347, #5586, #5607)
- Server stability improves via a larger `max_request_line_size` that is required to be able to access some next pages of Youtube channels videos and a rewritten static file handler (#5566, #5338)
#### For developers
- Top-level constants moved into dedicated modules, preferences handling was cleaned up, and the legacy signature helper is finally removed (#5596, #5450, #5550)
- Crystal API updates replaced the deprecated `Socket#blocking` property and restored the shard target plus SPDX license metadata (#5538, #5608, #5552)
- CI/tooling stayed current with newer GitHub Actions, install-crystal releases, and cache/checkout bumps (#5569, #5544, #5530, #5499)
### Bugs fixed
#### User-side
- Playlist importer edge cases, playlist API author URLs, and channel continuation tokens now handle empty values without crashing (#4787, #5618, #5614)
- Thin mode community posts, posts that reference unavailable videos, and DMCA content toggles work again (#5567, #5549, #5228)
- UI cleanups prevent channel name/button overflow, show explicit Erroneous CAPTCHA errors, and keep livestream timestamps clean (#5553, #5452, #5508, #5481)
- Trending feeds and related video counts regained accuracy alongside livestream/gaming categories (#5555, #5480, #5446)
#### For instance owners
- Companion downloads, CSP reuse, and check id generation behave predictably even under load (#5561, #5497, #5575)
- Proxy responses drop stray headers and HTTP proxy examples in the config were clarified (#5595, #5586)
- Docker/OCI builds were pinned to stable Crystal releases with OpenSSL bundled to avoid memory leaks (#5604, #5577, #5441)
#### For developers
- README commit instructions, shard targets, and unix socket docs were corrected (#5607, #5608, #5347)
- Thin mode preference comparisons no longer convert unnecessary strings (#5568)
- URL encoding fixes in the download widget and socket API updates prevent regressions when upgrading Crystal (#5367, #5538)
### Full list of pull requests merged since the last release (newest first)
* refactor: Move top level constants to it's own modules (https://github.com/iv-org/invidious/pull/5596, by @Fijxu)
* pages/watch: URL encode 'action' in download widget (https://github.com/iv-org/invidious/pull/5367, by @SamantazFox)
* Document use of unix sockets for `db` (https://github.com/iv-org/invidious/pull/5347, by @Fijxu)
* Generate companion CSP only once to reuse it (https://github.com/iv-org/invidious/pull/5497, by @Fijxu)
* Fix youtube CSV playlist importer (https://github.com/iv-org/invidious/pull/4787, by @ThatMatrix)
* Playlist API: return empty author url if ucid is empty (https://github.com/iv-org/invidious/pull/5618, by @radmorecameron)
* Channels: parse pronouns and display them on channel page (https://github.com/iv-org/invidious/pull/5617, by @radmorecameron)
* playlist: parse playlist thumbnails for topic autogenerated playlists (https://github.com/iv-org/invidious/pull/5616, by @radmorecameron)
* fix: add missing embedded protobuf message in continuation token for channel videos (https://github.com/iv-org/invidious/pull/5614, by @Fijxu)
* Update shard.yml to include target that was removed in commit 9d54cf9 (https://github.com/iv-org/invidious/pull/5608, by @Harm133)
* chore: Do not convert thin_mode preference to string to compare it in before_all (https://github.com/iv-org/invidious/pull/5568, by @Fijxu)
* Fix thin_mode preference for channel community page (https://github.com/iv-org/invidious/pull/5567, by @Fijxu)
* Fix commit command in README instructions, as per #5606 (https://github.com/iv-org/invidious/pull/5607, by @kirisakow)
* Revert "Bump crystallang/crystal from 1.16.3-alpine to 1.19.0-alpine in /docker" (https://github.com/iv-org/invidious/pull/5604, by @unixfox)
* Bump crystallang/crystal from 1.16.3-alpine to 1.19.0-alpine in /docker (https://github.com/iv-org/invidious/pull/5603, by @dependabot[bot])
* doc: Update HTTP proxy configuration comments (https://github.com/iv-org/invidious/pull/5586, by @unixfox)
* Strip unwanted headers from response headers in images and videoplayback (https://github.com/iv-org/invidious/pull/5595, by @Fijxu)
* Generate companion check id one time and add missing companion check id on captions (https://github.com/iv-org/invidious/pull/5575, by @Fijxu)
* Downgrade Crystal to 1.16.3 in OCI (https://github.com/iv-org/invidious/pull/5577, by @Fijxu)
* Allow downloading via companion (https://github.com/iv-org/invidious/pull/5561, by @JeroenBoersma)
* chore: crystal 1.8.2 + alpine 3.23 (https://github.com/iv-org/invidious/pull/5574, by @unixfox)
* Replace deprecated `blocking` property of `Socket` (https://github.com/iv-org/invidious/pull/5538, by @Fijxu)
* Replace `Kemal::StaticFileHandler` with direct subclass of stdlib `HTTP::StaticFileHandler` on Crystal >= 1.17.0 (https://github.com/iv-org/invidious/pull/5338, by @syeopite)
* dockerfile: compile openssl instead of using the one bundled on the crystal alpine image. (https://github.com/iv-org/invidious/pull/5441, by @Fijxu)
* Bump actions/cache from 4 to 5 (https://github.com/iv-org/invidious/pull/5569, by @dependabot[bot])
* Set Kemal `max_request_line_size` to 16384 for large channel continuation query parameters. (https://github.com/iv-org/invidious/pull/5566, by @Fijxu)
* Add link to GitHub release/tag/commit in footer (https://github.com/iv-org/invidious/pull/4702, by @shaedrich)
* Display "Erroneous CAPTCHA" for invalid captchas (https://github.com/iv-org/invidious/pull/5508, by @Fijxu)
* Fix channel name overflow (https://github.com/iv-org/invidious/pull/5553, by @Fijxu)
* Fix trending page by leaving livestream and gaming trending pages (https://github.com/iv-org/invidious/pull/5555, by @Fijxu)
* fix: restore dmca_content functionality (https://github.com/iv-org/invidious/pull/5228, by @Fijxu)
* Remove signature helper completely from Invidious (https://github.com/iv-org/invidious/pull/5550, by @Fijxu)
* Fix community posts when there is a unavailable video in a post (https://github.com/iv-org/invidious/pull/5549, by @Fijxu)
* chore: Update shard.yml to use SPDX license identifier (https://github.com/iv-org/invidious/pull/5552, by @Fijxu)
* Store `preferences` in a variable when reused and rename `prefs` to `preferences` (https://github.com/iv-org/invidious/pull/5450, by @Fijxu)
* Bump actions/checkout from 5 to 6 (https://github.com/iv-org/invidious/pull/5544, by @dependabot[bot])
* Bump crystal-lang/install-crystal from 1.8.3 to 1.9.1 (https://github.com/iv-org/invidious/pull/5530, by @dependabot[bot])
* Fix 0 view count on related videos section (https://github.com/iv-org/invidious/pull/5446, by @shiny-comic)
* Prevent timestamp from being set for Livestreams on "Watch on Youtube" links (https://github.com/iv-org/invidious/pull/5481, by @Fijxu)
* Add Livestreams to trending page (https://github.com/iv-org/invidious/pull/5480, by @Fijxu)
* Fix button overflow (https://github.com/iv-org/invidious/pull/5452, by @Fijxu)
* Bump crystal-lang/install-crystal from 1.8.2 to 1.8.3 (https://github.com/iv-org/invidious/pull/5499, by @dependabot[bot])
* Fixed broken companion hyperlink (https://github.com/iv-org/invidious/pull/5491, by @ndsvw)
## v2.20250913.0
### Wrap-up
This release primarily marks Invidious companion's ascend out of beta and its stable integration thereof into Invidious!
For those unaware Invidious companion is the successor to the `inv-sig-helper` tool, designed to securely pass YouTube's attestation checks and allow for the efficient retrieval and playback of video streams reliably.
Companion delivers YouTube fixes faster since its built on the community-driven [YouTube.js](https://github.com/LuanRT/YouTube.js) project, used by many open source projects such as [FreeTube](https://github.com/FreeTubeApp/FreeTube).
For more information see https://github.com/iv-org/invidious-companion and https://docs.invidious.io/installation/
But companion isn't the only new thing in this release!
Invidious will no longer error out completely as soon as a single item failed to parse in search results, channel pages, etc. Instead it now handles it gracefully by substituting those problematic items with an error card and rendering the page normally.
The player has gained some quality of life features such as being able to choose a default playlist for videos to be added to, or persisting caption appearance settings across the session.
Base Invidious video retrieval without Invidious companion has also been made more stable.
And finally a significant amount of bugs were fixed alongside many other minor improvements.
### New features & important changes
#### For Users
- DASH is now enabled by default due to YouTube's removal of the 720p non-dash streams
- Javascript licencing info has been added to all of Invidious' scripts, restoring full compatibility with LibreJS
- There is no longer an option for a text captcha during registration due to the shutdown (presumably) of the upstream service
- Parse errors in feeds will no longer render the entire feed unusable and instead will substitute only the broken items with error cards
- Keyboard shortcuts have been added to configure caption styles:
- `-`,`=` can be used to change the font size
- `o` can be used to cycle the opacity of the caption text
- `w` can be used to cycle the opacity of the caption box
- Caption styles changed through the VideoJS menu will now persist
- You can now choose a default playlist to add videos to instead of needing to manually select one each time
#### For instance owners
- Invidious companion support has been added to replace the deprecated inv-sig-helper
- **DASH is now the default resolution! Please ensure that your instances can withstand the significantly higher bandwidth usage or manually configure your instance to use non-dash streams by default**
- Invidious will now warn when it is unable to connect to the database instead of failing silently
- **The text captcha during registration has been removed due to the shutdown (presumably) of the upstream service**
#### For developers
- Dependabot has been added to keep Github Actions and Docker dependencies up-to-date.
- CI version matrix has been bumped to the latest patch release for each minor version
- The versions of Crystal that we test in CI/CD are now: `1.12.2`, `1.13.3`, `1.14.1`, `1.15.1`, `1.16.3`
- `Kilt` is no longer a dependency of Invidious
- The ARM64 docker image builds (and the test CI) has been changed to use Github's ARM64 runner instead of QEMU
- **An "error" JSON object can now be returned in various API responses in-place of an item that has failed to parse**:
```json
{
"type": "parse-error",
"errorMessage": "...",
"errorBacktrace": "..."
}
```
### Bugs fixed
#### User-side
- Livestream will now be properly proxied again allowing playback from the UI
- The proxy video preference for logged-in users will no longer get ignored when a default value is set by the instance
- Fixes the missing `label` key error on select search results and other feeds
- Invidious will no longer strip out spaces from search queries when navigating back from the preferences page
- Restores functionality to the `subscriptions:true` search keyword
- The channel RSS feeds will no longer have an empty title
- Individual community posts can be viewed again
- The playlists tab of channels can be viewed again
- Fix incorrect dates, region, etc of videos
- Various minor fixes were made to how video info is extracted in setups without Invidious companion to improve resiliency and chances of success
- Fix issue where the notification count becomes `TRUE` rather than an actual number
#### For instance owners
- Fixed a minor typo in config.example.yml (`effet` -> `effect`)
#### For developers
- The docker image test CI will now properly check whether Invidious has started
### Full list of pull requests merged since the last release (newest first)
* Add Invidious companion support (https://github.com/iv-org/invidious/pull/4985, by @unixfox)
* Bump shards.yml version to dev version (https://github.com/iv-org/invidious/pull/5206, by @syeopite)
* chore: enforce 16 characters for invidious_companion_key (https://github.com/iv-org/invidious/pull/5220, by @unixfox)
* chore: set dash by default (https://github.com/iv-org/invidious/pull/5216, by @unixfox)
* Fix minor casing issues in brand names (https://github.com/iv-org/invidious/pull/5258, thanks @efb4f5ff-1298-471a-8973-3d47447115dc)
* feat: route to invidious companion on downloads (https://github.com/iv-org/invidious/pull/5224, by @alexmaras)
* Fix proxying live DASH streams (https://github.com/iv-org/invidious/pull/4589, thanks @absidue)
* Reflect companion secret character limit in example config comment (https://github.com/iv-org/invidious/pull/5269, thanks @Vyquos)
* chore: Add dependabot for docker and github actions (https://github.com/iv-org/invidious/pull/5285, by @unixfox)
* Bump actions/stale from 8 to 9 (https://github.com/iv-org/invidious/pull/5291, thanks @dependabot[bot])
* Bump actions/cache from 3 to 4 (https://github.com/iv-org/invidious/pull/5289, thanks @dependabot[bot])
* Bump alpine from 3.20 to 3.21 in /docker (https://github.com/iv-org/invidious/pull/5288, thanks @dependabot[bot])
* Bump docker/build-push-action from 5 to 6 (https://github.com/iv-org/invidious/pull/5287, thanks @dependabot[bot])
* Bump crystal-lang/install-crystal from 1.8.0 to 1.8.2 (https://github.com/iv-org/invidious/pull/5286, thanks @dependabot[bot])
* Bump crystallang/crystal from 1.12.2-alpine to 1.16.2-alpine in /docker (https://github.com/iv-org/invidious/pull/5290, thanks @dependabot[bot])
* Bump crystallang/crystal from 1.16.2-alpine to 1.16.3-alpine in /docker (https://github.com/iv-org/invidious/pull/5301, thanks @dependabot[bot])
* CI: Bump Crystal version matrix (https://github.com/iv-org/invidious/pull/5293, by @Fijxu)
* fix(typo): 'Salect' -> 'Select' (https://github.com/iv-org/invidious/pull/5242, by @Fijxu)
* fix: set CSP header after setting preferences of registered users (https://github.com/iv-org/invidious/pull/5275, by @Fijxu)
* fix: safely access "label" key (https://github.com/iv-org/invidious/pull/5282, by @Fijxu)
* Add missing javascript licenses (https://github.com/iv-org/invidious/pull/5292, by @Fijxu)
* Add Javascript licence information automatically (https://github.com/iv-org/invidious/pull/5297, by @syeopite)
* Remove text captcha due to textcaptcha.com being down (https://github.com/iv-org/invidious/pull/5308, by @Fijxu)
* Release versioning maintenance (https://github.com/iv-org/invidious/pull/5310, by @syeopite)
* Update Kemal to 1.6.0 and remove Kilt (https://github.com/iv-org/invidious/pull/5120, by @syeopite)
* Translations update from Hosted Weblate (https://github.com/iv-org/invidious/pull/5192, thanks @weblate)
* require base_job before the other jobs (https://github.com/iv-org/invidious/pull/5194, by @Fijxu)
* Handle parse errors gracefully on timeline items (https://github.com/iv-org/invidious/pull/5196, by @syeopite)
* fix: do not strip '+' character from referer (https://github.com/iv-org/invidious/pull/5276, by @Fijxu)
* fix: pass user to `query.process` if present. (https://github.com/iv-org/invidious/pull/5277, by @Fijxu)
* Add missing xml.text on "title" element for channels RSS (https://github.com/iv-org/invidious/pull/5320, by @Fijxu)
* Remove `@iv-org/developers` from codeowners (https://github.com/iv-org/invidious/pull/5314, by @syeopite)
* Make base-Invidious video info extraction more resilient (https://github.com/iv-org/invidious/pull/5312, by @syeopite)
* Bump actions/checkout from 4 to 5 (https://github.com/iv-org/invidious/pull/5415, thanks @dependabot[bot])
* Player: Add keyboard shortcuts to configure captions (https://github.com/iv-org/invidious/pull/5188, thanks @epicsam123)
* CI: Use public ARM64 Github actions runners for ARM64 builds. (https://github.com/iv-org/invidious/pull/5305, by @Fijxu)
* CI: Fix docker ci job not checking if Invidious starts successfully or not (https://github.com/iv-org/invidious/pull/5306, by @Fijxu)
* YtAPI: Bump client versions (https://github.com/iv-org/invidious/pull/5325, by @Fijxu)
* YTAPI: Add `TvSimply` client (https://github.com/iv-org/invidious/pull/5344, by @Fijxu)
* Videos: Add fallback to TvSimply client (https://github.com/iv-org/invidious/pull/5345, by @Fijxu)
* Show message when connection to the database is not possible (https://github.com/iv-org/invidious/pull/5346, by @Fijxu)
* Channels: Fix fetching of individual community posts (https://github.com/iv-org/invidious/pull/5361, thanks @ChunkyProgrammer)
* Videos: Fix missing .id to retrieve first playlist video ID (https://github.com/iv-org/invidious/pull/5366, by @SamantazFox)
* HTML: Add Missing Noreferrers (https://github.com/iv-org/invidious/pull/5368, thanks @epicsam123)
* Documentation: Fix typo (effet -> effect) (https://github.com/iv-org/invidious/pull/5369, thanks @nsunami)
* Frontend: Fix notification count of `TRUE` (https://github.com/iv-org/invidious/pull/5391, thanks @fieryhenry)
* Player: Persist caption settings (https://github.com/iv-org/invidious/pull/5417, thanks @p-himik)
* Channels: Fix fetching channel playlists (https://github.com/iv-org/invidious/pull/5418, thanks @KrisVos130)
* CI: fix wrong if statement for build-docker job (https://github.com/iv-org/invidious/pull/5442, by @Fijxu)
* initial base_url companion support + proxy companion (https://github.com/iv-org/invidious/pull/5266, by @unixfox)
* Prevent player microformat from being overwritten by the next microformat (https://github.com/iv-org/invidious/pull/5453, by @Fijxu)
* Bump actions/stale from 9 to 10 (https://github.com/iv-org/invidious/pull/5457, thanks @dependabot[bot])
* Better documentation for the specific case public_url with companion (https://github.com/iv-org/invidious/pull/5461, by @unixfox)
* Add default playlist preference (https://github.com/iv-org/invidious/pull/5449, by @Fijxu)
* Translations update from Hosted Weblate (https://github.com/iv-org/invidious/pull/5313, thanks to our many translators)
* Release `v2.20250913.0` (https://github.com/iv-org/invidious/pull/5463, by @syeopite)
## v2.20250517.0

View File

@@ -129,7 +129,7 @@ You can read more here: https://docs.invidious.io/applications/
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. Commit your changes (`git commit -m '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 ).

View File

@@ -75,6 +75,16 @@ body {
height: auto;
}
.channel-profile > .channel-name-pronouns {
display: inline-block;
}
.channel-profile > .channel-name-pronouns > .channel-pronouns {
font-style: italic;
font-size: .8em;
font-weight: lighter;
}
body a.channel-owner {
background-color: #008bec;
color: #fff;
@@ -167,6 +177,7 @@ body a.pure-button-primary,
.pure-button-primary,
.pure-button-secondary {
white-space: normal;
border: 1px solid #a0a0a0;
border-radius: 3px;
margin: 0 .4em;
@@ -403,9 +414,15 @@ input[type="search"]::-webkit-search-cancel-button {
.video-card-row { margin: 15px 0; }
p.channel-name { margin: 0; }
p.channel-name { margin: 0; overflow-wrap: anywhere;}
p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
.channel-profile > .channel-name,
.channel-profile > .channel-name-pronouns > .channel-name
{
overflow-wrap: anywhere;
}
/*
* Comments & community posts

View File

@@ -86,6 +86,7 @@ ul.vjs-menu-content::-webkit-scrollbar {
background-color: rgba(0, 0, 0, 0.75) !important;
border-radius: 9px !important;
padding: 5px !important;
line-height: 1.5 !important;
}
.vjs-play-control,

View File

@@ -77,7 +77,7 @@ function create_notification_stream(subscriptions) {
function update_ticker_count() {
var notification_ticker = document.getElementById('notification_ticker');
const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
const notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
if (notification_count > 0) {
notification_ticker.innerHTML =
'<span id="notification_count">' + notification_count + '</span> <i class="icon ion-ios-notifications"></i>';

View File

@@ -5,6 +5,10 @@ var video_data = JSON.parse(document.getElementById('video_data').textContent);
var options = {
liveui: true,
playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0],
fontPercent: [0.5, 0.75, 1.25, 1.5, 1.75, 2, 3, 4],
windowOpacity: ['0', '0.5', '1'],
textOpacity: ['0.5', '1'],
persistTextTrackSettings: true,
controlBar: {
children: [
'playToggle',
@@ -133,16 +137,18 @@ player.on('timeupdate', function () {
// YouTube links
let elem_yt_watch = document.getElementById('link-yt-watch');
if (elem_yt_watch) {
let base_url_yt_watch = elem_yt_watch.getAttribute('data-base-url');
elem_yt_watch.href = addCurrentTimeToURL(base_url_yt_watch);
}
let elem_yt_embed = document.getElementById('link-yt-embed');
if (elem_yt_embed) {
let base_url_yt_embed = elem_yt_embed.getAttribute('data-base-url');
elem_yt_embed.href = addCurrentTimeToURL(base_url_yt_embed);
if (!video_data.live_now) {
let elem_yt_watch = document.getElementById('link-yt-watch');
if (elem_yt_watch) {
let base_url_yt_watch = elem_yt_watch.getAttribute('data-base-url');
elem_yt_watch.href = addCurrentTimeToURL(base_url_yt_watch);
}
let elem_yt_embed = document.getElementById('link-yt-embed');
if (elem_yt_embed) {
let base_url_yt_embed = elem_yt_embed.getAttribute('data-base-url');
elem_yt_embed.href = addCurrentTimeToURL(base_url_yt_embed);
}
}
// Invidious links
@@ -180,7 +186,7 @@ var shareOptions = {
};
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>';
var overlay_content = '<h1><a rel="noopener noreferrer" 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'},
@@ -450,7 +456,7 @@ if (!video_data.params.listen && video_data.params.annotations) {
if (target === 'current') {
location.href = path;
} else if (target === 'new') {
open(path, '_blank');
open(path, '_blank', 'noopener,noreferrer');
}
});
@@ -585,6 +591,13 @@ const toggle_captions = (function () {
};
})();
// For real-time updates to captions (if currently showing)
function update_captions() {
if (document.body.querySelector('.vjs-text-track-cue')) {
toggle_captions(); toggle_captions();
}
}
function toggle_fullscreen() {
player.isFullscreen() ? player.exitFullscreen() : player.requestFullscreen();
}
@@ -597,6 +610,34 @@ function increase_playback_rate(steps) {
player.playbackRate(options.playbackRates[newIndex]);
}
function increase_caption_size(steps) {
const maxIndex = options.fontPercent.length - 1;
const fontPercent = player.textTrackSettings.getValues().fontPercent || 1.25;
const curIndex = options.fontPercent.indexOf(fontPercent);
let newIndex = curIndex + steps;
newIndex = helpers.clamp(newIndex, 0, maxIndex);
player.textTrackSettings.setValues({ fontPercent: options.fontPercent[newIndex] });
update_captions();
}
function toggle_caption_window() {
const numOptions = options.windowOpacity.length;
const windowOpacity = player.textTrackSettings.getValues().windowOpacity || '0';
const curIndex = options.windowOpacity.indexOf(windowOpacity);
const newIndex = (curIndex + 1) % numOptions;
player.textTrackSettings.setValues({ windowOpacity: options.windowOpacity[newIndex] });
update_captions();
}
function toggle_caption_opacity() {
const numOptions = options.textOpacity.length;
const textOpacity = player.textTrackSettings.getValues().textOpacity || '1';
const curIndex = options.textOpacity.indexOf(textOpacity);
const newIndex = (curIndex + 1) % numOptions;
player.textTrackSettings.setValues({ textOpacity: options.textOpacity[newIndex] });
update_captions();
}
addEventListener('keydown', function (e) {
if (e.target.tagName.toLowerCase() === 'input') {
// Ignore input when focus is on certain elements, e.g. form fields.
@@ -692,6 +733,12 @@ addEventListener('keydown', function (e) {
case '>': action = increase_playback_rate.bind(this, 1); break;
case '<': action = increase_playback_rate.bind(this, -1); break;
case '=': action = increase_caption_size.bind(this, 1); break;
case '-': action = increase_caption_size.bind(this, -1); break;
case 'w': action = toggle_caption_window; break;
case 'o': action = toggle_caption_opacity; break;
default:
console.info('Unhandled key down event: %s:', decoratedKey, e);

View File

@@ -141,7 +141,7 @@ function get_reddit_comments() {
</b> \
</p> \
<b> \
<a rel="noopener" target="_blank" href="https://reddit.com{permalink}">{redditPermalinkText}</a> \
<a rel="noopener noreferrer" target="_blank" href="https://reddit.com{permalink}">{redditPermalinkText}</a> \
</b> \
</div> \
<div>{contentHtml}</div> \

View File

@@ -8,6 +8,13 @@
## Database configuration with separate parameters.
## This setting is MANDATORY, unless 'database_url' is used.
##
## Note: The 'db' setting allows the use of UNIX
## sockets. To do so, set 'host' to ""
## E.g:
## password: kemal
## host: ""
## port: 5432
##
db:
user: kemal
password: kemal
@@ -40,20 +47,6 @@ db:
##
#check_tables: false
##
## Path to an external signature resolver, used to emulate
## the Youtube client's Javascript. If no such server is
## available, some videos will not be playable.
##
## When this setting is commented out, no external
## resolver will be used.
##
## Accepted values: a path to a UNIX socket or "<IP>:<Port>"
## Default: <none>
##
#signature_server:
##
## Invidious companion is an external program
## for loading the video streams from YouTube servers.
@@ -61,31 +54,32 @@ db:
## When this setting is commented out, Invidious companion is not used.
## Otherwise, Invidious will proxy the requests to Invidious companion.
##
## Note: multiple URL can be configured. In this case, invidious will
## Note: multiple URL can be configured. In this case, Invidious will
## randomly pick one every time video data needs to be retrieved. This
## URL is then kept in the video metadata cache to allow video playback
## to work. Once said cache has expired, requesting that video's data
## again will cause a new companion URL to be picked.
##
## The parameter private_url needs to be configured for the internal
## communication between the companion and Invidious.
## And public_url is the public URL from which companion is listening
## to the requests from the user(s).
## The parameter private_url is required for the internal communication
## between Invidious companion and Invidious.
##
## If you are using a reverse proxy then you will probably need to
## configure the public_url to be the same as the domain used for Invidious.
## Also apply when used from an external IP address (without a domain).
## Examples: https://MYINVIDIOUSDOMAIN or http://192.168.1.100:8282
##
## Both parameter can have identical URL when Invidious is hosted in
## an internal network or at home or locally (localhost).
## The optional parameter public_url is the public URL from which
## Invidious companion is listening to the requests from the user(s).
## When this setting is commented out, Invidious proxy all requests to
## Invidious companion. Useful for simple setups.
## Otherwise, requests from the user(s) will reach Invidious companion directly.
## And you will need to configure a reverse proxy with separate routes
## for Invidious and Invidious companion.
## Read the post-install documentation for advanced reverse proxy
## documentation: https://docs.invidious.io/installation/#post-install-configuration
##
## Accepted values: "http(s)://<IP-HOSTNAME>:<Port>"
## Default: <none>
##
#invidious_companion:
# - private_url: "http://localhost:8282"
# public_url: "http://localhost:8282"
# - private_url: "http://localhost:8282/companion"
# # Uncomment for advanced reverse proxy configuration (see above).
# # public_url: "http://localhost:8282/companion"
##
## API key for Invidious companion, used for securing the communication
@@ -236,9 +230,13 @@ https_only: false
##
## Configuration for using a HTTP proxy
##
## If unset, then no HTTP proxy will be used.
## Proxy type supported: HTTP, HTTPS
##
## This is not used for loading the video streams from YouTube servers (circumvent YouTube restrictions)
## Please instead configure the proxy in Invidious companion:
## https://github.com/iv-org/invidious-companion/blob/master/config/config.example.toml
##
#http_proxy:
# user:
# password:
@@ -258,19 +256,6 @@ https_only: false
##
# use_innertube_for_captions: false
##
## Send Google session informations. This is useful when Invidious is blocked
## by the message "This helps protect our community."
## See https://github.com/iv-org/invidious/issues/4734.
##
## Warning: These strings gives much more identifiable information to Google!
##
## Accepted values: String
## Default: <none>
##
# po_token: ""
# visitor_data: ""
# -----------------------------
# Logging
# -----------------------------
@@ -865,7 +850,7 @@ default_user_preferences:
##
## Default dash video quality.
##
## Note: this setting only takes effet if the
## Note: this setting only takes effect if the
## 'quality' parameter is set to "dash".
##
## Accepted values:

View File

@@ -14,6 +14,10 @@ services:
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
depends_on:
invidious-db:
condition: service_healthy
restart: true
environment:
# Please read the following file for a comprehensive list of all available
# configuration options and their associated syntax:
@@ -32,7 +36,7 @@ services:
# statistics_enabled: false
hmac_key: "CHANGE_ME!!"
healthcheck:
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/trending || exit 1
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/stats || exit 1
interval: 30s
timeout: 5s
retries: 2

View File

@@ -1,6 +1,29 @@
FROM crystallang/crystal:1.16.3-alpine AS builder
# https://github.com/openssl/openssl/releases/tag/openssl-3.5.2
ARG OPENSSL_VERSION='3.5.2'
ARG OPENSSL_SHA256='c53a47e5e441c930c3928cf7bf6fb00e5d129b630e0aa873b08258656e7345ec'
FROM crystallang/crystal:1.16.3-alpine AS dependabot-crystal
# We compile openssl ourselves due to a memory leak in how crystal interacts
# with openssl
# Reference: https://github.com/iv-org/invidious/issues/1438#issuecomment-3087636228
FROM dependabot-crystal AS openssl-builder
RUN apk add --no-cache curl perl linux-headers
WORKDIR /
ARG OPENSSL_VERSION
ARG OPENSSL_SHA256
RUN curl -Ls "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz" --output openssl-${OPENSSL_VERSION}.tar.gz
RUN echo "${OPENSSL_SHA256} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c
RUN tar -xzvf openssl-${OPENSSL_VERSION}.tar.gz
RUN cd openssl-${OPENSSL_VERSION} && ./Configure --openssldir=/etc/ssl && make -j$(nproc)
FROM dependabot-crystal AS builder
RUN apk add --no-cache sqlite-static yaml-static
RUN apk del openssl-dev openssl-libs-static
ARG release
@@ -21,18 +44,24 @@ COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
RUN crystal spec --warnings all \
--link-flags "-lxml2 -llzma"
ARG OPENSSL_VERSION
COPY --from=openssl-builder /openssl-${OPENSSL_VERSION} /openssl-${OPENSSL_VERSION}
RUN --mount=type=cache,target=/root/.cache/crystal if [[ "${release}" == 1 ]] ; then \
PKG_CONFIG_PATH=/openssl-${OPENSSL_VERSION} \
crystal build ./src/invidious.cr \
--release \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
else \
PKG_CONFIG_PATH=/openssl-${OPENSSL_VERSION} \
crystal build ./src/invidious.cr \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
fi
FROM alpine:3.21
FROM alpine:3.23
RUN apk add --no-cache rsvg-convert ttf-opensans tini tzdata
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \

View File

@@ -1,6 +1,31 @@
FROM alpine:3.21 AS builder
RUN apk add --no-cache 'crystal=1.14.0-r0' shards sqlite-static yaml-static yaml-dev libxml2-static \
zlib-static openssl-libs-static openssl-dev musl-dev xz-static
# https://github.com/openssl/openssl/releases/tag/openssl-3.5.2
ARG OPENSSL_VERSION='3.5.2'
ARG OPENSSL_SHA256='c53a47e5e441c930c3928cf7bf6fb00e5d129b630e0aa873b08258656e7345ec'
FROM alpine:3.22 AS dependabot-alpine
# We compile openssl ourselves due to a memory leak in how crystal interacts
# with openssl
# Reference: https://github.com/iv-org/invidious/issues/1438#issuecomment-3087636228
FROM dependabot-alpine AS openssl-builder
RUN apk add --no-cache curl perl linux-headers build-base
WORKDIR /
ARG OPENSSL_VERSION
ARG OPENSSL_SHA256
RUN curl -Ls "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz" --output openssl-${OPENSSL_VERSION}.tar.gz
RUN echo "${OPENSSL_SHA256} openssl-${OPENSSL_VERSION}.tar.gz" | sha256sum -c
RUN tar -xzvf openssl-${OPENSSL_VERSION}.tar.gz
RUN cd openssl-${OPENSSL_VERSION} && ./Configure --openssldir=/etc/ssl && make -j$(nproc)
FROM dependabot-alpine AS builder
RUN apk add --no-cache 'crystal=1.16.3-r0' shards \
sqlite-static yaml-static yaml-dev \
pcre2-static gc-static \
libxml2-static zlib-static \
openssl-libs-static openssl-dev musl-dev xz-static
ARG release
@@ -22,18 +47,23 @@ COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
RUN crystal spec --warnings all \
--link-flags "-lxml2 -llzma"
ARG OPENSSL_VERSION
COPY --from=openssl-builder /openssl-${OPENSSL_VERSION} /openssl-${OPENSSL_VERSION}
RUN --mount=type=cache,target=/root/.cache/crystal if [[ "${release}" == 1 ]] ; then \
PKG_CONFIG_PATH=/openssl-${OPENSSL_VERSION} \
crystal build ./src/invidious.cr \
--release \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
else \
PKG_CONFIG_PATH=/openssl-${OPENSSL_VERSION} \
crystal build ./src/invidious.cr \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
fi
FROM alpine:3.21
FROM alpine:3.22
RUN apk add --no-cache rsvg-convert ttf-opensans tini tzdata
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \

View File

@@ -39,8 +39,6 @@
"User ID": "مُعرِّف المُستخدم",
"Password": "كلمة المرور",
"Time (h:mm:ss):": "الوقت (h:mm:ss):",
"Text CAPTCHA": "نص الكابتشا",
"Image CAPTCHA": "صورة الكابتشا",
"Sign In": "إنشاء حساب",
"Register": "التسجيل",
"E-mail": "البريد الإلكتروني",
@@ -569,5 +567,8 @@
"Filipino (auto-generated)": "الفلبينية (المولدة تلقائيًا)",
"channel_tab_courses_label": "الدورات",
"channel_tab_posts_label": "المنشورات",
"First page": "الصفحة الأولى"
"First page": "الصفحة الأولى",
"timeline_parse_error_placeholder_heading": "غير قادر على تحليل العنصر",
"timeline_parse_error_placeholder_message": "واجه Invidious خطأ أثناء محاولة تحليل هذا العنصر. لمزيد من المعلومات انظر أدناه:",
"timeline_parse_error_show_technical_details": "عرض التفاصيل التقنية"
}

View File

@@ -102,7 +102,6 @@
"Spanish (Spain)": "Испански (Испания)",
"invidious": "Invidious",
"crash_page_refresh": "пробвал да <a href=\"`x`\">опресниш страницата</a>",
"Image CAPTCHA": "CAPTCHA с Изображение",
"search_filters_features_option_hd": "HD",
"Chinese (Hong Kong)": "Китайски (Хонг Конг)",
"Import Invidious data": "Импортиране на Invidious JSON информацията",
@@ -457,7 +456,6 @@
"next_steps_error_message": "След което можеш да пробваш да: ",
"Hide annotations": "Скрий анотации",
"Standard YouTube license": "Стандартен YouTube лиценз",
"Text CAPTCHA": "Текст CAPTCHA",
"Log in/register": "Вход/регистрация",
"Punjabi": "Пенджаби",
"Change password": "Смяна на паролата",

View File

@@ -36,8 +36,6 @@
"User ID": "ইউজার আইডি",
"Password": "পাসওয়ার্ড",
"Time (h:mm:ss):": "সময় (ঘণ্টা:মিনিট:সেকেন্ড):",
"Text CAPTCHA": "টেক্সট ক্যাপচা",
"Image CAPTCHA": "চিত্র ক্যাপচা",
"Sign In": "সাইন ইন",
"Register": "নিবন্ধন",
"E-mail": "ই-মেইল",

View File

@@ -39,8 +39,6 @@
"User ID": "ইউজার আইডি",
"Password": "পাসওয়ার্ড",
"Time (h:mm:ss):": "সময় (ঘণ্টা:মিনিট:সেকেন্ড):",
"Text CAPTCHA": "টেক্সট ক্যাপচা",
"Image CAPTCHA": "চিত্র ক্যাপচা",
"Sign In": "সাইন ইন",
"Register": "নিবন্ধন",
"E-mail": "ই-মেইল",

View File

@@ -167,7 +167,6 @@
"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: ",
@@ -387,7 +386,6 @@
"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? ",

View File

@@ -39,8 +39,6 @@
"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",
@@ -518,5 +516,10 @@
"Filipino (auto-generated)": "Filipínština (vytvořeno automaticky)",
"First page": "První stránka",
"channel_tab_courses_label": "Kurzy",
"channel_tab_posts_label": "Příspěvky"
"channel_tab_posts_label": "Příspěvky",
"timeline_parse_error_show_technical_details": "Zobrazit technické podrobnosti",
"timeline_parse_error_placeholder_message": "Invidious narazil při pokusu o zpracování této položky na chybu. Další informace naleznete níže:",
"timeline_parse_error_placeholder_heading": "Nepodařilo se zpracovat položku",
"preferences_default_playlist": "Výchozí playlist: ",
"preferences_default_playlist_none": "Nenastaven žádný výchozí playlist"
}

View File

@@ -162,8 +162,6 @@
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_720p": "720p",
"invidious": "Invidious",
"Text CAPTCHA": "CAPTCHA testun",
"Image CAPTCHA": "CAPTCHA delwedd",
"preferences_continue_label": "Chwarae'r fideo nesaf fel rhagosodiad: ",
"preferences_continue_autoplay_label": "Chwarae'r fideo nesaf yn awtomatig: ",
"preferences_listen_label": "Sain yn unig: ",

View File

@@ -39,8 +39,6 @@
"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",

View File

@@ -12,7 +12,7 @@
"Next page": "Nächste Seite",
"Previous page": "Vorherige Seite",
"First page": "Erste Seite",
"Clear watch history?": "Verlauf löschen?",
"Clear watch history?": "Wiedergabeverlauf löschen?",
"New password": "Neues Passwort",
"New passwords must match": "Neue Passwörter müssen übereinstimmen",
"Authorize token?": "Token autorisieren?",
@@ -40,8 +40,6 @@
"User ID": "Benutzer-ID",
"Password": "Passwort",
"Time (h:mm:ss):": "Zeit (h:mm:ss):",
"Text CAPTCHA": "Text CAPTCHA",
"Image CAPTCHA": "Bild CAPTCHA",
"Sign In": "Anmelden",
"Register": "Registrieren",
"E-mail": "E-Mail",
@@ -108,11 +106,11 @@
"Top enabled: ": "Top aktiviert? ",
"CAPTCHA enabled: ": "CAPTCHA aktiviert? ",
"Login enabled: ": "Anmeldung aktiviert: ",
"Registration enabled: ": "Registrierung aktiviert? ",
"Report statistics: ": "Statistiken berichten? ",
"Registration enabled: ": "Registrierung aktiviert: ",
"Report statistics: ": "Statistiken berichten: ",
"Save preferences": "Einstellungen speichern",
"Subscription manager": "Abonnementverwaltung",
"Token manager": "Tokenverwalter",
"Token manager": "Tokenverwaltung",
"Token": "Token",
"Import/export": "Importieren/Exportieren",
"unsubscribe": "abbestellen",
@@ -122,20 +120,20 @@
"Log out": "Abmelden",
"Released under the AGPLv3 on Github.": "Auf GitHub unter der AGPLv3 Lizenz veröffentlicht.",
"Source available here.": "Quellcode verfügbar hier.",
"View JavaScript license information.": "Javascript Lizenzinformationen anzeigen.",
"View JavaScript license information.": "Javascript-Lizenzinformationen anzeigen.",
"View privacy policy.": "Datenschutzerklärung einsehen.",
"Trending": "Angesagt",
"Public": "Öffentlich",
"Unlisted": "Nicht aufgeführt",
"Unlisted": "Nicht gelistet",
"Private": "Privat",
"View all playlists": "Alle Wiedergabelisten anzeigen",
"Updated `x` ago": "Aktualisiert `x` vor",
"Delete playlist `x`?": "Wiedergabeliste löschen `x`?",
"Updated `x` ago": "Aktualisiert vor `x`",
"Delete playlist `x`?": "Wiedergabeliste `x` löschen?",
"Delete playlist": "Wiedergabeliste löschen",
"Create playlist": "Wiedergabeliste erstellen",
"Title": "Titel",
"Playlist privacy": "Vertrauliche Wiedergabeliste",
"Editing playlist `x`": "Wiedergabeliste bearbeiten `x`",
"Playlist privacy": "Wiedergabelisten-Privatsphäre",
"Editing playlist `x`": "Wiedergabeliste `x` bearbeiten",
"Show more": "Mehr anzeigen",
"Show less": "Weniger anzeigen",
"Watch on YouTube": "Video auf YouTube ansehen",
@@ -151,12 +149,12 @@
"Blacklisted regions: ": "Unerlaubte Regionen: ",
"Shared `x`": "Geteilt `x`",
"Premieres in `x`": "Premiere in `x`",
"Premieres `x`": "Erster Start `x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Hallo! Anscheinend haben Sie JavaScript deaktiviert. Klicken Sie hier um Kommentare anzuzeigen, beachten sie dass es etwas länger dauern kann um sie zu laden.",
"Premieres `x`": "Premiere `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 hast du JavaScript deaktiviert. Klicke hier, um Kommentare anzuzeigen, beachte, 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",
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` Kommentar anzeigen",
"": "`x` Kommentare anzeigen"
},
"View Reddit comments": "Reddit Kommentare anzeigen",
@@ -184,7 +182,7 @@
"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.",
"Could not pull trending pages.": "Beliebt-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",
@@ -192,7 +190,7 @@
"No such user": "Ungültiger Benutzer",
"Token is expired, please try again": "Token ist abgelaufen, bitte erneut versuchen",
"English": "Englisch",
"English (auto-generated)": "Englisch (automatisch erzeugt)",
"English (auto-generated)": "Englisch (automatisch generiert)",
"Afrikaans": "Afrikaans",
"Albanian": "Albanisch",
"Amharic": "Amharisch",
@@ -313,7 +311,7 @@
"Download": "Herunterladen",
"Download as: ": "Herunterladen als: ",
"%A %B %-d, %Y": "%A %-d %B %Y",
"(edited)": "(editiert)",
"(edited)": "(bearbeitet)",
"YouTube comment permalink": "YouTube-Kommentar Permalink",
"permalink": "Permalink",
"`x` marked it with a ❤": "`x` markierte es mit einem ❤",
@@ -321,7 +319,7 @@
"Video mode": "Videomodus",
"channel_tab_videos_label": "Videos",
"Playlists": "Wiedergabelisten",
"channel_tab_community_label": "Gemeinschaft",
"channel_tab_community_label": "Community",
"search_filters_sort_option_relevance": "Relevanz",
"search_filters_sort_option_rating": "Bewertung",
"search_filters_sort_option_date": "Hochladedatum",
@@ -329,7 +327,7 @@
"search_filters_type_label": "Inhaltstyp",
"search_filters_duration_label": "Dauer",
"search_filters_features_label": "Eigenschaften",
"search_filters_sort_label": "sortieren",
"search_filters_sort_label": "Sortieren nach",
"search_filters_date_option_hour": "Letzte Stunde",
"search_filters_date_option_today": "Heute",
"search_filters_date_option_week": "Diese Woche",
@@ -341,7 +339,7 @@
"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_subtitles": "Untertitel/CC",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_live": "Live",
@@ -358,7 +356,7 @@
"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",
"adminprefs_modified_source_code_url_label": "URL zum Repository 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)",
@@ -397,7 +395,7 @@
"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>",
"crash_page_refresh": "Versucht hast, <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",
@@ -406,15 +404,15 @@
"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",
"crash_page_before_reporting": "Bevor du einen Bug meldest, stelle sicher, dass du:",
"crash_page_switch_instance": "Eine <a href=\"`x`\">andere Instanz</a> versucht hast",
"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):",
"crash_page_read_the_faq": "Das <a href=\"`x`\">FAQ</a> gelesen hast",
"crash_page_search_issue": "Nach <a href=\"`x`\">bereits gemeldeten Bugs auf GitHub</a> gesucht hast",
"crash_page_report_issue": "Wenn all dies nicht geholfen hat, <a href=\"`x`\">öffne bitte ein neues Problem (issue) auf GitHub</a> (vorzugsweise auf Englisch) und füge den folgenden Text in deine Nachricht ein (bitte übersetze diesen Text NICHT):",
"generic_views_count": "{{count}} Aufruf",
"generic_views_count_plural": "{{count}} Aufrufe",
"generic_count_seconds": "{{count}} Sekunde",
@@ -425,7 +423,7 @@
"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!",
"crash_page_you_found_a_bug": "Anscheinend hast du einen Fehler in Invidious gefunden!",
"generic_count_months": "{{count}} Monat",
"generic_count_months_plural": "{{count}} Monaten",
"Cantonese (Hong Kong)": "Kantonesisch (Hong Kong)",
@@ -455,8 +453,8 @@
"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>.",
"search_message_change_filters_or_query": "Versuche, deine Suchanfrage zu erweitern und/oder die Filter zu ändern.",
"search_message_use_another_instance": "Du kannst 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)",
@@ -466,7 +464,7 @@
"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>",
"error_video_not_in_playlist": "Das angeforderte Video existiert nicht in dieser Wiedergabeliste. <a href=\"`x`\">Klicke 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",
@@ -501,5 +499,8 @@
"carousel_skip": "Galerie überspringen",
"Filipino (auto-generated)": "Philippinisch (automatisch generiert)",
"channel_tab_courses_label": "Kurse",
"channel_tab_posts_label": "Beiträge"
"channel_tab_posts_label": "Beiträge",
"timeline_parse_error_show_technical_details": "Technische Details anzeigen",
"timeline_parse_error_placeholder_heading": "Element kann nicht geparsed werden",
"timeline_parse_error_placeholder_message": "Invidious ist beim Parsen dieses Elements auf einen Fehler gestossen. Für weitere Informationen siehe unten:"
}

View File

@@ -39,8 +39,6 @@
"User ID": "Ταυτότητα χρήστη",
"Password": "Κωδικός πρόσβασης",
"Time (h:mm:ss):": "Ώρα (ω:λλ:δδ):",
"Text CAPTCHA": "Κείμενο CAPTCHA",
"Image CAPTCHA": "Εικόνα CAPTCHA",
"Sign In": "Εγγραφή",
"Register": "Εγγραφή",
"E-mail": "Ηλεκτρονικό ταχυδρομείο",

View File

@@ -122,6 +122,8 @@
"Redirect homepage to feed: ": "Redirect homepage to feed: ",
"preferences_max_results_label": "Number of videos shown in feed: ",
"preferences_sort_label": "Sort videos by: ",
"preferences_default_playlist": "Default playlist: ",
"preferences_default_playlist_none": "No default playlist set",
"published": "published",
"published - reverse": "published - reverse",
"alphabetically": "alphabetically",
@@ -406,6 +408,7 @@
"Default": "Default",
"Music": "Music",
"Gaming": "Gaming",
"Livestreams": "Livestreams",
"News": "News",
"Movies": "Movies",
"Download": "Download",
@@ -502,5 +505,6 @@
"carousel_go_to": "Go to slide `x`",
"timeline_parse_error_placeholder_heading": "Unable to parse item",
"timeline_parse_error_placeholder_message": "Invidious encountered an error while trying to parse this item. For more information see below:",
"timeline_parse_error_show_technical_details": "Show technical details"
"timeline_parse_error_show_technical_details": "Show technical details",
"dmca_content": "This video cannot be downloaded on this instance due to a DMCA/copyright infringement letter sent to the instance administrator."
}

View File

@@ -39,8 +39,6 @@
"User ID": "Uzula identigilo",
"Password": "Pasvorto",
"Time (h:mm:ss):": "Horo (h:mm:ss):",
"Text CAPTCHA": "Teksta CAPTCHA",
"Image CAPTCHA": "Bilda CAPTCHA",
"Sign In": "Ensaluti",
"Register": "Registriĝi",
"E-mail": "Retpoŝto",

View File

@@ -39,8 +39,6 @@
"User ID": "Nombre",
"Password": "Contraseña",
"Time (h:mm:ss):": "Hora (h:mm:ss):",
"Text CAPTCHA": "CAPTCHA en texto",
"Image CAPTCHA": "CAPTCHA en imagen",
"Sign In": "Iniciar sesión",
"Register": "Registrarse",
"E-mail": "Correo",
@@ -78,6 +76,8 @@
"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: ",
"preferences_default_playlist": "Lista de reproducción por defecto: ",
"preferences_default_playlist_none": "Ninguna lista de reproducción por defecto establecida",
"published": "fecha de publicación",
"published - reverse": "fecha de publicación: orden inverso",
"alphabetically": "alfabéticamente",
@@ -317,7 +317,7 @@
"`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",
"channel_tab_videos_label": "Vídeos",
"Playlists": "Listas de reproducción",
"channel_tab_community_label": "Comunidad",
"search_filters_sort_option_relevance": "Relevancia",
@@ -437,7 +437,7 @@
"generic_count_seconds_2": "{{count}} segundos",
"crash_page_before_reporting": "Antes de notificar un error asegúrate de que has:",
"crash_page_switch_instance": "probado a <a href=\"`x`\">usar otra instancia</a>",
"crash_page_read_the_faq": "leído las <a href=\"`x`\">Preguntas Frecuentes</a>",
"crash_page_read_the_faq": "lee 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>",
@@ -480,7 +480,7 @@
"tokens_count_2": "{{count}} tokens",
"search_message_use_another_instance": "También puedes <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>",
"error_video_not_in_playlist": "El vídeo que has solicitado 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",
@@ -518,5 +518,8 @@
"Filipino (auto-generated)": "Filipino (generados automáticamente)",
"channel_tab_posts_label": "Publicaciones",
"First page": "Primera página",
"channel_tab_courses_label": "Cursos"
"channel_tab_courses_label": "Cursos",
"timeline_parse_error_show_technical_details": "Enseñar detalles técnicos",
"timeline_parse_error_placeholder_message": "Invidious ha encontrado un error al tratar de procesar este elemento. Para más información ver abajo:",
"timeline_parse_error_placeholder_heading": "Imposible procesar este elemento"
}

View File

@@ -5,7 +5,7 @@
"View channel on YouTube": "Vaata kanalit YouTube'is",
"Log in": "Logi sisse",
"Log in/register": "Logi sisse/registreeru",
"Dark mode: ": "Tume režiim: ",
"Dark mode: ": "Tume kujundus: ",
"generic_videos_count": "{{count}} video",
"generic_videos_count_plural": "{{count}} videot",
"generic_subscribers_count": "{{count}} tellija",
@@ -22,12 +22,12 @@
"last": "viimane",
"Next page": "Järgmine leht",
"Previous page": "Eelmine leht",
"Clear watch history?": "Kustuta vaatamiste ajalugu?",
"Clear watch history?": "Kas kustutame vaatamiste ajaloo?",
"New password": "Uus salasõna",
"New passwords must match": "Uued salasõnad peavad ühtima",
"Import and Export Data": "Impordi ja ekspordi andmed",
"Import": "Impordi",
"Import YouTube subscriptions": "Impordi tellimused Youtube'ist/OPML-ist",
"Import YouTube subscriptions": "Impordi CSV või OPML-vormingus Youtube'i tellimused",
"Import FreeTube subscriptions (.db)": "Impordi tellimused FreeTube'ist (.db)",
"Import NewPipe data (.zip)": "Impordi NewPipe'i andmed (.zip)",
"Export": "Ekspordi",
@@ -37,11 +37,9 @@
"History": "Ajalugu",
"JavaScript license information": "JavaScripti litsentsi info",
"source": "allikas",
"User ID": "Kasutada ID",
"User ID": "Kasutajatunnus",
"Password": "Salasõna",
"Time (h:mm:ss):": "Aeg (h:mm:ss):",
"Text CAPTCHA": "CAPTCHA-tekst",
"Image CAPTCHA": "CAPTCHA-foto",
"Sign In": "Logi sisse",
"Register": "Registreeru",
"E-mail": "E-post",
@@ -57,48 +55,48 @@
"preferences_quality_dash_option_auto": "Automaatne",
"preferences_quality_dash_option_best": "Parim",
"preferences_quality_dash_option_worst": "Halvim",
"preferences_volume_label": "Video helitugevus: ",
"preferences_volume_label": "Video helivaljus: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_related_videos_label": "Näita sarnaseid videosid: ",
"preferences_related_videos_label": "Näita sarnaseid videoid: ",
"preferences_vr_mode_label": "Interaktiivne 360-kraadine video (vajalik WebGL): ",
"preferences_dark_mode_label": "Teema: ",
"preferences_dark_mode_label": "Kujundus: ",
"dark": "tume",
"light": "hele",
"preferences_category_subscription": "Tellimuse seaded",
"preferences_category_subscription": "Tellimuse eelistused",
"preferences_max_results_label": "Avalehel näidatavate videote arv: ",
"preferences_sort_label": "Sorteeri: ",
"published": "avaldatud",
"alphabetically": "tähestikulises järjekorras",
"alphabetically - reverse": "vastupidi tähestikulises järjekorras",
"channel name": "kanali nimi",
"preferences_unseen_only_label": "Näita ainult vaatamata videosid: ",
"preferences_unseen_only_label": "Näita ainult vaatamata videoid: ",
"Only show latest video from channel: ": "Näita ainult viimast videot: ",
"preferences_notifications_only_label": "Näita ainult teavitusi (kui neid on): ",
"Enable web notifications": "Luba veebiteavitused",
"`x` uploaded a video": "`x` laadis video üles",
"`x` is live": "`x` teeb otseülekannet",
"preferences_category_data": "Andme-eelistused",
"Clear watch history": "Puhasta vaatamisajalugu",
"Clear watch history": "Kustuta vaatamisajalugu",
"Import/export data": "Impordi/ekspordi andmed",
"Change password": "Muuda salasõna",
"Watch history": "Vaatamisajalugu",
"Delete account": "Kustuta kasutaja",
"Save preferences": "Salvesta eelistused",
"Token": "Token",
"Token": "Tunnusluba",
"Import/export": "Imprort/eksport",
"unsubscribe": "loobu tellimusest",
"Subscriptions": "Tellimused",
"search": "otsi",
"Source available here.": "Allikas on kättesaadaval siin.",
"View privacy policy.": "Vaata privaatsuspoliitikat.",
"Source available here.": "Lähtekood on kättesaadaval siin.",
"View privacy policy.": "Vaata andmekaitsepõhimõtteid.",
"Public": "Avalik",
"Private": "Privaatne",
"View all playlists": "Vaata kõiki esitusloendeid",
"Updated `x` ago": "Uuendas `x` tagasi",
"Delete playlist `x`?": "Kustuta esitusloend `x`?",
"Delete playlist": "Kustuta esitusloend",
"Create playlist": "Loo esitlusloend",
"Create playlist": "Koosta esitlusloend",
"Title": "Pealkiri",
"Playlist privacy": "Esitusloendi privaatsus",
"Show more": "Näita rohkem",
@@ -117,14 +115,14 @@
"Show replies": "Näita vastuseid",
"Incorrect password": "Vale salasõna",
"Wrong answer": "Vale vastus",
"User ID is a required field": "Kasutaja ID on kohustuslik väli",
"User ID is a required field": "Kasutajatunnus on kohustuslik väli",
"Password is a required field": "Salasõna on kohustuslik väli",
"Wrong username or password": "Vale kasutajanimi või salasõna",
"Password cannot be longer than 55 characters": "Salasõna ei tohi olla pikem kui 55 tähemärki",
"Password cannot be empty": "Salasõna ei tohi olla tühi",
"Please log in": "Palun logige sisse",
"Please log in": "Palun logi sisse",
"channel:`x`": "kanal:`x`",
"Deleted or invalid channel": "Kanal on kustutatud või seda ei leitud",
"Deleted or invalid channel": "Kanal on kustutatud või seda ei leidu",
"This channel does not exist.": "Sellist kanalit pole olemas.",
"comments_view_x_replies": "{{count}} vastus",
"comments_view_x_replies_plural": "{{count}} vastust",
@@ -134,86 +132,86 @@
"Not a playlist.": "Tegu pole esitusloendiga.",
"Playlist does not exist.": "Seda esitusloendit pole olemas.",
"No such user": "Sellist kasutajat pole",
"English": "Inglise",
"English (United Kingdom)": "Inglise (Suurbritannia)",
"English (United States)": "Inglise (USA)",
"English (auto-generated)": "Inglise (automaatselt koostatud)",
"Afrikaans": "Afrikaani",
"Albanian": "Albaania",
"Arabic": "Araabia",
"Armenian": "Armeenia",
"Bangla": "Bengali",
"Basque": "Baski",
"Belarusian": "Valgevene",
"Bulgarian": "Bulgaaria",
"Burmese": "Birma",
"Cantonese (Hong Kong)": "Kantoni (Hong Konk)",
"Chinese (China)": "Hiina (Hiina)",
"Chinese (Hong Kong)": "Hiina (Hong Kong)",
"Chinese (Simplified)": "Hiina (lihtsustatud)",
"Chinese (Taiwan)": "Hiina (Taiwan)",
"Croatian": "Horvaatia",
"Czech": "Tšehhi",
"Danish": "Taani",
"Dutch": "Hollandi",
"Esperanto": "Esperanto",
"Estonian": "Eesti",
"Filipino": "Filipiini",
"Finnish": "Soome",
"French": "Prantsuse",
"French (auto-generated)": "Prantsuse (automaatne)",
"Dutch (auto-generated)": "Hollandi (automaatne)",
"Galician": "Kaliitsia",
"Georgian": "Gruusia",
"Haitian Creole": "Haiti kreool",
"Hausa": "Hausa",
"Hawaiian": "Havaii",
"Hebrew": "Heebrea",
"Hindi": "Hindi",
"Hungarian": "Ungari",
"Icelandic": "Islandi",
"Indonesian": "Indoneesia",
"Japanese (auto-generated)": "Jaapani (automaatne)",
"Kannada": "Kannada",
"Kazakh": "Kasahhi",
"Luxembourgish": "Luksemburgi",
"Macedonian": "Makedoonia",
"Malay": "Malai",
"Maltese": "Malta",
"Maori": "Maori",
"Marathi": "Marathi",
"Mongolian": "Mongoli",
"Nepali": "Nepaali",
"Norwegian Bokmål": "Norra (Bokmål)",
"Persian": "Pärsia",
"Polish": "Poola",
"Portuguese": "Portugali",
"Portuguese (auto-generated)": "Portugali (automaatne)",
"Portuguese (Brazil)": "Portugali (Brasiilia)",
"Romanian": "Rumeenia",
"Russian": "Vene",
"Russian (auto-generated)": "Vene (automaatne)",
"Scottish Gaelic": "Šoti (Gaeli)",
"Serbian": "Serbia",
"Slovak": "Slovaki",
"Slovenian": "Sloveeni",
"Somali": "Somaali",
"Spanish": "Hispaania",
"Spanish (auto-generated)": "Hispaania (automaatne)",
"Spanish (Latin America)": "Hispaania (Ladina-Ameerika)",
"Spanish (Mexico)": "Hispaania (Mehhiko)",
"Spanish (Spain)": "Hispaania (Hispaania)",
"Swahili": "Suahili",
"Swedish": "Rootsi",
"Tajik": "Tadžiki",
"Tamil": "Tamiili",
"Thai": "Tai",
"Turkish": "Türgi",
"Turkish (auto-generated)": "Türgi (automaatne)",
"Ukrainian": "Ukraina",
"Uzbek": "Usbeki",
"Vietnamese": "Vietnami",
"Vietnamese (auto-generated)": "Vietnami (automaatne)",
"English": "inglise",
"English (United Kingdom)": "inglise (Suurbritannia)",
"English (United States)": "inglise (USA)",
"English (auto-generated)": "inglise (automaatselt koostatud)",
"Afrikaans": "afrikaani",
"Albanian": "albaania",
"Arabic": "araabia",
"Armenian": "armeenia",
"Bangla": "bengali",
"Basque": "baski",
"Belarusian": "valgevene",
"Bulgarian": "bulgaaria",
"Burmese": "birma",
"Cantonese (Hong Kong)": "kantoni (Hongkong)",
"Chinese (China)": "hiina (Hiina)",
"Chinese (Hong Kong)": "hiina (Hongkong)",
"Chinese (Simplified)": "hiina (lihtsustatud)",
"Chinese (Taiwan)": "hiina (Taiwan)",
"Croatian": "horvaadi",
"Czech": "tšehhi",
"Danish": "taani",
"Dutch": "hollandi",
"Esperanto": "esperanto",
"Estonian": "eesti",
"Filipino": "filipiini",
"Finnish": "soome",
"French": "prantsuse",
"French (auto-generated)": "prantsuse (automaatselt koostatud)",
"Dutch (auto-generated)": "hollandi (automaatne)",
"Galician": "galeegi",
"Georgian": "gruusia",
"Haitian Creole": "haiti kreooli",
"Hausa": "hausa",
"Hawaiian": "havaii",
"Hebrew": "heebrea",
"Hindi": "hindi",
"Hungarian": "ungari",
"Icelandic": "islandi",
"Indonesian": "indoneesia",
"Japanese (auto-generated)": "jaapani (automaatselt koostatud)",
"Kannada": "kannada",
"Kazakh": "kasahhi",
"Luxembourgish": "letseburgi",
"Macedonian": "makedoonia",
"Malay": "malai",
"Maltese": "malta",
"Maori": "maoori",
"Marathi": "marathi",
"Mongolian": "mongoli",
"Nepali": "nepaali",
"Norwegian Bokmål": "norra (Bokmål)",
"Persian": "pärsia",
"Polish": "poola",
"Portuguese": "portugali",
"Portuguese (auto-generated)": "portugali (automaatne)",
"Portuguese (Brazil)": "portugali (Brasiilia)",
"Romanian": "rumeenia",
"Russian": "vene",
"Russian (auto-generated)": "vene (automaatne)",
"Scottish Gaelic": "gaeli",
"Serbian": "serbia",
"Slovak": "slovaki",
"Slovenian": "sloveeni",
"Somali": "somaali",
"Spanish": "hispaania",
"Spanish (auto-generated)": "hispaania (automaatne)",
"Spanish (Latin America)": "hispaania (Ladina-Ameerika)",
"Spanish (Mexico)": "hispaania (Mehhiko)",
"Spanish (Spain)": "hispaania (Hispaania)",
"Swahili": "suahiili",
"Swedish": "rootsi",
"Tajik": "tadžiki",
"Tamil": "tamili",
"Thai": "tai",
"Turkish": "türgi",
"Turkish (auto-generated)": "türgi (automaatne)",
"Ukrainian": "ukraina",
"Uzbek": "usbeki",
"Vietnamese": "vietnami",
"Vietnamese (auto-generated)": "vietnami (automaatne)",
"generic_count_years": "{{count}} aasta",
"generic_count_years_plural": "{{count}} aastat",
"generic_count_months": "{{count}} kuu",
@@ -228,15 +226,15 @@
"generic_count_minutes_plural": "{{count}} minutit",
"Popular": "Populaarne",
"Search": "Otsi",
"Top": "Top",
"About": "Leheküljest",
"Top": "Parimad",
"About": "Saidi teave",
"preferences_locale_label": "Keel: ",
"View as playlist": "Vaata esitusloendina",
"Movies": "Filmid",
"Download as: ": "Laadi kui: ",
"Download as: ": "Laadi alla kui: ",
"(edited)": "(muudetud)",
"`x` marked it with a ❤": "`x` märkis ❤",
"Audio mode": "Audiorežiim",
"Audio mode": "Helirežiim",
"Video mode": "Videorežiim",
"search_filters_date_label": "Üleslaadimise kuupäev",
"search_filters_date_option_none": "Ükskõik mis kuupäev",
@@ -246,10 +244,10 @@
"search_filters_date_option_month": "Sel kuul",
"search_filters_date_option_year": "Sel aastal",
"search_filters_type_label": "Tüüp",
"search_filters_type_option_all": "Ükskõik mis tüüp",
"search_filters_type_option_all": "Ükskõik mis tüüpi",
"search_filters_duration_label": "Kestus",
"search_filters_type_option_show": "Näita",
"search_filters_duration_option_none": "Ükskõik mis kestus",
"search_filters_duration_option_none": "Ükskõik mis kestusega",
"search_filters_duration_option_short": "Lühike (alla 4 minuti)",
"search_filters_duration_option_medium": "Keskmine (4 - 20 minutit)",
"search_filters_duration_option_long": "Pikk (üle 20 minuti)",
@@ -258,9 +256,9 @@
"search_filters_features_option_hd": "HD",
"search_filters_features_option_subtitles": "Subtiitrid",
"search_filters_features_option_location": "Asukoht",
"search_filters_sort_label": "Sorteeri",
"search_filters_sort_label": "Järjestus",
"search_filters_sort_option_views": "Vaatamiste arv",
"next_steps_error_message": "Pärast mida võiksite proovida: ",
"next_steps_error_message": "Pärast seda võiksid proovida: ",
"videoinfo_started_streaming_x_ago": "Alustas otseülekannet `x` tagasi",
"Yes": "Jah",
"generic_views_count": "{{count}} vaatamine",
@@ -270,48 +268,48 @@
"preferences_region_label": "Riik: ",
"View YouTube comments": "Vaata YouTube'i kommentaare",
"preferences_extend_desc_label": "Ava video kirjeldus automaatselt: ",
"German (auto-generated)": "Saksa (automaatne)",
"Italian": "Itaalia",
"preferences_player_style_label": "Mängija stiil: ",
"German (auto-generated)": "saksa (automaatselt koostatud)",
"Italian": "itaalia",
"preferences_player_style_label": "Meediaesitaja stiil: ",
"subscriptions_unseen_notifs_count": "{{count}} lugemata teavitus",
"subscriptions_unseen_notifs_count_plural": "{{count}} lugemata teavitust",
"View more comments on Reddit": "Vaata teisi kommentaare Redditis",
"Only show latest unwatched video from channel: ": "Näita ainult viimast vaatamata videot: ",
"tokens_count": "{{count}} token",
"tokens_count_plural": "{{count}} tokenit",
"tokens_count": "{{count}} tunnusluba",
"tokens_count_plural": "{{count}} tunnusluba",
"Log out": "Logi välja",
"Premieres `x`": "Linastub`x`",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Vaata `x` kommentaari",
"": "Vaata `x` kommentaare"
},
"Khmer": "Khmeeri",
"Bosnian": "Bosnia",
"Corsican": "Korsika",
"Javanese": "Jaava",
"Lithuanian": "Leedu",
"Khmer": "khmeeri",
"Bosnian": "bosnia",
"Corsican": "korsika",
"Javanese": "jaava",
"Lithuanian": "leedu",
"channel_tab_videos_label": "Videod",
"channel_tab_community_label": "Kogukond",
"CAPTCHA is a required field": "CAPTCHA on kohustuslik väli",
"CAPTCHA is a required field": "Robotilõks on kohustuslik väli",
"comments_points_count": "{{count}} punkt",
"comments_points_count_plural": "{{count}} punkti",
"Chinese": "Hiina",
"German": "Saksa",
"Indonesian (auto-generated)": "Indoneesia (automaatne)",
"Italian (auto-generated)": "Itaalia (automaatne)",
"Kyrgyz": "Kirkiisi",
"Latin": "Ladina",
"Chinese": "hiina",
"German": "saksa",
"Indonesian (auto-generated)": "indoneesia (automaatselt koostatud)",
"Italian (auto-generated)": "itaalia (automaatselt koostatud)",
"Kyrgyz": "kirgiisi",
"Latin": "ladina",
"generic_count_seconds": "{{count}} sekund",
"generic_count_seconds_plural": "{{count}} sekundit",
"Catalan": "Katalaani",
"Chinese (Traditional)": "Hiina (traditsiooniline)",
"Greek": "Kreeka",
"Kurdish": "Kurdi",
"Latvian": "Läti",
"Irish": "Iiri",
"Korean": "Korea",
"Japanese": "Jaapani",
"Korean (auto-generated)": "Korea (automaatne)",
"Catalan": "katalaani",
"Chinese (Traditional)": "hiina (traditsiooniline)",
"Greek": "kreeka",
"Kurdish": "kurdi",
"Latvian": "läti",
"Irish": "iiri",
"Korean": "korea",
"Japanese": "jaapani",
"Korean (auto-generated)": "korea (automaatselt koostatud)",
"Music": "Muusika",
"Playlists": "Esitusloendid",
"search_filters_type_option_video": "Video",
@@ -325,8 +323,186 @@
"search_filters_type_option_channel": "Kanal",
"search_filters_type_option_playlist": "Esitusloend",
"search_filters_type_option_movie": "Film",
"next_steps_error_message_go_to_youtube": "Minna YouTube'i",
"next_steps_error_message_refresh": "Laadida uuesti",
"next_steps_error_message_go_to_youtube": "Mine YouTube'i",
"next_steps_error_message_refresh": "Laadi uuesti",
"footer_donate_page": "Anneta",
"videoinfo_watch_on_youTube": "Vaata YouTube'is"
"videoinfo_watch_on_youTube": "Vaata YouTube'is",
"Authorize token for `x`?": "Kas volitad tunnusloa kasutamise `x`-le?",
"Export data as JSON": "Expordi Invidious andmed JSON-ina",
"Import Invidious data": "Impordi Invidious JSON andmed",
"preferences_local_label": "Edasta videod vaheserveri kaudu: ",
"Music in this video": "Muusika selles videos",
"Token manager": "Tunnuslubade haldur",
"search_message_use_another_instance": "Võid ka <a href=\"`x`\">otsida teisest serverist</a>.",
"Standard YouTube license": "Tavaline Youtube'i litsens",
"Song: ": "Lugu: ",
"Add to playlist": "Lisa esitlusloendisse",
"Add to playlist: ": "Lisa esitlusloendisse: ",
"Search for videos": "Otsi videoid",
"The Popular feed has been disabled by the administrator.": "Administraator on populaarse voo välja lülitanud.",
"preferences_quality_dash_option_2160p": "2160p",
"generic_button_rss": "RSS uudisvoog",
"Import YouTube watch history (.json)": "Impordi Youtube vaatamiste ajalugu (.json)",
"published - reverse": "avaldatud - vastupidine",
"preferences_default_home_label": "Vaikimisi koduleht: ",
"preferences_feed_menu_label": "Voogude menüü: ",
"Login enabled: ": "Sisselogimine lubatud: ",
"Registration enabled: ": "Registreerimine lubatud: ",
"CAPTCHA enabled: ": "Robotilõks on kasutusel: ",
"Blacklisted regions: ": "Mustas nimekirjas piirkonnad: ",
"Wilson score: ": "Wilsoni skoor: ",
"generic_button_delete": "Kustuta",
"generic_button_edit": "Muuda",
"generic_button_save": "Salvesta",
"generic_button_cancel": "Tühista",
"Import YouTube playlist (.csv)": "Impordi Youtube esitlusloend (.csv)",
"preferences_category_misc": "Muud seadistused",
"preferences_annotations_subscribed_label": "Kas vaikimisi näitame tellitud kanalite sisukokkuvõtteid?: ",
"preferences_quality_dash_option_480p": "480p",
"preferences_continue_label": "Vaikimisi mängi järgmine video: ",
"View JavaScript license information.": "Vaata JavaScripti litsensiteavet.",
"preferences_listen_label": "Kuula vaikimisi: ",
"preferences_quality_dash_option_1080p": "1080p",
"Erroneous CAPTCHA": "Vigane robotilõks",
"Hidden field \"challenge\" is a required field": "Peidetud väli \"väljakutse\" on kohustuslik väli",
"Fallback captions: ": "Tagavara subtiitrid: ",
"preferences_category_admin": "Administraatori seadistused",
"preferences_automatic_instance_redirect_label": "Automaatne serveri ümbersuunamine (varuvariandile redirect.invidious.io): ",
"channel name - reverse": "kanali nimi - vastupidine",
"An alternative front-end to YouTube": "Alternatiivne Youtube esiliides",
"Subscription manager": "Tellimuste haldur",
"Redirect homepage to feed: ": "Suuna koduleht voole: ",
"Azerbaijani": "aserbaidžaani",
"Gujarati": "gudžarati",
"generic_channels_count": "{{count}} kanal",
"generic_channels_count_plural": "{{count}} kanalit",
"preferences_video_loop_label": "Alati korda: ",
"preferences_watch_history_label": "Lülita vaatamiste ajalugu sisse: ",
"preferences_speed_label": "Vaikimisi kiirus: ",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_240p": "240p",
"preferences_captions_label": "Vaikimisi subtiitrid: ",
"preferences_annotations_label": "Vaikimisi näita sisukokkuvõtteid: ",
"preferences_thin_mode_label": "Napp režiim: ",
"Manage subscriptions": "Halda tellimusi",
"Manage tokens": "Halda tunnuslube",
"preferences_show_nick_label": "Näita üleval hüüdnime ",
"revoke": "võta tagasi",
"Released under the AGPLv3 on Github.": "Avaldatud GitHubis AGPLv3 litsentsi alusel.",
"Trending": "Trendikas",
"Unlisted": "Ajajooneväline",
"Switch Invidious Instance": "Vaheta Invidiouse Serverit",
"Whitelisted regions: ": "Valges nimekirjas piirkonnad: ",
"Artist: ": "Esitaja: ",
"Could not fetch comments": "Kommentaaride laadimine ei õnnestunud",
"Album: ": "Album: ",
"Invidious Private Feed for `x`": "Invidiouse privaatne Voog `x`-ile",
"Could not pull trending pages.": "Ei saanud alla laadida trendikaid lehti.",
"Hidden field \"token\" is a required field": "Peidetud väli \"tunnusluba\" on kohustuslik väli",
"Erroneous challenge": "Ekslik väljakutse",
"Erroneous token": "Ekslik tunnusluba",
"Token is expired, please try again": "Tunnusluba on aegunud, palun proovi uuesti",
"Amharic": "amhari",
"Cebuano": "sebu",
"preferences_autoplay_label": "Automaatesitus: ",
"invidious": "Invidious",
"preferences_quality_dash_option_144p": "144p",
"Popular enabled: ": "Populaarsed videod on kasutusel: ",
"Top enabled: ": "Ülariba lubatud: ",
"Editing playlist `x`": "Esitlusloendi `x` muutmine",
"Show annotations": "Näita sisukokkuvõtteid",
"Hide annotations": "Peida sisukokkuvõtted",
"Could not create mix.": "Ei saanud miksi luua.",
"Authorize token?": "Kas volitad tunnusloa kasutamise?",
"playlist_button_add_items": "Lisa videoid",
"First page": "Esimene leht",
"preferences_preload_label": "Eellaadi videoandmed: ",
"preferences_category_visual": "Visuaalsed seadistused",
"preferences_comments_label": "Vaikimisi kommentaarid: ",
"Filipino (auto-generated)": "filipiini (automaatselt koostatud)",
"Could not get channel info.": "Kanali info tuvastamine ei õnnestunud.",
"Answer": "Vastus",
"Report statistics: ": "Teavita statistikast: ",
"Hmong": "hmongi",
"Igbo": "igbo",
"Interlingue": "interlingue",
"Lao": "lao",
"Malagasy": "malagassi",
"Malayalam": "malajalami",
"Pashto": "puštu",
"Nyanja": "njandža",
"Punjabi": "pandžabi",
"Samoan": "samoa",
"Shona": "šona",
"Sindhi": "sindhi",
"Sinhala": "singali",
"Southern Sotho": "lõunasotho",
"Sundanese": "sunda",
"Telugu": "telugu",
"Urdu": "urdu",
"Welsh": "kõmri",
"Western Frisian": "läänefriisi",
"Xhosa": "koosa",
"Yiddish": "jidiši",
"Yoruba": "joruba",
"Zulu": "suulu",
"Fallback comments: ": "Kommentaaride tagavaravariant: ",
"Rating: ": "Hinnang: ",
"Default": "Vaikimisi",
"Download is disabled": "Allalaadimine on keelatud",
"YouTube comment permalink": "YouTube'i kommentaari püsilink",
"permalink": "püsilink",
"Channel Sponsor": "Kanali sponsor",
"search_filters_features_label": "Omadused",
"search_filters_features_option_c_commons": "Creative Commons litsents",
"search_filters_features_option_three_sixty": "360°-video",
"search_filters_features_option_vr180": "VR180-video",
"search_filters_features_option_three_d": "3D-video",
"search_filters_features_option_hdr": "HDR-video",
"search_filters_features_option_purchased": "Ostetud",
"search_filters_sort_option_relevance": "Olulisus",
"search_filters_sort_option_rating": "Hinnang",
"search_filters_apply_button": "Rakenda valitud filtrid",
"footer_source_code": "Lähtekood",
"footer_original_source_code": "Algne lähtekood",
"footer_modfied_source_code": "Muudetud lähtekood",
"none": "mitte midagi",
"videoinfo_youTube_embed_link": "Lõimi",
"videoinfo_invidious_embed_link": "Lõimi link",
"adminprefs_modified_source_code_url_label": "Link muudetud lähtekoodi hoidlale",
"channel_tab_podcasts_label": "Taskuhäälingud",
"Engagement: ": "Kaasatus: ",
"download_subtitles": "Subtiitrid - `x` (.vtt)",
"user_created_playlists": "`x` - koostatud esitusloendid",
"user_saved_playlists": "`x` - salvestatud esitusloendid",
"Video unavailable": "Video pole saadaval",
"preferences_save_player_pos_label": "Salvesta taasesituse asukoht: ",
"crash_page_you_found_a_bug": "Tundub, et oled Invidiousest leidnud vea!",
"crash_page_before_reporting": "Enne veast teatamist, palun kontrolli, et oleksid:",
"crash_page_refresh": "proovinud <a href=\"`x`\">lehte uuesti laadida</a>",
"crash_page_switch_instance": "proovinud <a href=\"`x`\">kasutada mõnda muud Invidiouse serverit</a>",
"crash_page_read_the_faq": "lugenud <a href=\"`x`\">Korduma kippuvaid küsimusi (KKK)</a>",
"crash_page_search_issue": "otsinud <a href=\"`x`\">GitHubist sarnaseid ja juba teaeatud vigu</a>",
"crash_page_report_issue": "Kui ükski ülaltoodud võimalustest seda viga ei lahendanud, siis palun <a href=\"`x`\">koosta GitHubis meie veahalduses uus veateade</a> (soovitavalt inglise keeles) ja lisa sinnakogu järgnev tekst (palun ÄRA tõlgi seda teksti):",
"error_video_not_in_playlist": "Selles esitusloendis ei leidu soovitud videot. <a href=\"`x`\">Siit pääsed esitusloendi avalehele.</a>",
"channel_tab_shorts_label": "Lühivideod",
"channel_tab_streams_label": "Otseülekanded",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"channel_tab_releases_label": "Versioonid",
"channel_tab_courses_label": "Kursused",
"channel_tab_playlists_label": "Esitusloendid",
"channel_tab_posts_label": "Postitused",
"channel_tab_channels_label": "Kanalid",
"toggle_theme": "Vaheta kujundust",
"carousel_slide": "Slaid {{current}} / {{total}}",
"carousel_skip": "Jäta karussell vahele",
"carousel_go_to": "Ava slaid `x`",
"timeline_parse_error_placeholder_heading": "Objekti töötlemine ei õnnestu",
"timeline_parse_error_placeholder_message": "Selle objekti töötlemisel tekkis Invidiouses viga. Lisateave on alljärgnevas:",
"timeline_parse_error_show_technical_details": "Näita tehnilisi üksikasju",
"preferences_default_playlist": "Vaikimisi esitusloend: ",
"preferences_default_playlist_none": "Ühtegi vaikimisi esitusloendit ei leidu"
}

View File

@@ -38,8 +38,6 @@
"User ID": "Erabiltzaile IDa",
"Password": "Pasahitza",
"Time (h:mm:ss):": "Denbora (h:mm:ss):",
"Text CAPTCHA": "CAPTCHA testua",
"Image CAPTCHA": "CAPTCHA irudia",
"Sign In": "Hasi saioa",
"Register": "Eman izena",
"E-mail": "E-posta",

View File

@@ -1,12 +1,12 @@
{
"generic_views_count": "{{count}} بازدید",
"generic_views_count_plural": "{{count}} بازدید",
"generic_videos_count": "{{count}} ویدئو",
"generic_videos_count_plural": "{{count}} ویدئو",
"generic_videos_count": "{{count}} ویدیو",
"generic_videos_count_plural": "{{count}} ویدیو",
"generic_playlists_count": "{{count}} فهرست پخش",
"generic_playlists_count_plural": "{{count}} فهرست پخش",
"generic_subscribers_count": "{{count}} دنبال کننده",
"generic_subscribers_count_plural": "{{count}} دنبال کننده",
"generic_subscribers_count": "{{count}} دنبالکننده",
"generic_subscribers_count_plural": "{{count}} دنبالکننده",
"generic_subscriptions_count": "{{count}} اشتراک",
"generic_subscriptions_count_plural": "{{count}} اشتراک",
"LIVE": "زنده",
@@ -24,21 +24,21 @@
"Clear watch history?": "پاک کردن تاریخچه نمایش؟",
"New password": "گذرواژه تازه",
"New passwords must match": "گذارواژه های تازه باید باهم همخوانی داشته باشند",
"Authorize token?": "توکن دسترسی؟",
"Authorize token for `x`?": "توکن دسترسی برای `x`؟",
"Yes": "بله",
"No": "خیر",
"Authorize token?": "اجازه دادن به توکن؟",
"Authorize token for `x`?": "اجازه دادن به توکن برای `x`؟",
"Yes": "آری",
"No": "نه",
"Import and Export Data": "درون‌برد و برون‌برد داده",
"Import": "درون‌برد",
"Import Invidious data": "وارد کردن داده JSON اینویدیوس",
"Import YouTube subscriptions": "وارد کردن فایل CSV یا OPML سابسکرایب های یوتیوب",
"Import Invidious data": "درون‌برد داده JSON اینویدیوس",
"Import YouTube subscriptions": "درون‌برد پروندهٔ CSV یا 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": "گرفتن(خارج کردن) اطلاعات اینویدیوس با فرمت JSON",
"Export data as JSON": "برون‌برد دادهٔ اینویدیوس به‌عنوان JSON",
"Delete account?": "حذف حساب کاربری؟",
"History": "تاریخچه",
"An alternative front-end to YouTube": "یک پیشانه جایگزین برای یوتیوب",
@@ -49,15 +49,13 @@
"User ID": "شناسه کاربری",
"Password": "گذرواژه",
"Time (h:mm:ss):": "زمان (h:mm:ss):",
"Text CAPTCHA": "کپچای متنی",
"Image CAPTCHA": "کپچای تصویری",
"Sign In": "ورود",
"Register": "ثبت نام",
"E-mail": "ایمیل",
"Preferences": "ترجیحات",
"preferences_category_player": "ترجیحات نمایش‌دهنده",
"preferences_video_loop_label": "همواره ویدئو را بازپخش کن ",
"preferences_autoplay_label": "نمایش خودکار: ",
"preferences_video_loop_label": "همیشه بازپخش کن: ",
"preferences_autoplay_label": "پخش خودکار: ",
"preferences_continue_label": "پخش بعدی به طور پیشفرض: ",
"preferences_continue_autoplay_label": "پخش خودکار ویدیو بعدی: ",
"preferences_listen_label": "گوش کردن به طور پیشفرض: ",
@@ -68,14 +66,14 @@
"preferences_comments_label": "نظرات پیشفرض: ",
"youtube": "یوتیوب",
"reddit": "ردیت",
"preferences_captions_label": "زیرنویس های پیشفرض: ",
"Fallback captions: ": "عقب گرد زیرنویس ها: ",
"preferences_related_videos_label": "نمایش ویدیو های مرتبط: ",
"preferences_annotations_label": "نمایش حاشیه نویسی ها به طور پیشفرض: ",
"preferences_extend_desc_label": "گسترش خودکار توضیحات ویدئو: ",
"preferences_vr_mode_label": "ویدئوها ۳۶۰ درجه تعاملی(نیازمند WebGL): ",
"preferences_captions_label": "زیرنویسهای پیشفرض: ",
"Fallback captions: ": "عقبگرد زیرنویسها: ",
"preferences_related_videos_label": "نمایش ویدیوهای مرتبط: ",
"preferences_annotations_label": "نمایش حاشیهنویسیها بهطور پیشفرض: ",
"preferences_extend_desc_label": "گسترش خودکار توضیحات ویدیو: ",
"preferences_vr_mode_label": "ویدیوهای ۳۶۰ درجهٔ تعاملی (نیازمند WebGL): ",
"preferences_category_visual": "ترجیحات بصری",
"preferences_player_style_label": "حالت پخش کننده: ",
"preferences_player_style_label": "حالت پخشکننده: ",
"Dark mode: ": "حالت تاریک: ",
"preferences_dark_mode_label": "تم: ",
"dark": "تاریک",
@@ -84,7 +82,7 @@
"preferences_category_misc": "ترجیحات متفرقه",
"preferences_automatic_instance_redirect_label": "هدایت خودکار نمونه (انتقال به redirect.invidious.io): ",
"preferences_category_subscription": "ترجیحات اشتراک",
"preferences_annotations_subscribed_label": "نمایش حاشیه نویسی ها به طور پیشفرض برای کانال های مشترک شده: ",
"preferences_annotations_subscribed_label": "نمایش حاشیهنویسیها بهطور پیشفرض برای کانالهای مشترکشده: ",
"Redirect homepage to feed: ": "تغییر مسیر صفحه خانه به خوراک: ",
"preferences_max_results_label": "تعداد ویدیو های نمایش داده شده در خوراک: ",
"preferences_sort_label": "مرتب سازی ویدیو ها بر اساس: ",
@@ -383,21 +381,21 @@
"next_steps_error_message_refresh": "تازه‌سازی",
"next_steps_error_message_go_to_youtube": "رفتن به یوتیوب",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_dash": "DASH (کیفیت تطبیفی)",
"preferences_quality_option_dash": "DASH (کیفیت سازگارشونده)",
"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",
"preferences_quality_dash_option_4320p": "۴۳۲۰p",
"preferences_quality_dash_option_2160p": "۲۱۶۰p",
"preferences_quality_dash_option_1440p": "۱۴۴۰p",
"preferences_quality_dash_option_1080p": "۱۰۸۰p",
"preferences_quality_dash_option_720p": "۷۲۰p",
"preferences_quality_dash_option_480p": "۴۸۰p",
"preferences_quality_dash_option_360p": "۳۶۰p",
"preferences_quality_dash_option_240p": "۲۴۰p",
"preferences_quality_dash_option_144p": "۱۴۴p",
"invidious": "اینویدیوس",
"search_filters_features_option_three_sixty": "360°",
"footer_donate_page": "کمک مالی",
@@ -476,8 +474,8 @@
"generic_button_rss": "خوراک RSS",
"crash_page_read_the_faq": "که <a href=\"`x`\">سوالات بیشتر پرسیده شده (FAQ)</a> را خوانده‌اید",
"generic_button_delete": "حذف",
"Import YouTube playlist (.csv)": "واردکردن فهرست‌پخش YouTube (.csv)",
"Import YouTube watch history (.json)": "وارد کردن فهرست پخش YouTube (.json)",
"Import YouTube playlist (.csv)": "درون‌برد فهرست‌پخش YouTube (.csv)",
"Import YouTube watch history (.json)": "درون‌برد تاریخچهٔ تماشای یوتیوب (.json)",
"crash_page_you_found_a_bug": "به نظر می‌رسد که ایرادی در Invidious پیدا کرده‌اید!",
"channel_tab_podcasts_label": "پادکست‌ها",
"channel_tab_streams_label": "پخش زنده‌ها",
@@ -485,10 +483,10 @@
"channel_tab_playlists_label": "فهرست‌های پخش",
"channel_tab_channels_label": "کانال‌ها",
"error_video_not_in_playlist": "ویدیوی درخواستی معلق به این فهرست پخش نیست. <a href=\"`x`\">کلیک کنید تا به صفحهٔ اصلی فهرست پخش بروید.</a>",
"Add to playlist": "به لیست پخش افزوده شود",
"Add to playlist": "افزودن به فهرست پخش",
"Answer": "پاسخ",
"Search for videos": "جست و جو برای ویدیوها",
"Add to playlist: ": "افزودن به لیست پخش ",
"Search for videos": "جست‌وجو برای ویدیوها",
"Add to playlist: ": "افزودن به فهرست پخش ",
"The Popular feed has been disabled by the administrator.": "بخش ویدیوهای پرطرفدار توسط مدیر غیرفعال شده است.",
"carousel_slide": "اسلاید {{current}} از {{total}}",
"carousel_skip": "رد شدن از گرداننده",
@@ -497,5 +495,11 @@
"crash_page_report_issue": "اگر هیچ یک از روش های بالا کمکی نکردند لطفا <a href=\"`x`\"> (ترجیحا به انگلیسی) یک سوال جدید در گیت هاب بپرسید و </a> طوری که سوالتون شامل متن زیر باشه:",
"channel_tab_releases_label": "آثار",
"toggle_theme": "تغییر وضعیت تم",
"preferences_preload_label": "پیش بار کردن داده‌های ویدیو: "
"preferences_preload_label": "پیش بار کردن داده‌های ویدیو: ",
"First page": "نخستین صفحه",
"Filipino (auto-generated)": "فیلیپنی (تولید خودکار)",
"channel_tab_posts_label": "فرسته‌ها",
"timeline_parse_error_placeholder_heading": "ناتوانی در تجزیهٔ مورد",
"timeline_parse_error_placeholder_message": "اینویدیوس هنگام کوشش برای تجزیهٔ این مورد به خطایی برخورد. برای اطلاعات بیشتر زیر را ببینید:",
"timeline_parse_error_show_technical_details": "نمایش جزئیات فنی"
}

View File

@@ -39,8 +39,6 @@
"User ID": "Käyttäjätunnus",
"Password": "Salasana",
"Time (h:mm:ss):": "Aika (h:mm:ss):",
"Text CAPTCHA": "Teksti-CAPTCHA",
"Image CAPTCHA": "Kuva-CAPTCHA",
"Sign In": "Kirjaudu sisään",
"Register": "Rekisteröidy",
"E-mail": "Sähköposti",
@@ -497,5 +495,7 @@
"The Popular feed has been disabled by the administrator.": "Järjestelmänvalvoja on poistanut Suositut-syötteen.",
"Import YouTube watch history (.json)": "Tuo Youtube-katseluhistoria (.json)",
"toggle_theme": "Vaihda teemaa",
"preferences_preload_label": "Esilataa video data. "
"preferences_preload_label": "Esilataa video data. ",
"timeline_parse_error_show_technical_details": "Näytä tekniset yksityiskohdat",
"First page": "Ensimmäinen sivu"
}

View File

@@ -62,8 +62,6 @@
"User ID": "Identifiant utilisateur",
"Password": "Mot de passe",
"Time (h:mm:ss):": "Heure (h:mm:ss) :",
"Text CAPTCHA": "CAPTCHA textuel",
"Image CAPTCHA": "CAPTCHA pictural",
"Sign In": "S'identifier",
"Register": "S'inscrire",
"E-mail": "Courriel",
@@ -518,5 +516,6 @@
"preferences_preload_label": "Précharger les données de la vidéo : ",
"First page": "Première page",
"channel_tab_courses_label": "Cours",
"channel_tab_posts_label": "Messages"
"channel_tab_posts_label": "Messages",
"timeline_parse_error_show_technical_details": "Afficher les détails techniques"
}

506
locales/gsw.json Normal file
View File

@@ -0,0 +1,506 @@
{
"Add to playlist": "Enere Widergabelischte hinzuefüege",
"Add to playlist: ": "Enere Widergabelischte hinzuefüege: ",
"Answer": "Antwort",
"Search for videos": "Nach Videos sueche",
"The Popular feed has been disabled by the administrator.": "De Feed für beliebti Inhält isch vom Administrator deaktiviert worde.",
"generic_channels_count": "{{count}} Kanal",
"generic_channels_count_plural": "{{count}} Kanäl",
"generic_views_count": "{{count}} Uufruef",
"generic_views_count_plural": "{{count}} Uufrüef",
"generic_videos_count": "{{count}} Video",
"generic_videos_count_plural": "{{count}} Videos",
"generic_playlists_count": "{{count}} Widergabelischte",
"generic_playlists_count_plural": "{{count}} Widergabelischtene",
"generic_subscribers_count": "{{count}} Abonnent",
"generic_subscribers_count_plural": "{{count}} Abonnente",
"generic_subscriptions_count": "{{count}} Abo",
"generic_subscriptions_count_plural": "{{count}} Abos",
"generic_button_delete": "Lösche",
"generic_button_edit": "Bearbeite",
"generic_button_save": "Speichere",
"generic_button_cancel": "Abbreche",
"generic_button_rss": "RSS",
"LIVE": "LIVE",
"Shared `x` ago": "Vor `x` teilt",
"Unsubscribe": "Abo beende",
"Subscribe": "Abonniere",
"View channel on YouTube": "Kanal uf YouTube aazeige",
"View playlist on YouTube": "Widergabelischte uf YouTube aazeige",
"newest": "neusti",
"oldest": "ältisti",
"popular": "beliebtisti",
"last": "neusti",
"Next page": "Nächsti Siite",
"Previous page": "Vorherigi Siite",
"First page": "Ersti Siite",
"Clear watch history?": "Widergabeverlauf lösche?",
"New password": "Neus Passwort",
"New passwords must match": "Neui Passwörter müend übereinstimme",
"Authorize token?": "Token autorisiere?",
"Authorize token for `x`?": "Token für `x` autorisiere?",
"Yes": "Ja",
"No": "Nei",
"Import and Export Data": "Date importiere und exportiere",
"Import": "Importiere",
"Import Invidious data": "Invidious-JSON-Date importiere",
"Import YouTube subscriptions": "YouTube-CSV/OPML-Abonnements importiere",
"Import YouTube playlist (.csv)": "YouTube-Widergabelischte importiere (.csv)",
"Import YouTube watch history (.json)": "YouTube-Widergabeverlauf importiere (.json)",
"Import FreeTube subscriptions (.db)": "FreeTube Abonnements importiere (.db)",
"Import NewPipe subscriptions (.json)": "NewPipe Abonnements importiere (.json)",
"Import NewPipe data (.zip)": "NewPipe Date importiere (.zip)",
"Export": "Exportiere",
"Export subscriptions as OPML": "Abonnements als OPML exportiere",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Abonnements als OPML exportiere (für NewPipe & FreeTube)",
"Export data as JSON": "Invidious-Date als JSON exportiere",
"Delete account?": "Konto lösche?",
"History": "Verlauf",
"An alternative front-end to YouTube": "En alternativi Oberflächi für YouTube",
"JavaScript license information": "JavaScript Lizenzinformatione",
"source": "Quelle",
"Log in": "Aamelde",
"Log in/register": "Aamelde/registriere",
"User ID": "Benutzer-ID",
"Password": "Passwort",
"Time (h:mm:ss):": "Ziit (h:mm:ss):",
"Sign In": "Aamelde",
"Register": "Registriere",
"E-mail": "E-Mail",
"Preferences": "Iistellige",
"preferences_category_player": "Widergabeiistellige",
"preferences_video_loop_label": "Immer widerhole: ",
"preferences_preload_label": "Videodate vorlade: ",
"preferences_autoplay_label": "Automatisch abspiele: ",
"preferences_continue_label": "Immer automatisch nächsts Video abspiele: ",
"preferences_continue_autoplay_label": "Nächsts Video automatisch abspiele: ",
"preferences_listen_label": "Nur Ton als Standard: ",
"preferences_local_label": "Videos dur Proxy leite: ",
"preferences_watch_history_label": "Widergabeverlauf aktiviere: ",
"preferences_speed_label": "Standardgschwindigkeit: ",
"preferences_quality_label": "Bevorzugti Videoqualität: ",
"preferences_quality_option_dash": "DASH (adaptivi Qualität)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Mittel",
"preferences_quality_option_small": "Niedrig",
"preferences_quality_dash_label": "Bevorzugti DASH-Videoqualität: ",
"preferences_quality_dash_option_auto": "Auto",
"preferences_quality_dash_option_best": "Höchsti",
"preferences_quality_dash_option_worst": "Niedrigsti",
"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": "Widergabeluutstärchi: ",
"preferences_comments_label": "Standardkommentär: ",
"youtube": "YouTube",
"reddit": "Reddit",
"invidious": "Invidious",
"preferences_captions_label": "Standarduntertitel: ",
"Fallback captions: ": "Ersatzuntertitel: ",
"preferences_related_videos_label": "Ähnlichi Videos aazeige: ",
"preferences_annotations_label": "Aamerkige standardmässig aazeige: ",
"preferences_extend_desc_label": "Videobeschriibig automatisch erwiitere: ",
"preferences_vr_mode_label": "Interaktivi 360-Grad-Videos (bruucht WebGL): ",
"preferences_category_visual": "Aazeigeiistellige",
"preferences_region_label": "Land vo de Inhält: ",
"preferences_player_style_label": "Player-Stil: ",
"Dark mode: ": "Nachtmodus: ",
"preferences_dark_mode_label": "Modus: ",
"dark": "Nachtmodus",
"light": "hell",
"preferences_thin_mode_label": "Schlanke Modus: ",
"preferences_category_misc": "Suschtigi Iistellige",
"preferences_automatic_instance_redirect_label": "Automatischi Instanzwiiterleitig (über redirect.invidious.io): ",
"preferences_category_subscription": "Abonnementiistellige",
"preferences_annotations_subscribed_label": "Aamerkige für abonnierti Kanäl standardmässig aazeige? ",
"Redirect homepage to feed: ": "Startsiite zu Feed umleite: ",
"preferences_max_results_label": "Aazahl vo Videos wo im Feed aazeigt werded: ",
"preferences_sort_label": "Videos sortiere nach: ",
"published": "veröffentlicht",
"published - reverse": "veröffentlicht - invertiert",
"alphabetically": "alphabetisch",
"alphabetically - reverse": "alphabetisch - invertiert",
"channel name": "Kanalname",
"channel name - reverse": "Kanalname - invertiert",
"Only show latest video from channel: ": "Nur neusti Videos vom Kanal aazeige: ",
"Only show latest unwatched video from channel: ": "Neu neusti ungseheni Videos vom Kanal aazeige: ",
"preferences_unseen_only_label": "Nur ungseheni aazeige: ",
"preferences_notifications_only_label": "Nur Benachrichtigunge aazeige (wenns welchi git): ",
"Enable web notifications": "Webbenachrichtigunge aktiviere",
"`x` uploaded a video": "`x` het es Video ufeglade",
"`x` is live": "`x` isch live",
"preferences_category_data": "Dateiistellige",
"Clear watch history": "Verlauf lösche",
"Import/export data": "Date importiere/exportiere",
"Change password": "Passwort ändere",
"Manage subscriptions": "Abonnements verwalte",
"Manage tokens": "Tokens verwalte",
"Watch history": "Widergabeverlauf",
"Delete account": "Account lösche",
"preferences_category_admin": "Administrator-Iistellige",
"preferences_default_home_label": "Standard-Startsiite: ",
"preferences_feed_menu_label": "Feed-Menü: ",
"preferences_show_nick_label": "Nutzernäme obe aazeige: ",
"Popular enabled: ": "„Beliebt“-Siite aktiviert: ",
"Top enabled: ": "Top aktiviert? ",
"CAPTCHA enabled: ": "CAPTCHA aktiviert? ",
"Login enabled: ": "Aameldig aktiviert: ",
"Registration enabled: ": "Registrierig aktiviert: ",
"Report statistics: ": "Statistike brichte: ",
"Save preferences": "Iistellige speichere",
"Subscription manager": "Abonnementsverwaltig",
"Token manager": "Tokenverwaltig",
"Token": "Token",
"tokens_count": "{{count}} Token",
"tokens_count_plural": "{{count}} Tokens",
"Import/export": "Importiere/Exportiere",
"unsubscribe": "abbstelle",
"revoke": "widerrüefe",
"Subscriptions": "Abonnements",
"subscriptions_unseen_notifs_count": "{{count}} ungsehni Benachrichtigung",
"subscriptions_unseen_notifs_count_plural": "{{count}} ungsehni Benachrichtigunge",
"search": "Sueche",
"Log out": "Abmelde",
"Released under the AGPLv3 on Github.": "Uf GitHub under de AGPLv3 Lizenz veröffentlicht.",
"Source available here.": "Quellcode da verfüegbar.",
"View JavaScript license information.": "JavaScript-Lizenzinformatione aazeige.",
"View privacy policy.": "Dateschutzerchlärig iigseh.",
"Trending": "Aagseit",
"Public": "Öffentlich",
"Unlisted": "Nöd glischtet",
"Private": "Privat",
"View all playlists": "Alli Widergabelischtene aazeige",
"Updated `x` ago": "Aktualisiert vor `x`",
"Delete playlist `x`?": "Widergabelischte `x` lösche?",
"Delete playlist": "Widergabelischte lösche",
"Create playlist": "Widergabelischte erstelle",
"Title": "Titel",
"Playlist privacy": "Widergabelischte-Privatsphäri",
"Editing playlist `x`": "Widergabelischte `x` bearbeite",
"playlist_button_add_items": "Videos hinzuefüege",
"Show more": "Meh aazeige",
"Show less": "Weniger aazeige",
"Watch on YouTube": "Video uf YouTube aaluege",
"Switch Invidious Instance": "Invidious Instanz wechsle",
"search_message_no_results": "Kei Ergebnis gfunde.",
"search_message_change_filters_or_query": "Versuech, dini Suechaafrag z erwiitere und/oder d Filter z ändere.",
"search_message_use_another_instance": "Du chasch au <a href=\"`x`\">uf ere andere Instanz sueche</a>.",
"Hide annotations": "Aamerkige uusblende",
"Show annotations": "Aamerkige aazeige",
"Genre: ": "Genre: ",
"License: ": "Lizenz: ",
"Standard YouTube license": "Standard YouTube-Lizenz",
"Family friendly? ": "Familiefründlich? ",
"Wilson score: ": "Wilson-Score: ",
"Engagement: ": "Engagement: ",
"Whitelisted regions: ": "Erlaubti Regione: ",
"Blacklisted regions: ": "Unerlaubti Regione: ",
"Music in this video": "Musig i dem Video",
"Artist: ": "Künschtler: ",
"Song: ": "Musig: ",
"Album: ": "Album: ",
"Shared `x`": "Teilt `x`",
"Premieres in `x`": "Premiere i `x`",
"Premieres `x`": "Premiere `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! Anschinend hesch du JavaScript deaktiviert. Klick da, zum Kommentär aazzeige, beacht, dass es chli länger duure cha, zum sie z lade.",
"View YouTube comments": "YouTube Kommentär aazeige",
"View more comments on Reddit": "Meh Kommentär uf Reddit aazeige",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` Kommentar aazeige",
"": "`x` Kommentär aazeige"
},
"View Reddit comments": "Reddit-Kommentär aazeige",
"Hide replies": "Antworte verstecke",
"Show replies": "Antworte aazeige",
"Incorrect password": "Falschs Passwort",
"Wrong answer": "Ungültigi Antwort",
"Erroneous CAPTCHA": "Ungültigs CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA isch en erforderlichi Iigab",
"User ID is a required field": "Benutzer ID isch en erforderlichi Iigab",
"Password is a required field": "Passwort isch en erforderlichi Iigab",
"Wrong username or password": "Ungültige Benutzername oder Passwort",
"Password cannot be empty": "Passwort derf nöd leer sii",
"Password cannot be longer than 55 characters": "Passwort derf nöd länger als 55 Zeiche sii",
"Please log in": "Bitte aamelde",
"Invidious Private Feed for `x`": "Invidious Persönliche Feed für `x`",
"channel:`x`": "Kanal:`x`",
"Deleted or invalid channel": "Glöschte oder ungültige Kanal",
"This channel does not exist.": "De Kanal existiert nöd.",
"Could not get channel info.": "Kanalinformatione hend nöd chönne glade werde.",
"Could not fetch comments": "Kommentär hend nöd chönne glade werde",
"comments_view_x_replies": "{{count}} Antwort aazeige",
"comments_view_x_replies_plural": "{{count}} Antworte aazeige",
"`x` ago": "vor `x`",
"Load more": "Meh lade",
"comments_points_count": "{{count}} Punkt",
"comments_points_count_plural": "{{count}} Pünkt",
"Could not create mix.": "Mix het nöd chönne erstellt werde.",
"Empty playlist": "Widergabelischte isch leer",
"Not a playlist.": "Ungültigi Widergabelischte.",
"Playlist does not exist.": "Widergabelischte existiert nöd.",
"Could not pull trending pages.": "Beliebt-Siitene hend nöd chönne glade werde.",
"Hidden field \"challenge\" is a required field": "Versteckts Feld „challenge“ isch en erforderlichi Iigab",
"Hidden field \"token\" is a required field": "Versteckts Feld „token“ isch en erforderlichi Iigab",
"Erroneous challenge": "Ungültige Test",
"Erroneous token": "Ungültige Token",
"No such user": "Ungültige Benutzer",
"Token is expired, please try again": "Token isch abgloffe, bitte nomal versueche",
"generic_count_years": "{{count}} Jahr",
"generic_count_years_plural": "{{count}} Jahr",
"generic_count_months": "{{count}} Monet",
"generic_count_months_plural": "{{count}} Mönet",
"generic_count_weeks": "{{count}} Wuche",
"generic_count_weeks_plural": "{{count}} Wuche",
"generic_count_days": "{{count}} Tag",
"generic_count_days_plural": "{{count}} Täg",
"generic_count_hours": "{{count}} Stund",
"generic_count_hours_plural": "{{count}} Stunde",
"generic_count_minutes": "{{count}} Minute",
"generic_count_minutes_plural": "{{count}} Minute",
"generic_count_seconds": "{{count}} Sekunde",
"generic_count_seconds_plural": "{{count}} Sekunde",
"Fallback comments: ": "Alternativi Kommentär: ",
"Popular": "Populär",
"Search": "Sueche",
"Top": "Top",
"About": "Über",
"Rating: ": "Bewertig: ",
"preferences_locale_label": "Spraach: ",
"View as playlist": "Als Widergabelischte aazeige",
"Default": "Standard",
"Music": "Musig",
"Gaming": "Videospiel",
"News": "Neuigkeite",
"Movies": "Film",
"Download": "Abelade",
"Download as: ": "Abelade als: ",
"Download is disabled": "Abelade isch deaktiviert",
"%A %B %-d, %Y": "%A %-d %B %Y",
"(edited)": "(bearbeitet)",
"YouTube comment permalink": "YouTube-Kommentar Permalink",
"permalink": "Permalink",
"`x` marked it with a ❤": "`x` hets mitme ❤ markiert",
"Channel Sponsor": "Kanalsponsor",
"Audio mode": "Audiomodus",
"Video mode": "Videomodus",
"Playlists": "Widergabelischtene",
"search_filters_title": "Filtere",
"search_filters_date_label": "Upload-Datum",
"search_filters_date_option_none": "Bliebigs Datum",
"search_filters_date_option_hour": "Letschti Stund",
"search_filters_date_option_today": "Hüt",
"search_filters_date_option_week": "Die Wuche",
"search_filters_date_option_month": "De Monet",
"search_filters_date_option_year": "Das Jahr",
"search_filters_type_label": "Inhaltstyp",
"search_filters_type_option_all": "Bliebige Typ",
"search_filters_type_option_video": "Video",
"search_filters_type_option_channel": "Kanal",
"search_filters_type_option_playlist": "Widergabelischte",
"search_filters_type_option_movie": "Film",
"search_filters_type_option_show": "Aazeige",
"search_filters_duration_label": "Duur",
"search_filters_duration_option_none": "Bliebigi Längi",
"search_filters_duration_option_short": "Churz (< 4 Minute)",
"search_filters_duration_option_medium": "Mittel (4 - 20 Minute)",
"search_filters_duration_option_long": "Lang (> 20 Minute)",
"search_filters_features_label": "Eigeschafte",
"search_filters_features_option_live": "Live",
"search_filters_features_option_four_k": "4K",
"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_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": "Standort",
"search_filters_features_option_purchased": "Kauft",
"search_filters_sort_label": "Sortiere nach",
"search_filters_sort_option_relevance": "Relevanz",
"search_filters_sort_option_rating": "Bewertig",
"search_filters_sort_option_date": "Ueladedatum",
"search_filters_sort_option_views": "Uufrüef",
"search_filters_apply_button": "Uusgwählti Filter aawende",
"Current version: ": "Aktuelli Version: ",
"next_steps_error_message": "Nachher das versueche: ",
"next_steps_error_message_refresh": "Aktualisiere",
"next_steps_error_message_go_to_youtube": "Zu YouTube gah",
"footer_donate_page": "Spende",
"footer_documentation": "Dokumentation",
"footer_source_code": "Quellcode",
"footer_original_source_code": "Original Quellcode",
"footer_modfied_source_code": "Modifizierte Quellcode",
"adminprefs_modified_source_code_url_label": "URL zum Repository vom modifizierte Quellcode",
"none": "kei",
"videoinfo_started_streaming_x_ago": "Stream het vor `x` aagfange",
"videoinfo_watch_on_youTube": "Uf YouTube aaluege",
"videoinfo_youTube_embed_link": "Iibettet",
"videoinfo_invidious_embed_link": "Link zum Iibette",
"download_subtitles": "Untertitel - `x` (.vtt)",
"user_created_playlists": "`x` Widergabelischtene erstellt",
"user_saved_playlists": "`x` Widergabelischtene gspeicheret",
"Video unavailable": "Video nöd verfüegbar",
"preferences_save_player_pos_label": "Widergabeposition speichere: ",
"crash_page_you_found_a_bug": "Anschinend hesch du en Fehler in Invidious gfunde!",
"crash_page_before_reporting": "Bevor du en Bug meldsch, stell sicher, dass du:",
"crash_page_refresh": "Versuecht hesch, <a href=\"`x`\">d Siite neu z lade</a>",
"crash_page_switch_instance": "En <a href=\"`x`\">anderi Instanz</a> versuecht hesch",
"crash_page_read_the_faq": "S <a href=\"`x`\">FAQ</a> glese hesch",
"crash_page_search_issue": "Nach <a href=\"`x`\">scho gmeldete Bugs uf GitHub</a> gsuecht hesch",
"crash_page_report_issue": "Wenn all das nöd ghulfe het, <a href=\"`x`\">öffne bitte es neus Problem (issue) uf GitHub</a> (vorzugswiis uf Englisch) und füeg de folgendi Text i dini Nachricht ii (bitte übersetz de Text NÖD):",
"error_video_not_in_playlist": "S agforderete Video existiert nöd i dere Widergabelischte. <a href=\"`x`\">Klick da, zum zur Startsiite vo de Widergabelischte z cho.</a>",
"channel_tab_videos_label": "Videos",
"channel_tab_shorts_label": "Shorts",
"channel_tab_streams_label": "Livestreams",
"channel_tab_podcasts_label": "Podcasts",
"channel_tab_releases_label": "Veröffentlichige",
"channel_tab_courses_label": "Kürs",
"channel_tab_playlists_label": "Widergabelischtene",
"channel_tab_community_label": "Community",
"channel_tab_posts_label": "Biiträg",
"channel_tab_channels_label": "Kanäl",
"toggle_theme": "Thema wechsle",
"carousel_slide": "Siite {{current}} vo {{total}}",
"carousel_skip": "Galerie überspringe",
"carousel_go_to": "Zu Element `x` springe",
"timeline_parse_error_placeholder_heading": "Element cha nöd parsed werde",
"timeline_parse_error_placeholder_message": "Invidious isch bim Parse vo dem Element uf en Fehler gstosse. Für wiiteri Information lueg da une:",
"timeline_parse_error_show_technical_details": "Technischi Details aazeige",
"English": "Englisch",
"English (United Kingdom)": "Englisch (Vereinigts Königriich)",
"English (United States)": "Englisch (Vereinigti Staate)",
"English (auto-generated)": "Englisch (automatisch generiert)",
"Afrikaans": "Afrikaans",
"Albanian": "Albanisch",
"Amharic": "Amharisch",
"Arabic": "Arabisch",
"Armenian": "Armenisch",
"Azerbaijani": "Aserbaidschanisch",
"Bangla": "Bengalisch",
"Basque": "Baskisch",
"Belarusian": "Wiissrussisch",
"Bosnian": "Bosnisch",
"Bulgarian": "Bulgarisch",
"Burmese": "Burmesisch",
"Cantonese (Hong Kong)": "Kantonesisch (Hong Kong)",
"Catalan": "Katalanisch",
"Cebuano": "Cebuano",
"Chinese": "Chinesisch",
"Chinese (China)": "Chinesisch (China)",
"Chinese (Hong Kong)": "Chinesisch (Hong Kong)",
"Chinese (Simplified)": "Chinesisch (vereifacht)",
"Chinese (Taiwan)": "Chinesisch (Taiwan)",
"Chinese (Traditional)": "Chinesisch (traditionell)",
"Corsican": "Korsisch",
"Croatian": "Kroatisch",
"Czech": "Tschechisch",
"Danish": "Dänisch",
"Dutch": "Niederländisch",
"Dutch (auto-generated)": "Niederländisch (automatisch generiert)",
"Esperanto": "Esperanto",
"Estonian": "Estnisch",
"Filipino": "Philippinisch",
"Filipino (auto-generated)": "Philippinisch (automatisch generiert)",
"Finnish": "Finnisch",
"French": "Französisch",
"French (auto-generated)": "Französisch (automatisch generiert)",
"Galician": "Galizisch",
"Georgian": "Gerogisch",
"German": "Dütsch",
"German (auto-generated)": "Dütsch (automatisch generiert)",
"Greek": "Griechisch",
"Gujarati": "Gujarati",
"Haitian Creole": "Haitianischs Kreolisch",
"Hausa": "Hausa",
"Hawaiian": "Hawaiianisch",
"Hebrew": "Hebräisch",
"Hindi": "Hindi",
"Hmong": "Hmong",
"Hungarian": "Ungarisch",
"Icelandic": "Isländisch",
"Igbo": "Igbo",
"Indonesian": "Indonesisch",
"Indonesian (auto-generated)": "Indonesisch (automatisch generiert)",
"Interlingue": "Interlingue",
"Irish": "Irisch",
"Italian": "Italienisch",
"Italian (auto-generated)": "Italienisch (automatisch generiert)",
"Japanese": "Japanisch",
"Japanese (auto-generated)": "Japanisch (automatisch generiert)",
"Javanese": "Javanisch",
"Kannada": "Kannada",
"Kazakh": "Kasachisch",
"Khmer": "Khmer",
"Korean": "Koreanisch",
"Korean (auto-generated)": "Koreanisch (automatisch generiert)",
"Kurdish": "Kurdisch",
"Kyrgyz": "Kirgisisch",
"Lao": "Laotisch",
"Latin": "Latinisch",
"Latvian": "Lettisch",
"Lithuanian": "Litauisch",
"Luxembourgish": "Luxeburgisch",
"Macedonian": "Mazedonisch",
"Malagasy": "Madagassisch",
"Malay": "Malaiisch",
"Malayalam": "Malayalam",
"Maltese": "Maltesisch",
"Maori": "Maori",
"Marathi": "Marathi",
"Mongolian": "Mongolisch",
"Nepali": "Nepalesisch",
"Norwegian Bokmål": "Norwegisch",
"Nyanja": "Nyanja",
"Pashto": "Paschtunisch",
"Persian": "Persisch",
"Polish": "Polnisch",
"Portuguese": "Portugiesisch",
"Portuguese (auto-generated)": "Portugiesisch (automatisch generiert)",
"Portuguese (Brazil)": "Portugiesisch (Brasilie)",
"Punjabi": "Pandschabi",
"Romanian": "Rumänisch",
"Russian": "Russisch",
"Russian (auto-generated)": "Russisch (automatisch generiert)",
"Samoan": "Samoanisch",
"Scottish Gaelic": "Schottischs Gällisch",
"Serbian": "Serbisch",
"Shona": "Schona",
"Sindhi": "Sindhi",
"Sinhala": "Singhalesisch",
"Slovak": "Slowakisch",
"Slovenian": "Slowenisch",
"Somali": "Somali",
"Southern Sotho": "Südlichs Sotho",
"Spanish": "Spanisch",
"Spanish (auto-generated)": "Spanisch (automatisch generiert)",
"Spanish (Latin America)": "Spanisch (Latinamerika)",
"Spanish (Mexico)": "Spanisch (Mexiko)",
"Spanish (Spain)": "Spanisch (Spanie)",
"Sundanese": "Sundanesisch",
"Swahili": "Suaheli",
"Swedish": "Schwedisch",
"Tajik": "Tadschikisch",
"Tamil": "Tamilisch",
"Telugu": "Telugu",
"Thai": "Thailändisch",
"Turkish": "Türkisch",
"Turkish (auto-generated)": "Türkisch (automatisch generiert)",
"Ukrainian": "Ukrainisch",
"Urdu": "Urdu",
"Uzbek": "Usbekisch",
"Vietnamese": "Vietnamesisch",
"Vietnamese (auto-generated)": "Vietnamesisch (automatisch generiert)",
"Welsh": "Walisisch",
"Western Frisian": "Weschtfriesisch",
"Xhosa": "Xhosa",
"Yiddish": "Jiddisch",
"Yoruba": "Joruba",
"Zulu": "Zulu"
}

View File

@@ -39,8 +39,6 @@
"User ID": "שם משתמש",
"Password": "סיסמה",
"Time (h:mm:ss):": "זמן (h:mm:ss):",
"Text CAPTCHA": "Text CAPTCHA",
"Image CAPTCHA": "Image CAPTCHA",
"Sign In": "התחברות",
"Register": "הרשמה",
"E-mail": "דוא״ל",

View File

@@ -80,8 +80,6 @@
"Register": "पंजीकृत करें",
"E-mail": "ईमेल",
"Time (h:mm:ss):": "समय (घं:मिमि:सेसे):",
"Text CAPTCHA": "टेक्स्ट CAPTCHA",
"Image CAPTCHA": "चित्र CAPTCHA",
"Sign In": "साइन इन करें",
"Preferences": "प्राथमिकताएँ",
"preferences_category_player": "प्लेयर की प्राथमिकताएँ",
@@ -199,7 +197,7 @@
"Switch Invidious Instance": "Invidious उदाहरण बदलें",
"search_message_no_results": "कोई परिणाम नहीं मिला।",
"search_message_change_filters_or_query": "अपने खोज क्वेरी को और चौड़ा करें और/या फ़िल्टर बदलें।",
"search_message_use_another_instance": " आप <a href=\"`x`\">दूसरे उदाहरण पर भी खोज सकते हैं</a>।",
"search_message_use_another_instance": "आप <a href=\"`x`\">दूसरे उदाहरण पर भी खोज सकते हैं</a>।",
"Hide annotations": "टिप्पणियाँ छिपाएँ",
"Show annotations": "टिप्पणियाँ दिखाएँ",
"Genre: ": "श्रेणी: ",
@@ -434,7 +432,7 @@
"search_filters_features_option_location": "जगह",
"search_filters_features_option_purchased": "खरीदा गया",
"search_filters_sort_label": "इस क्रम से लगाएँ",
"search_filters_sort_option_date": "अपलोड की ताीख",
"search_filters_sort_option_date": "अपलोड की ताीख",
"search_filters_sort_option_views": "देखे जाने की संख्या",
"search_filters_apply_button": "चयनित फ़िल्टर लागू करें",
"footer_documentation": "प्रलेख",
@@ -476,7 +474,7 @@
"generic_button_cancel": "रद्द करें",
"generic_button_rss": "आरएसएस",
"generic_button_edit": "संपादित करें",
"generic_button_delete": "हटाए",
"generic_button_delete": "हटाए",
"playlist_button_add_items": "वीडियो जोड़ें",
"Song: ": "गाना: ",
"channel_tab_podcasts_label": "पाॅडकास्ट",
@@ -496,5 +494,13 @@
"carousel_skip": "कैरोसेल छोड़ें",
"Add to playlist: ": "प्लेलिस्ट में जोड़ें: ",
"Search for videos": "वीडियो खोजें",
"carousel_go_to": "स्लाइड `x` पर जाएँ"
"carousel_go_to": "स्लाइड `x` पर जाएँ",
"First page": "पहला पृष्ठ",
"preferences_preload_label": "वीडियो डेटा प्रीलोड करें: ",
"Filipino (auto-generated)": "फ़िलिपीनो (अपने-आप जनरेट हुआ)",
"channel_tab_courses_label": "कोर्स",
"channel_tab_posts_label": "पोस्ट",
"timeline_parse_error_placeholder_heading": "आयटम को पार्स नहीं किया जा सका",
"timeline_parse_error_placeholder_message": "इस आयटम को पार्स करते समय Invidious को एक त्रुटि आई। अधिक जानकारी के लिए नीचे देखें:",
"timeline_parse_error_show_technical_details": "तकनीकी जानकारी दिखाएँ"
}

View File

@@ -39,8 +39,6 @@
"User ID": "Korisnički ID",
"Password": "Lozinka",
"Time (h:mm:ss):": "Vrijeme (h:mm:ss):",
"Text CAPTCHA": "Tekstualni CAPTCHA",
"Image CAPTCHA": "Slikovni CAPTCHA",
"Sign In": "Prijavi se",
"Register": "Registriraj se",
"E-mail": "E-mail adresa",
@@ -515,5 +513,11 @@
"carousel_go_to": "Idi na kadar `x`",
"carousel_skip": "Preskoči vrtuljak",
"Filipino (auto-generated)": "Filipinski (automatski generirano)",
"preferences_preload_label": "Unaprijed učitaj podatke videa: "
"preferences_preload_label": "Unaprijed učitaj podatke videa: ",
"channel_tab_posts_label": "Objave",
"timeline_parse_error_placeholder_heading": "Nije moguće obraditi stavku",
"timeline_parse_error_placeholder_message": "Invidious je naišao na grešku prilikom obrade ove stavke. Za više informacija pogledajte niže dolje:",
"timeline_parse_error_show_technical_details": "Prikaži tehničke detalje",
"First page": "Prva stranica",
"channel_tab_courses_label": "Tečajevi"
}

View File

@@ -49,8 +49,6 @@
"User ID": "Felhasználói azonosító",
"Password": "Jelszó",
"Time (h:mm:ss):": "A pontos idő (ó:pp:mm):",
"Text CAPTCHA": "Szöveges CAPTCHA kérése",
"Image CAPTCHA": "Kép CAPTCHA kérése",
"Sign In": "Bejelentkezés",
"Register": "Regisztrálás",
"E-mail": "E-mail-cím",

View File

@@ -5,7 +5,6 @@
"oldest": "plus ancian",
"published": "data de publication",
"invidious": "Invidious",
"Image CAPTCHA": "Imagine CAPTCHA",
"newest": "plus nove",
"generic_button_save": "Salveguardar",
"Dark mode: ": "Modo obscur: ",

View File

@@ -44,8 +44,6 @@
"User ID": "ID Pengguna",
"Password": "Kata Sandi",
"Time (h:mm:ss):": "Waktu (j:mm:dd):",
"Text CAPTCHA": "Teks CAPTCHA",
"Image CAPTCHA": "Gambar CAPTCHA",
"Sign In": "Masuk",
"Register": "Daftar",
"E-mail": "Surel",

View File

@@ -39,8 +39,6 @@
"User ID": "Auðkenni notanda",
"Password": "Lykilorð",
"Time (h:mm:ss):": "Tími (h:mm: ss):",
"Text CAPTCHA": "CAPTCHA-texti",
"Image CAPTCHA": "CAPTCHA-mynd",
"Sign In": "Skrá inn",
"Register": "Nýskrá",
"E-mail": "Tölvupóstur",
@@ -501,5 +499,8 @@
"Filipino (auto-generated)": "Filippínska (sjálfvirkt útbúin)",
"channel_tab_posts_label": "Færslur",
"First page": "Fyrsta síða",
"channel_tab_courses_label": "Kennsluefni"
"channel_tab_courses_label": "Kennsluefni",
"timeline_parse_error_placeholder_heading": "Tekst ekki að meðhöndla þetta atriði",
"timeline_parse_error_placeholder_message": "Invidious rakst á villu við að reyna að meðhöndla þetta atriði. Skoðaðu nánari upplýsingar hér fyrir neðan:",
"timeline_parse_error_show_technical_details": "Sýna nánari tæknilegar upplýsingar"
}

View File

@@ -48,8 +48,6 @@
"User ID": "ID utente",
"Password": "Password",
"Time (h:mm:ss):": "Orario (h:mm:ss):",
"Text CAPTCHA": "Testo del CAPTCHA",
"Image CAPTCHA": "Immagine CAPTCHA",
"Sign In": "Accedi",
"Register": "Registrati",
"E-mail": "E-mail",
@@ -129,7 +127,7 @@
"subscriptions_unseen_notifs_count_0": "{{count}} notifica non visualizzata",
"subscriptions_unseen_notifs_count_1": "{{count}} notifiche non visualizzate",
"subscriptions_unseen_notifs_count_2": "{{count}} notifiche non visualizzate",
"search": "Cerca",
"search": "cerca",
"Log out": "Esci",
"Source available here.": "Codice sorgente.",
"View JavaScript license information.": "Guarda le informazioni di licenza del codice JavaScript.",
@@ -518,5 +516,8 @@
"Filipino (auto-generated)": "Filippino (generati automaticamente)",
"First page": "Prima pagina",
"channel_tab_courses_label": "Corsi",
"channel_tab_posts_label": "Post"
"channel_tab_posts_label": "Post",
"timeline_parse_error_show_technical_details": "Mostra i dettagli tecnici",
"timeline_parse_error_placeholder_message": "Invidious ha riscontrato un errore tentando di leggere questo elemento. Per altre informazioni vedi di seguito:",
"timeline_parse_error_placeholder_heading": "Lettura elemento non riuscita"
}

View File

@@ -44,8 +44,6 @@
"User ID": "ユーザー ID",
"Password": "パスワード",
"Time (h:mm:ss):": "時間 (時:分分:秒秒):",
"Text CAPTCHA": "テキスト CAPTCHA",
"Image CAPTCHA": "画像 CAPTCHA",
"Sign In": "サインイン",
"Register": "登録",
"E-mail": "メールアドレス",
@@ -484,5 +482,10 @@
"Filipino (auto-generated)": "フィリピノ語 (自動生成)",
"First page": "最初のページ",
"channel_tab_posts_label": "投稿",
"channel_tab_courses_label": "コース"
"channel_tab_courses_label": "コース",
"timeline_parse_error_placeholder_message": "Invidious によるこの項目の解析中にエラーが発生。詳細は以下:",
"timeline_parse_error_placeholder_heading": "この項目を解析できません",
"timeline_parse_error_show_technical_details": "技術的詳細を表示",
"preferences_default_playlist": "デフォルトのプレイリスト: ",
"preferences_default_playlist_none": "デフォルトのプレイリストは設定されていません"
}

View File

@@ -36,8 +36,6 @@
"Register": "회원가입",
"Sign In": "로그인",
"preferences_category_misc": "기타 설정",
"Image CAPTCHA": "이미지 캡차",
"Text CAPTCHA": "텍스트 캡차",
"Time (h:mm:ss):": "시각 (h:mm:ss):",
"Password": "비밀번호",
"User ID": "사용자 ID",

View File

@@ -44,8 +44,6 @@
"JavaScript license information": "Informaziòn su la licensa JavaScript",
"source": "font",
"Log in": "Và dent",
"Text CAPTCHA": "Tèst del CAPTCHA",
"Image CAPTCHA": "Imàgen del CAPTCHA",
"Sign In": "Ven denter",
"Register": "Registres",
"E-mail": "E-mail",

View File

@@ -39,8 +39,6 @@
"User ID": "Naudotojo ID",
"Password": "Slaptažodis",
"Time (h:mm:ss):": "Laikas (h:mm:ss):",
"Text CAPTCHA": "CAPTCHA tekstas",
"Image CAPTCHA": "CAPTCHA paveikslėlis",
"Sign In": "Prisijungti",
"Register": "Registruotis",
"E-mail": "El. paštas",

View File

@@ -65,5 +65,79 @@
"youtube": "YouTube",
"Add to playlist: ": "Pievienot atskaņošanas sarakstam: ",
"Subscribe": "Abonēt",
"View channel on YouTube": "Skatīt kanālu YouTube vietnē"
"View channel on YouTube": "Skatīt kanālu YouTube vietnē",
"LIVE": "TIEŠRAIDE",
"Export": "Izgūt",
"preferences_dark_mode_label": "Motīvs: ",
"published": "Publicēšanas datuma",
"preferences_sort_label": "Kārtot video pēc: ",
"search_filters_sort_label": "Kārtot pēc",
"search_filters_sort_option_date": "Augšupielādes datuma",
"search_filters_sort_option_views": "Skatījumu skaita",
"published - reverse": "Publicēšanas datuma apgrieztā secībā",
"generic_views_count_0": "{{count}} skatījumi",
"generic_views_count_1": "{{count}} skatījums",
"generic_views_count_2": "{{count}} skatījumi",
"generic_videos_count_0": "{{count}} video",
"generic_videos_count_1": "{{count}} video",
"generic_videos_count_2": "{{count}} video",
"generic_playlists_count_0": "{{count}} atskaņošanas saraksti",
"generic_playlists_count_1": "{{count}} atskaņošanas saraksts",
"generic_playlists_count_2": "{{count}} atskaņošanas saraksti",
"generic_subscriptions_count_0": "{{count}} abonementi",
"generic_subscriptions_count_1": "{{count}} abonements",
"generic_subscriptions_count_2": "{{count}} abonementi",
"subscriptions_unseen_notifs_count_0": "{{count}} jauni paziņojumi",
"subscriptions_unseen_notifs_count_1": "{{count}} jauns paziņojums",
"subscriptions_unseen_notifs_count_2": "{{count}} jauni paziņojumi",
"comments_view_x_replies_0": "Skatīt {{count}} atbildes",
"comments_view_x_replies_1": "Skatīt {{count}} atbildi",
"comments_view_x_replies_2": "Skatīt {{count}} atbildes",
"generic_count_years_0": "{{count}} gadi",
"generic_count_years_1": "{{count}} gads",
"generic_count_years_2": "{{count}} gadi",
"generic_count_months_0": "{{count}} mēneši",
"generic_count_months_1": "{{count}} mēnesis",
"generic_count_months_2": "{{count}} mēneši",
"generic_count_weeks_0": "{{count}} nedēļas",
"generic_count_weeks_1": "{{count}} nedēļa",
"generic_count_weeks_2": "{{count}} nedēļas",
"generic_count_days_0": "{{count}} dienas",
"generic_count_days_1": "{{count}} diena",
"generic_count_days_2": "{{count}} dienas",
"generic_count_hours_0": "{{count}} stundas",
"generic_count_hours_1": "{{count}} stunda",
"generic_count_hours_2": "{{count}} stundas",
"generic_count_minutes_0": "{{count}} minūtes",
"generic_count_minutes_1": "{{count}} minūte",
"generic_count_minutes_2": "{{count}} minūtes",
"generic_count_seconds_0": "{{count}} sekundes",
"generic_count_seconds_1": "{{count}} sekunde",
"generic_count_seconds_2": "{{count}} sekundes",
"Import YouTube playlist (.csv)": "Ievietot YouTube atskaņošanas sarakstu (.csv)",
"Import YouTube watch history (.json)": "Ievietot YouTube skatīto video vēsturi (.json)",
"Import FreeTube subscriptions (.db)": "Ievietot FreeTube abonementus (.db)",
"Import NewPipe subscriptions (.json)": "Ievietot NewPipe abonementus (.json)",
"Import NewPipe data (.zip)": "Ievietot NewPipe datus (.zip)",
"Export subscriptions as OPML": "Izgūt abonementus OPML formātā",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Izgūt abonementus OPML formātā (der NewPipe un FreeTube lietotnēm)",
"preferences_max_results_label": "Video skaits plūsmā: ",
"channel name": "kanāla nosaukuma",
"channel name - reverse": "kanāla nosaukuma apgrieztā secībā",
"preferences_unseen_only_label": "Rādīt tikai neskatītos video: ",
"Enable web notifications": "Iespējot paziņojumus pārlūkā",
"`x` uploaded a video": "`x` augšupielādēja video",
"Watch history": "Skatīto video vēsture",
"Delete account": "Dzēst kontu",
"Save preferences": "Saglabāt iestatījumus",
"Import/export": "Ievietot/Izgūt",
"Released under the AGPLv3 on Github.": "Izvietots GitHub saskaņā ar AGPLv3 licenci.",
"Source available here.": "Pirmkods pieejams šeit.",
"View JavaScript license information.": "Skatīt JavaScript licences informāciju.",
"Public": "Publisks",
"Private": "Privāts",
"View all playlists": "Skatīt visus atskaņošanas sarakstus",
"Delete playlist `x`?": "Vai tiešām dzēst `x` atskaņošanas sarakstu?",
"Delete playlist": "Dzēst atskaņošanas sarakstu",
"Create playlist": "Izveidot atskaņošanas sarakstu"
}

View File

@@ -39,8 +39,6 @@
"User ID": "Bruker-ID",
"Password": "Passord",
"Time (h:mm:ss):": "Tid (h:mm:ss):",
"Text CAPTCHA": "Tekst-CAPTCHA",
"Image CAPTCHA": "Bilde-CAPTCHA",
"Sign In": "Innlogging",
"Register": "Registrer",
"E-mail": "E-post",

View File

@@ -39,8 +39,6 @@
"User ID": "Gebruikers-id",
"Password": "Wachtwoord",
"Time (h:mm:ss):": "Tijd (h:mm:ss):",
"Text CAPTCHA": "Tekst-CAPTCHA",
"Image CAPTCHA": "Afbeelding-CAPTCHA",
"Sign In": "Inloggen",
"Register": "Registreren",
"E-mail": "E-mailadres",
@@ -501,5 +499,8 @@
"Filipino (auto-generated)": "Filipijns (automatisch gegenereerd)",
"channel_tab_courses_label": "Cursussen",
"First page": "Eerste pagina",
"channel_tab_posts_label": "Gepost"
"channel_tab_posts_label": "Gepost",
"timeline_parse_error_placeholder_heading": "Kan item niet parsen",
"timeline_parse_error_placeholder_message": "Invidious kwam een fout tegen bij het proberen te parsen van dit item. Voor meer informatie, kijk hieronder:",
"timeline_parse_error_show_technical_details": "Technische details weergeven"
}

View File

@@ -39,8 +39,6 @@
"User ID": "ID użytkownika",
"Password": "Hasło",
"Time (h:mm:ss):": "Godzina (h:mm:ss):",
"Text CAPTCHA": "Tekst CAPTCHA",
"Image CAPTCHA": "Obraz CAPTCHA",
"Sign In": "Zaloguj się",
"Register": "Zarejestruj się",
"E-mail": "E-mail",
@@ -78,7 +76,7 @@
"Redirect homepage to feed: ": "Przekieruj stronę główną do subskrybcji: ",
"preferences_max_results_label": "Liczba filmów widoczna na stronie subskrybcji: ",
"preferences_sort_label": "Sortuj filmy: ",
"published": "po czasie publikacji",
"published": "opublikowano",
"published - reverse": "po czasie publikacji od najstarszych",
"alphabetically": "alfabetycznie",
"alphabetically - reverse": "alfabetycznie od tyłu",
@@ -518,5 +516,10 @@
"Filipino (auto-generated)": "filipiński (wygenerowany automatycznie)",
"First page": "Pierwsza strona",
"channel_tab_posts_label": "Posty",
"channel_tab_courses_label": "Kursy"
"channel_tab_courses_label": "Kursy",
"timeline_parse_error_placeholder_message": "Invidious napotkał błąd podczas próby parsowania tego elementu. Aby uzyskać więcej informacji, zobacz poniżej:",
"timeline_parse_error_placeholder_heading": "Nie można przeanalizować elementu",
"timeline_parse_error_show_technical_details": "Pokaż szczegóły techniczne",
"preferences_default_playlist_none": "Brak domyślnej playlisty",
"preferences_default_playlist": "Domyślna playlista: "
}

View File

@@ -18,7 +18,7 @@
"Authorize token for `x`?": "Autorizar token para `x`?",
"Yes": "Sim",
"No": "Não",
"Import and Export Data": "Importar/exportar dados",
"Import and Export Data": "Importar e exportar dados",
"Import": "Importar",
"Import Invidious data": "Importar dados JSON do Invidious",
"Import YouTube subscriptions": "Importar inscrições no formato CSV ou OPML do YouTube",
@@ -39,8 +39,6 @@
"User ID": "Usuário",
"Password": "Senha",
"Time (h:mm:ss):": "Hora (h:mm:ss):",
"Text CAPTCHA": "Mudar para um desafio de texto",
"Image CAPTCHA": "Mudar para um desafio visual",
"Sign In": "Fazer login",
"Register": "Criar conta",
"E-mail": "E-mail",
@@ -484,7 +482,7 @@
"channel_tab_channels_label": "Canais",
"channel_tab_playlists_label": "Playlists",
"channel_tab_shorts_label": "Shorts",
"channel_tab_streams_label": "Transmissão ao vivo",
"channel_tab_streams_label": "Transmissões ao vivo",
"Music in this video": "Música neste vídeo",
"Artist: ": "Artista: ",
"Album: ": "Álbum: ",
@@ -518,5 +516,8 @@
"Filipino (auto-generated)": "Filipino (gerado automaticamente)",
"channel_tab_posts_label": "Postagens",
"First page": "Primeira página",
"channel_tab_courses_label": "Cursos"
"channel_tab_courses_label": "Cursos",
"timeline_parse_error_show_technical_details": "Mostrar detalhes técnicos",
"timeline_parse_error_placeholder_message": "O Invidious encontrou um problema ao processar este item. Para mais informações, veja abaixo:",
"timeline_parse_error_placeholder_heading": "Incapaz de processar item"
}

View File

@@ -39,8 +39,6 @@
"User ID": "Utilizador",
"Password": "Palavra-passe",
"Time (h:mm:ss):": "Tempo (h:mm:ss):",
"Text CAPTCHA": "Texto CAPTCHA",
"Image CAPTCHA": "Imagem CAPTCHA",
"Sign In": "Entrar",
"Register": "Registar",
"E-mail": "E-mail",
@@ -518,5 +516,8 @@
"Filipino (auto-generated)": "Filipino (gerado automaticamente)",
"channel_tab_courses_label": "Cursos",
"channel_tab_posts_label": "Publicações",
"toggle_theme": "Trocar tema"
"toggle_theme": "Trocar tema",
"timeline_parse_error_placeholder_heading": "Incapaz de processar o elemento",
"timeline_parse_error_placeholder_message": "O Invidious encontrou um problema ao processar este elemento. Para mais informações, veja abaixo:",
"timeline_parse_error_show_technical_details": "Mostrar detalhes técnicos"
}

View File

@@ -236,8 +236,6 @@
"Preferences": "Preferências",
"E-mail": "E-mail",
"Register": "Registar",
"Image CAPTCHA": "Imagem CAPTCHA",
"Text CAPTCHA": "Texto CAPTCHA",
"Time (h:mm:ss):": "Tempo (h:mm:ss):",
"Password": "Palavra-passe",
"User ID": "Utilizador",

View File

@@ -39,8 +39,6 @@
"User ID": "ID Utilizator",
"Password": "Parolă",
"Time (h:mm:ss):": "Ora (h:mm:ss) :",
"Text CAPTCHA": "Text CAPTCHA",
"Image CAPTCHA": "Imagine CAPTCHA",
"Sign In": "Conectați-vă",
"Register": "Înregistrați-vă",
"E-mail": "E-mail",

View File

@@ -22,7 +22,7 @@
"Import and Export Data": "Импорт и экспорт данных",
"Import": "Импорт",
"Import Invidious data": "Импортировать JSON с данными Invidious",
"Import YouTube subscriptions": "Импортировать подписки из CSV или OPML",
"Import YouTube subscriptions": "Импортировать подписки из YouTube через файлы CSV или OPML",
"Import FreeTube subscriptions (.db)": "Импортировать подписки из FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Импортировать подписки из NewPipe (.json)",
"Import NewPipe data (.zip)": "Импортировать данные из NewPipe (.zip)",
@@ -40,8 +40,6 @@
"User ID": "ИД пользователя",
"Password": "Пароль",
"Time (h:mm:ss):": "Время (ч:мм:сс):",
"Text CAPTCHA": "Текстовая капча (англ.)",
"Image CAPTCHA": "Капча-картинка",
"Sign In": "Войти",
"Register": "Регистрация",
"E-mail": "Эл. почта",
@@ -511,11 +509,17 @@
"Answer": "Ответить",
"Search for videos": "Поиск видео",
"The Popular feed has been disabled by the administrator.": "Лента популярного была отключена администратором.",
"toggle_theme": "Переключатель тем",
"carousel_slide": "Пролистано {{current}} из {{total}}",
"toggle_theme": "Переключить тему оформления",
"carousel_slide": "Слайд {{current}} из {{total}}",
"carousel_skip": "Пропустить всё",
"carousel_go_to": "Перейти к странице `x`",
"carousel_go_to": "Перейти на слайд `x`",
"preferences_preload_label": "Предзагрузка видеоданных: ",
"channel_tab_courses_label": "Курсы",
"channel_tab_posts_label": "Записи"
"channel_tab_posts_label": "Записи",
"timeline_parse_error_placeholder_message": "Invidious столкнулся с ошибкой, пытаясь разобрать с этот элемент. Подробнее смотрите ниже:",
"timeline_parse_error_placeholder_heading": "Невозможно разобрать элемент",
"timeline_parse_error_show_technical_details": "Показать технические подробности",
"Filipino (auto-generated)": "Филиппинский (автоматически сгенерировано)",
"preferences_default_playlist": "Плейлист по умолчанию: ",
"preferences_default_playlist_none": "Плейлист по умолчанию не указан"
}

View File

@@ -82,8 +82,6 @@
"Export subscriptions as OPML": "දායකත්වයන් OPML ලෙස අපනයනය කරන්න",
"JavaScript license information": "JavaScript බලපත්‍ර තොරතුරු",
"User ID": "පරිශීලක කේතය",
"Text CAPTCHA": "CAPTCHA පෙල",
"Image CAPTCHA": "CAPTCHA රූපය",
"E-mail": "විද්‍යුත් තැපෑල",
"preferences_quality_label": "කැමති වීඩියෝ ගුණත්වය: ",
"preferences_quality_option_hd720": "HD720",

View File

@@ -36,8 +36,6 @@
"User ID": "ID používateľa",
"Password": "Heslo",
"Time (h:mm:ss):": "Čas (h:mm:ss):",
"Text CAPTCHA": "Textové CAPTCHA",
"Image CAPTCHA": "Obrázkové CAPTCHA",
"Sign In": "Prihlásiť sa",
"Register": "Registrovať",
"E-mail": "E-mail",

View File

@@ -24,9 +24,7 @@
"User ID": "ID uporabnika",
"Password": "Geslo",
"Time (h:mm:ss):": "Čas (h:mm:ss):",
"Text CAPTCHA": "Besedilo CAPTCHA",
"source": "izvorna koda",
"Image CAPTCHA": "Slika CAPTCHA",
"Sign In": "Prijavi se",
"Register": "Registriraj se",
"E-mail": "E-pošta",
@@ -532,5 +530,11 @@
"carousel_slide": "Diapozitiv {{current}} od {{total}}",
"carousel_skip": "Preskoči galerijo",
"carousel_go_to": "Pojdi na diapozitiv `x`",
"preferences_preload_label": "Predhodno naloži video podatke: "
"preferences_preload_label": "Predhodno naloži video podatke: ",
"First page": "Prva stran",
"channel_tab_courses_label": "Tečaji",
"channel_tab_posts_label": "Objave",
"timeline_parse_error_placeholder_heading": "Elementa ni mogoče razčleniti",
"timeline_parse_error_placeholder_message": "Invidious je naletel na napako pri poskusu razčlenitve tega elementa. Za več informacij glej spodaj:",
"timeline_parse_error_show_technical_details": "Pokaži tehnične podrobnosti"
}

View File

@@ -42,8 +42,6 @@
"User ID": "ID Përdoruesi",
"Password": "Fjalëkalim",
"Time (h:mm:ss):": "Kohë (h:mm:ss):",
"Text CAPTCHA": "CAPTCHA Tekst",
"Image CAPTCHA": "CAPTCHA Figurë",
"Sign In": "Hyni",
"Register": "Regjistrohuni",
"E-mail": "Email",

View File

@@ -39,8 +39,6 @@
"User ID": "ID korisnika",
"Password": "Lozinka",
"Time (h:mm:ss):": "Vreme (č:mm:ss):",
"Text CAPTCHA": "Tekst CAPTCHA",
"Image CAPTCHA": "Slika CAPTCHA",
"Sign In": "Prijava",
"Register": "Registracija",
"E-mail": "Imejl",
@@ -518,5 +516,8 @@
"Filipino (auto-generated)": "Filipinski (automatski generisano)",
"channel_tab_posts_label": "Objave",
"First page": "Prva stranica",
"channel_tab_courses_label": "Kursevi"
"channel_tab_courses_label": "Kursevi",
"timeline_parse_error_placeholder_heading": "Nije moguće raščlaniti predmet",
"timeline_parse_error_show_technical_details": "Prikaži tehničke detalje",
"timeline_parse_error_placeholder_message": "Invidious je naišao na grešku prilikom pokušaja raščlanjivanja ovog predmeta. Za više informacija pogledajte ispod:"
}

View File

@@ -39,8 +39,6 @@
"User ID": "ID корисника",
"Password": "Лозинка",
"Time (h:mm:ss):": "Време (ч:мм:сс):",
"Text CAPTCHA": "Текст CAPTCHA",
"Image CAPTCHA": "Слика CAPTCHA",
"Sign In": "Пријава",
"Register": "Регистрација",
"E-mail": "Имејл",
@@ -518,5 +516,8 @@
"Filipino (auto-generated)": "Филипински (аутоматски генерисано)",
"channel_tab_courses_label": "Курсеви",
"First page": "Прва страница",
"channel_tab_posts_label": "Објаве"
"channel_tab_posts_label": "Објаве",
"timeline_parse_error_show_technical_details": "Прикажи техничке детаље",
"timeline_parse_error_placeholder_heading": "Није могуће рашчланити предмет",
"timeline_parse_error_placeholder_message": "Invidious је наишао на грешку приликом покушаја рашчлањивања овог предмета. За више информација погледајте испод:"
}

View File

@@ -39,8 +39,6 @@
"User ID": "Användar-ID",
"Password": "Lösenord",
"Time (h:mm:ss):": "Tid (h:mm:ss):",
"Text CAPTCHA": "Text-CAPTCHA",
"Image CAPTCHA": "Bild-CAPTCHA",
"Sign In": "Inloggning",
"Register": "Registrera",
"E-mail": "E-post",

View File

@@ -282,8 +282,6 @@
"Import": "இறக்குமதி",
"Import NewPipe subscriptions (.json)": "நியூபிப்பிப் சந்தாக்களை இறக்குமதி செய்யுங்கள் (.json)",
"Export": "ஏற்றுமதி",
"Text CAPTCHA": "உரை கேப்ட்சா",
"Image CAPTCHA": "பட கேப்ட்சா",
"preferences_category_player": "பிளேயர் விருப்பத்தேர்வுகள்",
"preferences_video_loop_label": "எப்போதும் லூப்: ",
"preferences_continue_autoplay_label": "தன்னியக்க அடுத்த வீடியோ: ",
@@ -498,5 +496,11 @@
"channel_tab_channels_label": "சேனல்கள்",
"toggle_theme": "கருப்பொருளை மாற்றவும்",
"carousel_slide": "{{total}} இன் ச்லைடு {{current}}",
"carousel_skip": "கொணர்வி தவிர்க்கவும்"
"carousel_skip": "கொணர்வி தவிர்க்கவும்",
"First page": "முதல் பக்கம்",
"channel_tab_courses_label": "படிப்புகள்",
"channel_tab_posts_label": "இடுகைகள்",
"timeline_parse_error_placeholder_heading": "உருப்படியை அலச முடியவில்லை",
"timeline_parse_error_placeholder_message": "இந்த உருப்படியை அலச முயற்சிக்கும் போது ஒரு பிழையை அடக்கமடைந்தது. மேலும் தகவலுக்கு கீழே காண்க:",
"timeline_parse_error_show_technical_details": "தொழில்நுட்ப விவரங்களைக் காட்டு"
}

View File

@@ -1,7 +1,26 @@
{
"Add to playlist": "Aýdym sanawyna goş",
"Add to playlist": "Pleýer Sanawa goş",
"Add to playlist: ": "Pleýliste goş: ",
"Answer": "Jogap",
"Search for videos": "Wideo gözläň",
"The Popular feed has been disabled by the administrator.": "Trende bolan administrator tarapyndan ýapyldy."
"The Popular feed has been disabled by the administrator.": "Trende bolan administrator tarapyndan ýapyldy.",
"generic_views_count": "{{count}} gezek görülen",
"generic_views_count_plural": "{{count}} görülen",
"generic_button_delete": "Öçür",
"generic_button_save": "Ýatda sakla",
"generic_button_cancel": "Goýbolsun",
"generic_button_rss": "RSS",
"LIVE": "Efif",
"generic_playlists_count": "{{count}} Oýnaw sanawy",
"generic_playlists_count_plural": "{{count}} Oýnaw sanawlary",
"generic_subscribers_count": "{{count}} abuna",
"generic_subscribers_count_plural": "{{count}} abunaçalar",
"generic_subscriptions_count": "{{count}} abuna",
"generic_subscriptions_count_plural": "{{count}} abunalar",
"generic_button_edit": "Üýtget",
"generic_videos_count": "{{count}} widýo",
"generic_videos_count_plural": "{{count}} widýolar",
"Shared `x` ago": "`x` öň paýlaşyldy",
"generic_channels_count": "{{count}} kanal",
"generic_channels_count_plural": "{{count}} kanallar"
}

View File

@@ -39,8 +39,6 @@
"User ID": "Kullanıcı Kimliği",
"Password": "Parola",
"Time (h:mm:ss):": "Zaman (h:mm:ss):",
"Text CAPTCHA": "Metin CAPTCHA",
"Image CAPTCHA": "Resim CAPTCHA",
"Sign In": "Oturum Aç",
"Register": "Kayıt Ol",
"E-mail": "E-Posta",
@@ -501,5 +499,8 @@
"First page": "İlk sayfa",
"Filipino (auto-generated)": "Filipince (oto-oluşturuldu)",
"channel_tab_courses_label": "Kurslar",
"channel_tab_posts_label": "Yazılar"
"channel_tab_posts_label": "Yazılar",
"timeline_parse_error_placeholder_heading": "Öge ayrıştıramıyor",
"timeline_parse_error_placeholder_message": "Invidious, bu ögeyi ayrıştırmaya çalışırken bir hatayla karşılaştı. Daha fazla bilgi için aşağıya bakın:",
"timeline_parse_error_show_technical_details": "Teknik ayrıntıları göster"
}

View File

@@ -39,8 +39,6 @@
"User ID": "ID користувача",
"Password": "Пароль",
"Time (h:mm:ss):": "Час (г:хх:сс):",
"Text CAPTCHA": "Текст CAPTCHA",
"Image CAPTCHA": "Зображення CAPTCHA",
"Sign In": "Увійти",
"Register": "Зареєструватися",
"E-mail": "Електронна пошта",
@@ -518,5 +516,8 @@
"Filipino (auto-generated)": "Філіппінська (згенеровано автоматично)",
"First page": "Перша сторінка",
"channel_tab_courses_label": "Курси",
"channel_tab_posts_label": "Дописи"
"channel_tab_posts_label": "Дописи",
"timeline_parse_error_placeholder_heading": "Неможливо розібрати елемент",
"timeline_parse_error_show_technical_details": "Показати технічні подробиці",
"timeline_parse_error_placeholder_message": "Invidious зіткнувся з помилкою під час спроби розібрати цей елемент. Докладнішу інформацію читайте нижче:"
}

View File

@@ -41,8 +41,6 @@
"User ID": "Mã nhận dạng người dùng",
"Password": "Mật khẩu",
"Time (h:mm:ss):": "Thời gian (h:mm:ss):",
"Text CAPTCHA": "CAPTCHA dạng chữ",
"Image CAPTCHA": "CAPTCHA dạng ảnh",
"Sign In": "Đăng nhập",
"Register": "Đăng ký",
"E-mail": "E-mail",

View File

@@ -44,8 +44,6 @@
"User ID": "用户 ID",
"Password": "密码",
"Time (h:mm:ss):": "时间 (h:mm:ss):",
"Text CAPTCHA": "文本验证码",
"Image CAPTCHA": "图片验证码",
"Sign In": "登录",
"Register": "注册",
"E-mail": "E-mail",
@@ -484,5 +482,10 @@
"Filipino (auto-generated)": "菲律宾语 (自动生成)",
"channel_tab_posts_label": "帖子",
"First page": "第一页",
"channel_tab_courses_label": "课程"
"channel_tab_courses_label": "课程",
"timeline_parse_error_show_technical_details": "显示技术细节",
"timeline_parse_error_placeholder_heading": "无法解析项目",
"timeline_parse_error_placeholder_message": "Invidious 在尝试解析此项目时遇到一个错误。更多信息请见下方:",
"preferences_default_playlist": "默认播放列表: ",
"preferences_default_playlist_none": "尚无默认播放列表"
}

View File

@@ -44,8 +44,6 @@
"User ID": "使用者 ID",
"Password": "密碼",
"Time (h:mm:ss):": "時間 (h:mm:ss):",
"Text CAPTCHA": "文字 CAPTCHA",
"Image CAPTCHA": "圖片 CAPTCHA",
"Sign In": "登入",
"Register": "註冊",
"E-mail": "電子郵件",
@@ -484,5 +482,10 @@
"Filipino (auto-generated)": "菲律賓語(自動產生)",
"channel_tab_courses_label": "課程",
"First page": "第一頁",
"channel_tab_posts_label": "貼文"
"channel_tab_posts_label": "貼文",
"timeline_parse_error_show_technical_details": "顯示技術細節",
"timeline_parse_error_placeholder_heading": "無法解析項目",
"timeline_parse_error_placeholder_message": "Invidious 在嘗試解析此項目時遇到錯誤。要取得更多資訊,請見下方:",
"preferences_default_playlist": "預設播放清單: ",
"preferences_default_playlist_none": "未設定預設播放清單"
}

View File

@@ -1,10 +1,14 @@
name: invidious
version: 2.20250517.0-dev
version: 2.20260207.0
authors:
- Invidious team <contact@invidious.io>
- Contributors!
targets:
invidious:
main: src/invidious.cr
description: |
Invidious is an alternative front-end to YouTube
@@ -38,7 +42,7 @@ development_dependencies:
crystal: ">= 1.10.0, < 2.0.0"
license: AGPLv3
license: AGPL-3.0-only
repository: https://github.com/iv-org/invidious
homepage: https://invidious.io

View File

@@ -0,0 +1 @@
Hello world

View File

@@ -0,0 +1,233 @@
# Due to the way that specs are handled this file cannot be run together with
# everything else without causing a compile time error that'll be incredibly
# annoying to resolve.
#
# TODO: Create different spec categories that can then be ran through make.
# An implementation of this can be seen with the tests for the Crystal compiler itself.
#
# For now run this with `crystal spec spec/http_server/handlers/static_assets_handler_spec.cr -Drunning_by_self`
{% skip_file if compare_versions(Crystal::VERSION, "1.17.0-dev") < 0 || !flag?(:running_by_self) %}
require "http"
require "spectator"
require "../../../src/invidious/http_server/static_assets_handler.cr"
private def get_static_assets_handler
return Invidious::HttpServer::StaticAssetsHandler.new "spec/http_server/handlers/static_assets_handler", directory_listing: false
end
# Slightly modified version of `handle` function from
#
# https://github.com/crystal-lang/crystal/blob/3f369d2c721e9462d9f6126cb0bcd4c6992f0225/spec/std/http/server/handlers/static_file_handler_spec.cr#L5
private def handle(request, handler : HTTP::Handler? = nil, decompress : Bool = false)
io = IO::Memory.new
response = HTTP::Server::Response.new(io)
context = HTTP::Server::Context.new(request, response)
if !handler
handler = get_static_assets_handler
get_static_assets_handler.call context
else
handler.call(context)
end
response.close
io.rewind
HTTP::Client::Response.from_io(io, decompress: decompress)
end
# Makes and yields a temporary file with the given prefix
private def make_temporary_file(prefix, contents = nil, &)
tempfile = File.tempfile(prefix, "static_assets_handler_spec", dir: "spec/http_server/handlers/static_assets_handler")
file_link = "/#{File.basename(tempfile.path)}"
yield tempfile, file_link
ensure
tempfile.try &.delete
end
# Changes the contents of the temporary file after yield
private def cycle_temporary_file_contents(temporary_file, initial, &)
temporary_file.rewind << initial
temporary_file.rewind.flush
yield
temporary_file.rewind << "something else"
temporary_file.rewind.flush
end
# Get relative file path to a file within the static_assets_handler folder
macro get_file_path(basename)
"spec/http_server/handlers/static_assets_handler/#{ {{basename}} }"
end
Spectator.describe StaticAssetsHandler do
it "Can serve a file" do
response = handle HTTP::Request.new("GET", "/test.txt")
expect(response.status_code).to eq(200)
expect(response.body).to eq(File.read(get_file_path("test.txt")))
end
it "Can serve cached file" do
make_temporary_file("cache_test") do |temporary_file, file_link|
cycle_temporary_file_contents(temporary_file, "foo") do
expect(temporary_file.rewind.gets_to_end).to eq("foo")
# Should get cached by the first run
response = handle HTTP::Request.new("GET", file_link)
expect(response.status_code).to eq(200)
expect(response.body).to eq("foo")
end
# Temporary file is updated after `cycle_temporary_file_contents` is called
# but if the file is successfully cached then we'll only get the original
# contents.
response = handle HTTP::Request.new("GET", file_link)
expect(response.status_code).to eq(200)
expect(response.body).to eq("foo")
end
end
it "Adds cache headers" do
response = handle HTTP::Request.new("GET", "/test.txt")
expect(response.headers["cache_control"]).to eq("max-age=2629800")
end
context "Can handle range requests" do
it "Can serve range request" do
headers = HTTP::Headers{"Range" => "bytes=0-2"}
response = handle HTTP::Request.new("GET", "/test.txt", headers)
expect(response.status_code).to eq(206)
expect(response.headers["Content-Range"]?).to eq "bytes 0-2/11"
expect(response.body).to eq "Hel"
end
it "Will cache entire file even if doing partial requests" do
make_temporary_file("range_cache") do |temporary_file, file_link|
cycle_temporary_file_contents(temporary_file, "Hello world") do
handle HTTP::Request.new("GET", file_link, HTTP::Headers{"Range" => "bytes=0-2"})
end
# Second request shouldn't have changed
headers = HTTP::Headers{"Range" => "bytes=3-8"}
response = handle HTTP::Request.new("GET", file_link, headers)
expect(response.status_code).to eq(206)
expect(response.body).to eq "lo wor"
end
end
end
context "Is able to support compression" do
def decompressed(string : String)
decompressed = Compress::Gzip::Reader.open(IO::Memory.new(string)) do |gzip|
gzip.gets_to_end
end
return expect(decompressed)
end
it "For full file requests" do
handler = HTTP::CompressHandler.new
handler.next = get_static_assets_handler()
make_temporary_file("check decompression handler") do |temporary_file, file_link|
cycle_temporary_file_contents(temporary_file, "Hello world") do
response = handle HTTP::Request.new("GET", file_link, headers: HTTP::Headers{"Accept-Encoding" => "gzip"}), handler: handler
expect(response.headers["Content-Encoding"]).to eq("gzip")
decompressed(response.body).to eq("Hello world")
end
# Are cached requests working?
response = handle HTTP::Request.new("GET", file_link, headers: HTTP::Headers{"Accept-Encoding" => "gzip"}), handler: handler
expect(response.headers["Content-Encoding"]).to eq("gzip")
decompressed(response.body).to eq("Hello world")
# Able to retrieve non gzipped file?
response = handle HTTP::Request.new("GET", file_link), handler: handler
expect(response.body).to eq("Hello world")
expect(response.headers).to_not have_key("Content-Encoding")
end
end
# Inspired by the equivalent tests from upstream
it "For partial file requests" do
handler = HTTP::CompressHandler.new
handler.next = get_static_assets_handler()
make_temporary_file("check_decompression_handler_on_partial_requests") do |temporary_file, file_link|
cycle_temporary_file_contents(temporary_file, "Hello world this is a very long string") do
range_response_results = {
"10-20/38" => "d this is a",
"0-0/38" => "H",
"5-9/38" => " worl",
}
range_request_header_value = {"10-20", "5-9", "0-0"}.join(',')
range_response_header_value = range_response_results.keys
response = handle HTTP::Request.new("GET", file_link, headers: HTTP::Headers{"Range" => "bytes=#{range_request_header_value}", "Accept-Encoding" => "gzip"}), handler: handler
expect(response.headers["Content-Encoding"]).to eq("gzip")
# Decompress response
response = HTTP::Client::Response.new(
status: response.status,
headers: response.headers,
body_io: Compress::Gzip::Reader.new(IO::Memory.new(response.body)),
)
count = 0
MIME::Multipart.parse(response) do |headers, part|
part_range = headers["Content-Range"][6..]
expect(part_range).to be_within(range_response_header_value)
expect(part.gets_to_end).to eq(range_response_results[part_range])
count += 1
end
expect(count).to eq(3)
end
# Is the file cached?
temporary_file << "Something else"
temporary_file.flush.rewind
response = handle HTTP::Request.new("GET", file_link, headers: HTTP::Headers{"Accept-Encoding" => "gzip"}), handler: handler
decompressed(response.body).to eq("Hello world this is a very long string")
end
end
end
it "Will not cache additional files if the cache limit is reached" do
5.times do |times|
data = "a" * 1_000_000
make_temporary_file("test cache size limit #{times}") do |temporary_file, file_link|
cycle_temporary_file_contents(temporary_file, data) do
response = handle HTTP::Request.new("GET", file_link)
expect(response.status_code).to eq(200)
expect(response.body).to eq(data)
end
response = handle HTTP::Request.new("GET", file_link)
expect(response.status_code).to eq(200)
expect(response.body).to eq(data)
end
end
# Cache should be 5 mb so no more files will be cached.
make_temporary_file("test cache size limit uncached") do |temporary_file, file_link|
cycle_temporary_file_contents(temporary_file, "a") do
response = handle HTTP::Request.new("GET", file_link)
expect(response.status_code).to eq(200)
expect(response.body).to eq("a")
end
response = handle HTTP::Request.new("GET", file_link)
expect(response.status_code).to eq(200)
expect(response.body).to_not eq("a")
end
end
after_each { Invidious::HttpServer::StaticAssetsHandler.clear_cache }
end

View File

@@ -52,7 +52,6 @@ Spectator.describe "parse_video_info" do
expect(info["relatedVideos"][0]["title"]).to eq("$1 vs $250,000,000 Private Island!")
expect(info["relatedVideos"][0]["author"]).to eq("MrBeast")
expect(info["relatedVideos"][0]["ucid"]).to eq("UCX6OQ3DkcsbYNE6H8uQQuVA")
expect(info["relatedVideos"][0]["view_count"]).to eq("230617484")
expect(info["relatedVideos"][0]["short_view_count"]).to eq("230M")
expect(info["relatedVideos"][0]["author_verified"]).to eq("true")
@@ -138,7 +137,6 @@ Spectator.describe "parse_video_info" do
expect(info["relatedVideos"][0]["title"]).to eq("Chris Rea - The Road To Hell 1989 Full Version")
expect(info["relatedVideos"][0]["author"]).to eq("NEA ZIXNH")
expect(info["relatedVideos"][0]["ucid"]).to eq("UCYMEOGcvav3gCgImK2J07CQ")
expect(info["relatedVideos"][0]["view_count"]).to eq("53298661")
expect(info["relatedVideos"][0]["short_view_count"]).to eq("53M")
expect(info["relatedVideos"][0]["author_verified"]).to eq("false")

View File

@@ -75,7 +75,6 @@ Spectator.describe "parse_video_info" do
expect(info["relatedVideos"][0]["id"]).to eq("j7jPzzjbVuk")
expect(info["relatedVideos"][0]["author"]).to eq("Democracy Now!")
expect(info["relatedVideos"][0]["ucid"]).to eq("UCzuqE7-t13O4NIDYJfakrhw")
expect(info["relatedVideos"][0]["view_count"]).to eq("7576")
expect(info["relatedVideos"][0]["short_view_count"]).to eq("7.5K")
expect(info["relatedVideos"][0]["author_verified"]).to eq("true")

View File

@@ -1,3 +1,24 @@
{% if compare_versions(Crystal::VERSION, "1.17.0-dev") >= 0 %}
# Strip StaticFileHandler from the binary
#
# This allows us to compile on 1.17.0 as the compiler won't try to
# semantically check the outdated upstream code.
class Kemal::Config
private def setup_static_file_handler
end
end
# Nullify `Kemal::StaticFileHandler`
#
# Needed until the next release of Kemal after 1.7
class Kemal::StaticFileHandler < HTTP::StaticFileHandler
def call(context : HTTP::Server::Context)
end
end
{% skip_file %}
{% end %}
# Since systems have a limit on number of open files (`ulimit -a`),
# we serve them from memory to avoid 'Too many open files' without needing
# to modify ulimit.

View File

@@ -60,24 +60,20 @@ alias IV = Invidious
CONFIG = Config.load
HMAC_KEY = CONFIG.hmac_key
PG_DB = DB.open CONFIG.database_url
ARCHIVE_URL = URI.parse("https://archive.org")
PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com")
REDDIT_URL = URI.parse("https://www.reddit.com")
YT_URL = URI.parse("https://www.youtube.com")
HOST_URL = make_host_url(Kemal.config)
CHARS_SAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
TEST_IDS = {"AgbeGFYluEA", "BaW_jenozKc", "a9LDPn-MO4I", "ddFvjfvPnqk", "iqKdEhx-dD4"}
PG_DB = begin
DB.open CONFIG.database_url
rescue ex
puts "Failed to connect to PostgreSQL database: #{ex.cause.try &.message}"
puts "Check your 'config.yml' database settings or PostgreSQL settings."
exit(1)
end
HOST_URL = make_host_url(Kemal.config)
MAX_ITEMS_PER_PAGE = 1500
REQUEST_HEADERS_WHITELIST = {"accept", "accept-encoding", "cache-control", "content-length", "if-none-match", "range"}
RESPONSE_HEADERS_BLACKLIST = {"access-control-allow-origin", "alt-svc", "server"}
HTTP_CHUNK_SIZE = 10485760 # ~10MB
CURRENT_BRANCH = {{ "#{`git branch | sed -n '/* /s///p'`.strip}" }}
CURRENT_COMMIT = {{ "#{`git rev-list HEAD --max-count=1 --abbrev-commit`.strip}" }}
CURRENT_VERSION = {{ "#{`git log -1 --format=%ci | awk '{print $1}' | sed s/-/./g`.strip}" }}
CURRENT_TAG = {{ "#{`git tag --points-at HEAD`.strip}" }}
# This is used to determine the `?v=` on the end of file URLs (for cache busting). We
# only need to expire modified assets, so we can use this to find the last commit that changes
@@ -90,7 +86,7 @@ SOFTWARE = {
"branch" => "#{CURRENT_BRANCH}",
}
YT_POOL = YoutubeConnectionPool.new(YT_URL, capacity: CONFIG.pool_size)
YT_POOL = YoutubeConnectionPool.new(URI.parse("https://www.youtube.com"), capacity: CONFIG.pool_size)
# Image request pool
@@ -164,15 +160,6 @@ Invidious::Database.check_integrity(CONFIG)
{% puts "\nDone checking player dependencies, now compiling Invidious...\n" %}
{% end %}
# Misc
DECRYPT_FUNCTION =
if sig_helper_address = CONFIG.signature_server.presence
IV::DecryptFunction.new(sig_helper_address)
else
nil
end
# Start jobs
if CONFIG.channel_threads > 0
@@ -221,23 +208,29 @@ error 404 do |env|
Invidious::Routes::ErrorRoutes.error_404(env)
end
error 500 do |env, ex|
error_template(500, ex)
end
static_headers do |env|
env.response.headers.add("Cache-Control", "max-age=2629800")
error 500 do |env, exception|
error_template(500, exception)
end
# Init Kemal
public_folder "assets"
Kemal.config.powered_by_header = false
add_handler FilteredCompressHandler.new
add_handler APIHandler.new
add_handler AuthHandler.new
add_handler DenyFrame.new
{% if compare_versions(Crystal::VERSION, "1.17.0-dev") >= 0 %}
Kemal.config.serve_static = false
add_handler Invidious::HttpServer::StaticAssetsHandler.new("assets", directory_listing: false)
{% else %}
public_folder "assets"
static_headers do |env|
env.response.headers.add("Cache-Control", "max-age=2629800")
end
{% end %}
add_context_storage_type(Array(String))
add_context_storage_type(Preferences)
add_context_storage_type(Invidious::User)
@@ -252,6 +245,8 @@ Kemal.config.app_name = "Invidious"
{% end %}
Kemal.run do |config|
config.server.not_nil!.max_request_line_size = 16384
if socket_binding = CONFIG.socket_binding
File.delete?(socket_binding.path)
# Create a socket and set its desired permissions

View File

@@ -12,6 +12,7 @@ record AboutChannel,
sub_count : Int32,
joined : Time,
is_family_friendly : Bool,
pronouns : String?,
allowed_regions : Array(String),
tabs : Array(String),
tags : Array(String),
@@ -160,14 +161,21 @@ def get_about_info(ucid, locale) : AboutChannel
end
sub_count = 0
pronouns = nil
if (metadata_rows = initdata.dig?("header", "pageHeaderRenderer", "content", "pageHeaderViewModel", "metadata", "contentMetadataViewModel", "metadataRows").try &.as_a)
metadata_rows.each do |row|
metadata_part = row.dig?("metadataParts").try &.as_a.find { |i| i.dig?("text", "content").try &.as_s.includes?("subscribers") }
if !metadata_part.nil?
sub_count = short_text_to_number(metadata_part.dig("text", "content").as_s.split(" ")[0]).to_i32
subscribe_metadata_part = row.dig?("metadataParts").try &.as_a.find { |i| i.dig?("text", "content").try &.as_s.includes?("subscribers") }
if !subscribe_metadata_part.nil?
sub_count = short_text_to_number(subscribe_metadata_part.dig("text", "content").as_s.split(" ")[0]).to_i32
end
break if sub_count != 0
pronoun_metadata_part = row.dig?("metadataParts").try &.as_a.find { |i| i.dig?("tooltip").try &.as_s.includes?("Pronouns") }
if !pronoun_metadata_part.nil?
pronouns = pronoun_metadata_part.dig("text", "content").as_s
end
break if sub_count != 0 && !pronouns.nil?
end
end
@@ -184,6 +192,7 @@ def get_about_info(ucid, locale) : AboutChannel
sub_count: sub_count,
joined: joined,
is_family_friendly: is_family_friendly,
pronouns: pronouns,
allowed_regions: allowed_regions,
tabs: tab_names,
tags: tags,

View File

@@ -3,8 +3,8 @@ private IMAGE_QUALITIES = {320, 560, 640, 1280, 2000}
# TODO: Add "sort_by"
def fetch_channel_community(ucid, cursor, locale, format, thin_mode)
if cursor.nil?
# Egljb21tdW5pdHk%3D is the protobuf object to load "community"
initial_data = YoutubeAPI.browse(ucid, params: "Egljb21tdW5pdHk%3D")
# EgVwb3N0c_IGBAoCSgA%3D is the protobuf object to load "posts"
initial_data = YoutubeAPI.browse(ucid, params: "EgVwb3N0c_IGBAoCSgA%3D")
items = [] of JSON::Any
extract_items(initial_data) do |item|
@@ -24,15 +24,21 @@ def fetch_channel_community(ucid, cursor, locale, format, thin_mode)
return extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode)
end
def decode_ucid_from_post_protobuf(params)
decoded_protobuf = params.try { |i| URI.decode_www_form(i) }
.try { |i| Base64.decode(i) }
.try { |i| IO::Memory.new(i) }
.try { |i| Protodec::Any.parse(i) }
return decoded_protobuf.try(&.["56:0:embedded"]["2:0:string"].as_s)
end
def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode)
object = {
"2:string" => "community",
"25:embedded" => {
"22:string" => post_id.to_s,
},
"45:embedded" => {
"2:varint" => 1_i64,
"3:varint" => 1_i64,
"56:embedded" => {
"2:string" => ucid,
"3:string" => post_id.to_s,
"11:string" => ucid,
},
}
params = object.try { |i| Protodec::Any.cast_json(i) }
@@ -40,7 +46,7 @@ def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode)
.try { |i| Base64.urlsafe_encode(i) }
.try { |i| URI.encode_www_form(i) }
initial_data = YoutubeAPI.browse(ucid, params: params)
initial_data = YoutubeAPI.browse("FEpost_detail", params: params)
items = [] of JSON::Any
extract_items(initial_data) do |item|
@@ -137,7 +143,7 @@ def extract_channel_community(items, *, ucid, locale, format, thin_mode, is_sing
case attachment.as_h
when .has_key?("videoRenderer")
parse_item(attachment)
.as(SearchVideo)
.as(SearchVideo | ProblematicTimelineItem)
.to_json(locale, json)
when .has_key?("backstageImageRenderer")
json.object do

View File

@@ -6,19 +6,19 @@ def fetch_channel_playlists(ucid, author, continuation, sort_by)
case sort_by
when "last", "last_added"
# Equivalent to "&sort=lad"
# {"2:string": "playlists", "3:varint": 4, "4:varint": 1, "6:varint": 1}
"EglwbGF5bGlzdHMYBCABMAE%3D"
# {"2:string": "playlists", "3:varint": 4, "4:varint": 1, "6:varint": 1, "110:embedded": {"1:embedded": {"8:string": ""}}}
"EglwbGF5bGlzdHMYBCABMAHyBgQKAkIA"
when "oldest", "oldest_created"
# formerly "&sort=da"
# Not available anymore :c or maybe ??
# {"2:string": "playlists", "3:varint": 2, "4:varint": 1, "6:varint": 1}
"EglwbGF5bGlzdHMYAiABMAE%3D"
# {"2:string": "playlists", "3:varint": 2, "4:varint": 1, "6:varint": 1, "110:embedded": {"1:embedded": {"8:string": ""}}}
"EglwbGF5bGlzdHMYAiABMAHyBgQKAkIA"
# {"2:string": "playlists", "3:varint": 1, "4:varint": 1, "6:varint": 1}
# "EglwbGF5bGlzdHMYASABMAE%3D"
when "newest", "newest_created"
# Formerly "&sort=dd"
# {"2:string": "playlists", "3:varint": 3, "4:varint": 1, "6:varint": 1}
"EglwbGF5bGlzdHMYAyABMAE%3D"
# {"2:string": "playlists", "3:varint": 3, "4:varint": 1, "6:varint": 1, "110:embedded": {"1:embedded": {"8:string": ""}}}
"EglwbGF5bGlzdHMYAyABMAHyBgQKAkIA"
end
initial_data = YoutubeAPI.browse(ucid, params: params || "")

View File

@@ -114,7 +114,11 @@ module Invidious::Channel::Tabs
"2:embedded" => {
"1:string" => "00000000-0000-0000-0000-000000000000",
},
"4:varint" => sort_options_videos_short(sort_by),
"4:varint" => sort_options_videos_short(sort_by),
"8:embedded" => {
"1:string" => "00000000-0000-0000-0000-000000000000",
"3:varint" => sort_options_videos_short(sort_by),
},
},
}
@@ -130,7 +134,11 @@ module Invidious::Channel::Tabs
"2:embedded" => {
"1:string" => "00000000-0000-0000-0000-000000000000",
},
"4:varint" => sort_options_videos_short(sort_by),
"4:varint" => sort_options_videos_short(sort_by),
"7:embedded" => {
"1:string" => "00000000-0000-0000-0000-000000000000",
"3:varint" => sort_options_videos_short(sort_by),
},
},
}
@@ -154,7 +162,11 @@ module Invidious::Channel::Tabs
"2:embedded" => {
"1:string" => "00000000-0000-0000-0000-000000000000",
},
"5:varint" => sort_by_numerical,
"5:varint" => sort_by_numerical,
"8:embedded" => {
"1:string" => "00000000-0000-0000-0000-000000000000",
"3:varint" => sort_by_numerical,
},
},
}

View File

@@ -1,5 +1,6 @@
module Invidious::Comments
extend self
private REDDIT_URL = URI.parse("https://www.reddit.com")
def fetch_reddit(id, sort_by = "confidence")
client = make_client(REDDIT_URL)

View File

@@ -16,23 +16,27 @@ module Invidious::Comments
return parse_youtube(id, response, format, locale, thin_mode, sort_by)
end
def fetch_community_post_comments(ucid, post_id)
def fetch_community_post_comments(ucid, post_id, sort_by = "top")
case sort_by
when "top"
sort_by_val = 0_i64
when "new", "newest"
sort_by_val = 1_i64
else # top
sort_by_val = 0_i64
end
object = {
"2:string" => "community",
"25:embedded" => {
"22:string" => post_id,
},
"45:embedded" => {
"2:varint" => 1_i64,
"3:varint" => 1_i64,
},
"2:string" => "posts",
"53:embedded" => {
"4:embedded" => {
"6:varint" => 0_i64,
"27:varint" => 1_i64,
"6:varint" => sort_by_val,
"15:varint" => 2_i64,
"25:varint" => 0_i64,
"29:string" => post_id,
"30:string" => ucid,
},
"7:varint" => 0_i64,
"8:string" => "comments-section",
},
}
@@ -43,7 +47,7 @@ module Invidious::Comments
object2 = {
"80226972:embedded" => {
"2:string" => ucid,
"2:string" => "FEcomment_post_detail_page_web_top_level",
"3:string" => object_parsed,
},
}
@@ -320,6 +324,15 @@ module Invidious::Comments
end
def produce_continuation(video_id, cursor = "", sort_by = "top")
case sort_by
when "top"
sort_by_val = 0_i64
when "new", "newest"
sort_by_val = 1_i64
else # top
sort_by_val = 0_i64
end
object = {
"2:embedded" => {
"2:string" => video_id,
@@ -340,21 +353,12 @@ module Invidious::Comments
"1:string" => cursor,
"4:embedded" => {
"4:string" => video_id,
"6:varint" => 0_i64,
"6:varint" => sort_by_val,
},
"5:varint" => 20_i64,
},
}
case sort_by
when "top"
object["6:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 0_i64
when "new", "newest"
object["6:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 1_i64
else # top
object["6:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 0_i64
end
continuation = object.try { |i| Protodec::Any.cast_json(i) }
.try { |i| Protodec::Any.from_json(i) }
.try { |i| Base64.urlsafe_encode(i) }

View File

@@ -52,6 +52,8 @@ struct ConfigPreferences
property vr_mode : Bool = true
property show_nick : Bool = true
property save_player_pos : Bool = false
@[YAML::Field(ignore: true)]
property default_playlist : String? = nil
def to_tuple
{% begin %}
@@ -82,6 +84,9 @@ class Config
@[YAML::Field(converter: Preferences::URIConverter)]
property public_url : URI = URI.parse("")
# Indicates if this companion instance uses the built-in proxy
property builtin_proxy : Bool = false
end
# Number of threads to use for crawling videos from channels (for updating subscriptions)
@@ -148,9 +153,6 @@ class Config
@[YAML::Field(converter: Preferences::FamilyConverter)]
property force_resolve : Socket::Family = Socket::Family::UNSPEC
# External signature solver server socket (either a path to a UNIX domain socket or "<IP>:<Port>")
property signature_server : String? = nil
# Port to listen for connections (overridden by command line argument)
property port : Int32 = 3000
# Host to bind (overridden by command line argument)
@@ -165,11 +167,6 @@ class Config
# Use Innertube's transcripts API instead of timedtext for closed captions
property use_innertube_for_captions : Bool = false
# visitor data ID for Google session
property visitor_data : String? = nil
# poToken for passing bot attestation
property po_token : String? = nil
# Invidious companion
property invidious_companion : Array(CompanionConfig) = [] of CompanionConfig
@@ -257,11 +254,7 @@ class Config
{% end %}
if config.invidious_companion.present?
# invidious_companion and signature_server can't work together
if config.signature_server
puts "Config: You can not run inv_sig_helper and invidious_companion at the same time."
exit(1)
elsif config.invidious_companion_key.empty?
if config.invidious_companion_key.empty?
puts "Config: Please configure a key if you are using invidious companion."
exit(1)
elsif config.invidious_companion_key == "CHANGE_ME!!"
@@ -271,10 +264,16 @@ class Config
puts "Config: The value of 'invidious_companion_key' needs to be a size of 16 characters."
exit(1)
end
elsif config.signature_server
puts("WARNING: inv-sig-helper is deprecated. Please switch to Invidious companion: https://docs.invidious.io/companion-installation/")
# Set public_url to built-in proxy path when omitted
config.invidious_companion.each do |companion|
if companion.public_url.to_s.empty?
companion.public_url = URI.parse("/companion")
companion.builtin_proxy = true
end
end
else
puts("WARNING: Invidious companion is required to view and playback videos. For more information see https://docs.invidious.io/companion-installation/")
puts("WARNING: Invidious companion is required to view and playback videos. For more information see https://docs.invidious.io/installation/")
end
# HMAC_key is mandatory

View File

@@ -2,9 +2,9 @@ module Invidious::Frontend::Misc
extend self
def redirect_url(env : HTTP::Server::Context)
prefs = env.get("preferences").as(Preferences)
preferences = env.get("preferences").as(Preferences)
if prefs.automatic_instance_redirect
if preferences.automatic_instance_redirect
current_page = env.get?("current_page").as(String)
return "/redirect?referer=#{current_page}"
else

View File

@@ -23,6 +23,10 @@ module Invidious::Frontend::WatchPage
return "<p id=\"download\">#{translate(locale, "Download is disabled")}</p>"
end
if CONFIG.dmca_content.includes?(video.id)
return "<p id=\"download\">#{translate(locale, "dmca_content")}</p>"
end
url = "/download"
if (CONFIG.invidious_companion.present?)
invidious_companion = CONFIG.invidious_companion.sample
@@ -32,9 +36,9 @@ module Invidious::Frontend::WatchPage
return String.build(4000) do |str|
str << "<form"
str << " class=\"pure-form pure-form-stacked\""
str << " action='#{url}'"
str << " action='" << HTML.escape(url) << "'"
str << " method='post'"
str << " rel='noopener'"
str << " rel='noopener noreferrer'"
str << " target='_blank'>"
str << '\n'

View File

@@ -3,15 +3,28 @@
# IPv6 addresses.
#
class TCPSocket
def initialize(host, port, dns_timeout = nil, connect_timeout = nil, blocking = false, family = Socket::Family::UNSPEC)
Addrinfo.tcp(host, port, timeout: dns_timeout, family: family) do |addrinfo|
super(addrinfo.family, addrinfo.type, addrinfo.protocol, blocking)
connect(addrinfo, timeout: connect_timeout) do |error|
close
error
{% if compare_versions(Crystal::VERSION, "1.18.0-dev") >= 0 %}
def initialize(host : String, port, dns_timeout = nil, connect_timeout = nil, blocking = false, family = Socket::Family::UNSPEC)
Addrinfo.tcp(host, port, timeout: dns_timeout, family: family) do |addrinfo|
super(family: addrinfo.family, type: addrinfo.type, protocol: addrinfo.protocol)
Socket.set_blocking(self.fd, blocking)
connect(addrinfo, timeout: connect_timeout) do |error|
close
error
end
end
end
end
{% else %}
def initialize(host : String, port, dns_timeout = nil, connect_timeout = nil, blocking = false, family = Socket::Family::UNSPEC)
Addrinfo.tcp(host, port, timeout: dns_timeout, family: family) do |addrinfo|
super(addrinfo.family, addrinfo.type, addrinfo.protocol, blocking)
connect(addrinfo, timeout: connect_timeout) do |error|
close
error
end
end
end
{% end %}
end
# :ditto:

View File

@@ -61,28 +61,13 @@ class Kemal::ExceptionHandler
end
end
class FilteredCompressHandler < Kemal::Handler
class FilteredCompressHandler < HTTP::CompressHandler
exclude ["/videoplayback", "/videoplayback/*", "/vi/*", "/sb/*", "/ggpht/*", "/api/v1/auth/notifications"]
exclude ["/api/v1/auth/notifications", "/data_control"], "POST"
def call(env)
return call_next env if exclude_match? env
{% if flag?(:without_zlib) %}
call_next env
{% else %}
request_headers = env.request.headers
if request_headers.includes_word?("Accept-Encoding", "gzip")
env.response.headers["Content-Encoding"] = "gzip"
env.response.output = Compress::Gzip::Writer.new(env.response.output, sync_close: true)
elsif request_headers.includes_word?("Accept-Encoding", "deflate")
env.response.headers["Content-Encoding"] = "deflate"
env.response.output = Compress::Deflate::Writer.new(env.response.output, sync_close: true)
end
call_next env
{% end %}
def call(context)
return call_next context if exclude_match? context
super
end
end

View File

@@ -1,5 +1,7 @@
require "./macros"
TEST_IDS = {"AgbeGFYluEA", "BaW_jenozKc", "a9LDPn-MO4I", "ddFvjfvPnqk", "iqKdEhx-dD4"}
struct Nonce
include DB::Serializable

View File

@@ -1,349 +0,0 @@
require "uri"
require "socket"
require "socket/tcp_socket"
require "socket/unix_socket"
{% if flag?(:advanced_debug) %}
require "io/hexdump"
{% end %}
private alias NetworkEndian = IO::ByteFormat::NetworkEndian
module Invidious::SigHelper
enum UpdateStatus
Updated
UpdateNotRequired
Error
end
# -------------------
# Payload types
# -------------------
abstract struct Payload
end
struct StringPayload < Payload
getter string : String
def initialize(str : String)
raise Exception.new("SigHelper: String can't be empty") if str.empty?
@string = str
end
def self.from_bytes(slice : Bytes)
size = IO::ByteFormat::NetworkEndian.decode(UInt16, slice)
if size == 0 # Error code
raise Exception.new("SigHelper: Server encountered an error")
end
if (slice.bytesize - 2) != size
raise Exception.new("SigHelper: String size mismatch")
end
if str = String.new(slice[2..])
return self.new(str)
else
raise Exception.new("SigHelper: Can't read string from socket")
end
end
def to_io(io)
# `.to_u16` raises if there is an overflow during the conversion
io.write_bytes(@string.bytesize.to_u16, NetworkEndian)
io.write(@string.to_slice)
end
end
private enum Opcode
FORCE_UPDATE = 0
DECRYPT_N_SIGNATURE = 1
DECRYPT_SIGNATURE = 2
GET_SIGNATURE_TIMESTAMP = 3
GET_PLAYER_STATUS = 4
PLAYER_UPDATE_TIMESTAMP = 5
end
private record Request,
opcode : Opcode,
payload : Payload?
# ----------------------
# High-level functions
# ----------------------
class Client
@mux : Multiplexor
def initialize(uri_or_path)
@mux = Multiplexor.new(uri_or_path)
end
# Forces the server to re-fetch the YouTube player, and extract the necessary
# components from it (nsig function code, sig function code, signature timestamp).
def force_update : UpdateStatus
request = Request.new(Opcode::FORCE_UPDATE, nil)
value = send_request(request) do |bytes|
IO::ByteFormat::NetworkEndian.decode(UInt16, bytes)
end
case value
when 0x0000 then return UpdateStatus::Error
when 0xFFFF then return UpdateStatus::UpdateNotRequired
when 0xF44F then return UpdateStatus::Updated
else
code = value.nil? ? "nil" : value.to_s(base: 16)
raise Exception.new("SigHelper: Invalid status code received #{code}")
end
end
# Decrypt a provided n signature using the server's current nsig function
# code, and return the result (or an error).
def decrypt_n_param(n : String) : String?
request = Request.new(Opcode::DECRYPT_N_SIGNATURE, StringPayload.new(n))
n_dec = self.send_request(request) do |bytes|
StringPayload.from_bytes(bytes).string
end
return n_dec
end
# Decrypt a provided s signature using the server's current sig function
# code, and return the result (or an error).
def decrypt_sig(sig : String) : String?
request = Request.new(Opcode::DECRYPT_SIGNATURE, StringPayload.new(sig))
sig_dec = self.send_request(request) do |bytes|
StringPayload.from_bytes(bytes).string
end
return sig_dec
end
# Return the signature timestamp from the server's current player
def get_signature_timestamp : UInt64?
request = Request.new(Opcode::GET_SIGNATURE_TIMESTAMP, nil)
return self.send_request(request) do |bytes|
IO::ByteFormat::NetworkEndian.decode(UInt64, bytes)
end
end
# Return the current player's version
def get_player : UInt32?
request = Request.new(Opcode::GET_PLAYER_STATUS, nil)
return self.send_request(request) do |bytes|
has_player = (bytes[0] == 0xFF)
player_version = IO::ByteFormat::NetworkEndian.decode(UInt32, bytes[1..4])
has_player ? player_version : nil
end
end
# Return when the player was last updated
def get_player_timestamp : UInt64?
request = Request.new(Opcode::PLAYER_UPDATE_TIMESTAMP, nil)
return self.send_request(request) do |bytes|
IO::ByteFormat::NetworkEndian.decode(UInt64, bytes)
end
end
private def send_request(request : Request, &)
channel = @mux.send(request)
slice = channel.receive
return yield slice
rescue ex
LOGGER.debug("SigHelper: Error when sending a request")
LOGGER.trace(ex.inspect_with_backtrace)
return nil
end
end
# ---------------------
# Low level functions
# ---------------------
class Multiplexor
alias TransactionID = UInt32
record Transaction, channel = ::Channel(Bytes).new
@prng = Random.new
@mutex = Mutex.new
@queue = {} of TransactionID => Transaction
@conn : Connection
@uri_or_path : String
def initialize(@uri_or_path)
@conn = Connection.new(uri_or_path)
listen
end
def listen : Nil
raise "Socket is closed" if @conn.closed?
LOGGER.debug("SigHelper: Multiplexor listening")
spawn do
loop do
begin
receive_data
rescue ex
LOGGER.info("SigHelper: Connection to helper died with '#{ex.message}' trying to reconnect...")
# We close the socket because for some reason is not closed.
@conn.close
loop do
begin
@conn = Connection.new(@uri_or_path)
LOGGER.info("SigHelper: Reconnected to SigHelper!")
rescue ex
LOGGER.debug("SigHelper: Reconnection to helper unsuccessful with error '#{ex.message}'. Retrying")
sleep 500.milliseconds
next
end
break if !@conn.closed?
end
end
Fiber.yield
end
end
end
def send(request : Request)
transaction = Transaction.new
transaction_id = @prng.rand(TransactionID)
# Add transaction to queue
@mutex.synchronize do
# On a 32-bits random integer, this should never happen. Though, just in case, ...
if @queue[transaction_id]?
raise Exception.new("SigHelper: Duplicate transaction ID! You got a shiny pokemon!")
end
@queue[transaction_id] = transaction
end
write_packet(transaction_id, request)
return transaction.channel
end
def receive_data
transaction_id, slice = read_packet
@mutex.synchronize do
if transaction = @queue.delete(transaction_id)
# Remove transaction from queue and send data to the channel
transaction.channel.send(slice)
LOGGER.trace("SigHelper: Transaction unqueued and data sent to channel")
else
raise Exception.new("SigHelper: Received transaction was not in queue")
end
end
end
# Read a single packet from the socket
private def read_packet : {TransactionID, Bytes}
# Header
transaction_id = @conn.read_bytes(UInt32, NetworkEndian)
length = @conn.read_bytes(UInt32, NetworkEndian)
LOGGER.trace("SigHelper: Recv transaction 0x#{transaction_id.to_s(base: 16)} / length #{length}")
if length > 67_000
raise Exception.new("SigHelper: Packet longer than expected (#{length})")
end
# Payload
slice = Bytes.new(length)
@conn.read(slice) if length > 0
LOGGER.trace("SigHelper: payload = #{slice}")
LOGGER.trace("SigHelper: Recv transaction 0x#{transaction_id.to_s(base: 16)} - Done")
return transaction_id, slice
end
# Write a single packet to the socket
private def write_packet(transaction_id : TransactionID, request : Request)
LOGGER.trace("SigHelper: Send transaction 0x#{transaction_id.to_s(base: 16)} / opcode #{request.opcode}")
io = IO::Memory.new(1024)
io.write_bytes(request.opcode.to_u8, NetworkEndian)
io.write_bytes(transaction_id, NetworkEndian)
if payload = request.payload
payload.to_io(io)
end
@conn.send(io)
@conn.flush
LOGGER.trace("SigHelper: Send transaction 0x#{transaction_id.to_s(base: 16)} - Done")
end
end
class Connection
@socket : UNIXSocket | TCPSocket
{% if flag?(:advanced_debug) %}
@io : IO::Hexdump
{% end %}
def initialize(host_or_path : String)
case host_or_path
when .starts_with?('/')
# Make sure that the file exists
if File.exists?(host_or_path)
@socket = UNIXSocket.new(host_or_path)
else
raise Exception.new("SigHelper: '#{host_or_path}' no such file")
end
when .starts_with?("tcp://")
uri = URI.parse(host_or_path)
@socket = TCPSocket.new(uri.host.not_nil!, uri.port.not_nil!)
else
uri = URI.parse("tcp://#{host_or_path}")
@socket = TCPSocket.new(uri.host.not_nil!, uri.port.not_nil!)
end
LOGGER.info("SigHelper: Using helper at '#{host_or_path}'")
{% if flag?(:advanced_debug) %}
@io = IO::Hexdump.new(@socket, output: STDERR, read: true, write: true)
{% end %}
@socket.sync = false
@socket.blocking = false
end
def closed? : Bool
return @socket.closed?
end
def close : Nil
@socket.close if !@socket.closed?
end
def flush(*args, **options)
@socket.flush(*args, **options)
end
def send(*args, **options)
@socket.send(*args, **options)
end
# Wrap IO functions, with added debug tooling if needed
{% for function in %w(read read_bytes write write_bytes) %}
def {{function.id}}(*args, **options)
{% if flag?(:advanced_debug) %}
@io.{{function.id}}(*args, **options)
{% else %}
@socket.{{function.id}}(*args, **options)
{% end %}
end
{% end %}
end
end

View File

@@ -1,53 +0,0 @@
require "http/params"
require "./sig_helper"
class Invidious::DecryptFunction
@last_update : Time = Time.utc - 42.days
def initialize(uri_or_path)
@client = SigHelper::Client.new(uri_or_path)
self.check_update
end
def check_update
# If we have updated in the last 5 minutes, do nothing
return if (Time.utc - @last_update) < 5.minutes
# Get the amount of time elapsed since when the player was updated, in the
# event where multiple invidious processes are run in parallel.
update_time_elapsed = (@client.get_player_timestamp || 301).seconds
if update_time_elapsed > 5.minutes
LOGGER.debug("Signature: Player might be outdated, updating")
@client.force_update
@last_update = Time.utc
end
end
def decrypt_nsig(n : String) : String?
self.check_update
return @client.decrypt_n_param(n)
rescue ex
LOGGER.debug(ex.message || "Signature: Unknown error")
LOGGER.trace(ex.inspect_with_backtrace)
return nil
end
def decrypt_signature(str : String) : String?
self.check_update
return @client.decrypt_sig(str)
rescue ex
LOGGER.debug(ex.message || "Signature: Unknown error")
LOGGER.trace(ex.inspect_with_backtrace)
return nil
end
def get_sts : UInt64?
self.check_update
return @client.get_signature_timestamp
rescue ex
LOGGER.debug(ex.message || "Signature: Unknown error")
LOGGER.trace(ex.inspect_with_backtrace)
return nil
end
end

View File

@@ -1,3 +1,5 @@
PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com")
# See http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
def ci_lower_bound(pos, n)
if n == 0

View File

@@ -0,0 +1,120 @@
{% skip_file if compare_versions(Crystal::VERSION, "1.17.0-dev") < 0 %}
module Invidious::HttpServer
class StaticAssetsHandler < HTTP::StaticFileHandler
# In addition to storing the actual data of a file, it also implements the required
# getters needed for the object to imitate a `File::Stat` within `StaticFileHandler`.
#
# Since the `File::Stat` is created once in `#call` and then passed around to the
# rest of the class's methods, imitating the object allows us to only lookup
# the cache hash once for every request.
#
private record CachedFile, data : Bytes, size : Int64, modification_time : Time do
def directory?
false
end
def file?
true
end
end
CACHE_LIMIT = 5_000_000 # 5MB
@@current_cache_size = 0
@@cached_files = {} of Path => CachedFile
# Returns metadata for the requested file
#
# If the requested file is cached, a `CachedFile` is returned instead of a `File::Stat`.
# This represents the metadata info of a cached file and implements all the methods of `File::Stat` that
# is used by the `StaticAssetsHandler`.
#
# The `CachedFile` also stores the raw bytes of the cached file, and this method serves as the place where
# the cached file is retrieved if it exists. Though the data will only be read in `#serve_file`
private def file_info(expanded_path : Path)
file_path = @public_dir.join(expanded_path.to_kind(Path::Kind.native))
{@@cached_files[file_path]? || File.info?(file_path), file_path}
end
# Add "Cache-Control" header to the response
private def add_cache_headers(response_headers : HTTP::Headers, last_modified : Time) : Nil
super; response_headers["Cache-Control"] = "max-age=2629800"
end
# Serves and caches the file at the given path.
#
# This is an override of `serve_file` to allow serving a file from memory, and to cache it
# it as needed.
private def serve_file(context : HTTP::Server::Context, file_info, file_path : Path, original_file_path : Path, last_modified : Time)
context.response.content_type = MIME.from_filename(original_file_path.to_s, "application/octet-stream")
range_header = context.request.headers["Range"]?
# If the file is cached we can just directly serve it
if file_info.is_a? CachedFile
return dispatch_serve(context, file_info.data, file_info, range_header)
end
# Otherwise we'll need to read from disk and cache it
retrieve_bytes_from = IO::Memory.new
File.open(file_path) do |file|
# We cannot cache partial data so we'll rewind and read from the start
if range_header
dispatch_serve(context, file, file_info, range_header)
IO.copy(file.rewind, retrieve_bytes_from)
else
context.response.output = IO::MultiWriter.new(context.response.output, retrieve_bytes_from, sync_close: true)
dispatch_serve(context, file, file_info, range_header)
end
end
return flush_io_to_cache(retrieve_bytes_from, file_path, file_info)
end
# Writes file data to the cache
private def flush_io_to_cache(io, file_path, file_info)
if (@@current_cache_size += file_info.size) <= CACHE_LIMIT
@@cached_files[file_path] = CachedFile.new(io.to_slice, file_info.size, file_info.modification_time)
end
end
# Either send the file in full, or just fragments of it depending on the request
private def dispatch_serve(context, file, file_info, range_header)
if range_header
# an IO is needed for `serve_file_range`
file = file.is_a?(Bytes) ? IO::Memory.new(file, writeable: false) : file
serve_file_range(context, file, range_header, file_info)
else
context.response.headers["Accept-Ranges"] = "bytes"
serve_file_full(context, file, file_info)
end
end
# If we're serving the full file right away then there's no need for an IO at all.
private def serve_file_full(context : HTTP::Server::Context, file : Bytes, file_info)
context.response.status = :ok
context.response.content_length = file_info.size
context.response.write file
end
# Serves segments of a file based on the `Range header`
#
# An override of `serve_file_range` to allow using a generic IO rather than a `File`.
# Literally the same code as what we inherited but just with the `file` argument's type
# being set to `IO` rather than `File`
#
# Can be removed once https://github.com/crystal-lang/crystal/issues/15817 is fixed.
private def serve_file_range(context : HTTP::Server::Context, file : IO, range_header : String, file_info)
# Paste in the body of inherited serve_file_range
{{@type.superclass.methods.select(&.name.==("serve_file_range"))[0].body}}
end
# Clear cached files.
#
# This is only used in the specs to clear the cache before each handler test
def self.clear_cache
@@current_cache_size = 0
return @@cached_files.clear
end
end
end

View File

@@ -266,7 +266,6 @@ module Invidious::JSONify::APIv1
json.field "lengthSeconds", rv["length_seconds"]?.try &.to_i
json.field "viewCountText", rv["short_view_count"]?
json.field "viewCount", rv["view_count"]?.try &.empty? ? nil : rv["view_count"].to_i64
json.field "published", rv["published"]?
if rv["published"]?.try &.presence
json.field "publishedText", translate(locale, "`x` ago", recode_date(Time.parse_rfc3339(rv["published"].to_s), locale))

View File

@@ -107,7 +107,11 @@ struct Playlist
json.field "author", self.author
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
if !self.ucid.empty?
json.field "authorUrl", "/channel/#{self.ucid}"
else
json.field "authorUrl", ""
end
json.field "subtitle", self.subtitle
json.field "authorThumbnails" do
@@ -359,6 +363,9 @@ def fetch_playlist(plid : String)
thumbnail = playlist_info.dig?(
"thumbnailRenderer", "playlistVideoThumbnailRenderer",
"thumbnail", "thumbnails", 0, "url"
).try &.as_s || playlist_info.dig?(
"thumbnailRenderer", "playlistCustomThumbnailRenderer",
"thumbnail", "thumbnails", 0, "url"
).try &.as_s
views = 0_i64

View File

@@ -104,6 +104,7 @@ module Invidious::Routes::API::V1::Channels
json.field "tabs", channel.tabs
json.field "tags", channel.tags
json.field "authorVerified", channel.verified
json.field "pronouns", channel.pronouns
json.field "latestVideos" do
json.array do
@@ -436,7 +437,7 @@ module Invidious::Routes::API::V1::Channels
if ucid.nil?
response = YoutubeAPI.resolve_url("https://www.youtube.com/post/#{id}")
return error_json(400, "Invalid post ID") if response["error"]?
ucid = response.dig("endpoint", "browseEndpoint", "browseId").as_s
ucid = decode_ucid_from_post_protobuf(response.dig("endpoint", "browseEndpoint", "params").as_s)
else
ucid = ucid.to_s
end
@@ -460,13 +461,15 @@ module Invidious::Routes::API::V1::Channels
format = env.params.query["format"]?
format ||= "json"
sort_by = env.params.query["sort_by"]?.try &.downcase
sort_by ||= "top"
continuation = env.params.query["continuation"]?
case continuation
when nil, ""
ucid = env.params.query["ucid"]
comments = Comments.fetch_community_post_comments(ucid, id)
comments = Comments.fetch_community_post_comments(ucid, id, sort_by: sort_by)
else
comments = YoutubeAPI.browse(continuation: continuation)
end

View File

@@ -190,15 +190,30 @@ module Invidious::Routes::API::V1::Misc
sub_endpoint = endpoint["watchEndpoint"]? || endpoint["browseEndpoint"]? || endpoint
params = sub_endpoint.try &.dig?("params")
if sub_endpoint["browseId"]?.try &.as_s == "FEpost_detail"
decoded_protobuf = params.try &.as_s.try { |i| URI.decode_www_form(i) }
.try { |i| Base64.decode(i) }
.try { |i| IO::Memory.new(i) }
.try { |i| Protodec::Any.parse(i) }
ucid = decoded_protobuf.try(&.["56:0:embedded"]["2:0:string"].as_s)
post_id = decoded_protobuf.try(&.["56:0:embedded"]["3:1:string"].as_s)
else
ucid = sub_endpoint["browseId"]? if sub_endpoint["browseId"]? && sub_endpoint["browseId"]?.try &.as_s.starts_with? "UC"
post_id = nil
end
rescue ex
return error_json(500, ex)
end
JSON.build do |json|
json.object do
json.field "ucid", sub_endpoint["browseId"].as_s if sub_endpoint["browseId"]?
json.field "browseId", sub_endpoint["browseId"].as_s if sub_endpoint["browseId"]?
json.field "ucid", ucid if ucid != nil
json.field "videoId", sub_endpoint["videoId"].as_s if sub_endpoint["videoId"]?
json.field "playlistId", sub_endpoint["playlistId"].as_s if sub_endpoint["playlistId"]?
json.field "startTimeSeconds", sub_endpoint["startTimeSeconds"].as_i if sub_endpoint["startTimeSeconds"]?
json.field "postId", post_id if post_id != nil
json.field "params", params.try &.as_s
json.field "pageType", page_type
end

View File

@@ -1,6 +1,9 @@
require "html"
module Invidious::Routes::API::V1::Videos
private INTERNET_ARCHIVE_URL = URI.parse("https://archive.org")
private CHARS_SAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
def self.videos(env)
locale = env.get("preferences").as(Preferences).locale
@@ -279,7 +282,7 @@ module Invidious::Routes::API::V1::Videos
file = URI.encode_www_form("#{id[0, 3]}/#{id}.xml")
location = make_client(ARCHIVE_URL, &.get("/download/youtubeannotations_#{index}/#{id[0, 2]}.tar/#{file}"))
location = make_client(INTERNET_ARCHIVE_URL, &.get("/download/youtubeannotations_#{index}/#{id[0, 2]}.tar/#{file}"))
if !location.headers["Location"]?
env.response.status_code = location.status_code

View File

@@ -1,4 +1,16 @@
module Invidious::Routes::BeforeAll
struct CompanionCSP
property companion_urls : String = ""
def initialize
self.companion_urls = CONFIG.invidious_companion.reject(&.builtin_proxy).map do |companion|
"#{companion.public_url.scheme}://#{companion.public_url.host}#{companion.public_url.port ? ":#{companion.public_url.port}" : ""}"
end.join(" ")
end
end
private COMPANION_CSP = CompanionCSP.new
def self.handle(env)
preferences = Preferences.from_json("{}")
@@ -35,9 +47,9 @@ module Invidious::Routes::BeforeAll
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data:",
"font-src 'self' data:",
"connect-src 'self'",
"connect-src 'self' " + COMPANION_CSP.companion_urls,
"manifest-src 'self'",
"media-src 'self' blob:",
"media-src 'self' blob: " + COMPANION_CSP.companion_urls,
"child-src 'self' blob:",
"frame-src 'self'",
"frame-ancestors " + frame_ancestors,
@@ -63,6 +75,7 @@ module Invidious::Routes::BeforeAll
"/videoplayback",
"/latest_version",
"/download",
"/companion/",
}.any? { |r| env.request.resource.starts_with? r }
if env.request.cookies.has_key? "SID"
@@ -93,8 +106,8 @@ module Invidious::Routes::BeforeAll
end
dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s
thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s
thin_mode = thin_mode == "true"
thin_mode = env.params.query["thin_mode"]?
thin_mode = (thin_mode == "true") || preferences.thin_mode
locale = env.params.query["hl"]? || preferences.locale
preferences.dark_mode = dark_mode

View File

@@ -231,8 +231,10 @@ module Invidious::Routes::Channels
env.redirect "/post/#{URI.encode_www_form(lb)}?ucid=#{URI.encode_www_form(ucid)}"
end
thin_mode = env.params.query["thin_mode"]? || env.get("preferences").as(Preferences).thin_mode
thin_mode = thin_mode == "true"
preferences = env.get("preferences").as(Preferences)
thin_mode = env.params.query["thin_mode"]?
thin_mode = (thin_mode == "true") || preferences.thin_mode
continuation = env.params.query["continuation"]?
@@ -264,11 +266,11 @@ module Invidious::Routes::Channels
id = env.params.url["id"]
ucid = env.params.query["ucid"]?
prefs = env.get("preferences").as(Preferences)
preferences = env.get("preferences").as(Preferences)
locale = prefs.locale
locale = preferences.locale
thin_mode = env.params.query["thin_mode"]? || prefs.thin_mode
thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode
thin_mode = thin_mode == "true"
nojs = env.params.query["nojs"]?
@@ -284,7 +286,7 @@ module Invidious::Routes::Channels
response = YoutubeAPI.resolve_url("https://www.youtube.com/post/#{id}")
return error_template(400, "Invalid post ID") if response["error"]?
ucid = response.dig("endpoint", "browseEndpoint", "browseId").as_s
ucid = decode_ucid_from_post_protobuf(response.dig("endpoint", "browseEndpoint", "params").as_s)
post_response = fetch_channel_community_post(ucid, id, locale, "json", thin_mode)
end

View File

@@ -0,0 +1,60 @@
module Invidious::Routes::Companion
# GET /companion
def self.get_companion(env)
url = env.request.path
if env.request.query
url += "?#{env.request.query}"
end
begin
COMPANION_POOL.client do |wrapper|
wrapper.client.get(url, env.request.headers) do |resp|
return self.proxy_companion(env, resp)
end
end
rescue ex
end
end
# POST /companion
def self.post_companion(env)
url = env.request.path
if env.request.query
url += "?#{env.request.query}"
end
begin
COMPANION_POOL.client do |wrapper|
wrapper.client.post(url, env.request.headers, env.request.body) do |resp|
return self.proxy_companion(env, resp)
end
end
rescue ex
end
end
def self.options_companion(env)
url = env.request.path
if env.request.query
url += "?#{env.request.query}"
end
begin
COMPANION_POOL.client do |wrapper|
wrapper.client.options(url, env.request.headers) do |resp|
return self.proxy_companion(env, resp)
end
end
rescue ex
end
end
private def self.proxy_companion(env, response)
env.response.status_code = response.status_code
response.headers.each do |key, value|
env.response.headers[key] = value
end
return IO.copy response.body_io, env.response
end
end

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