1
0
Fork 0
forked from loeade/invidious

Compare commits

...

87 commits

Author SHA1 Message Date
82c962f3b8 Merge remote-tracking branch 'upstream/master' 2024-11-07 21:35:52 +01:00
Samantaz Fox
792d0d5f6d
CI: Check Crystal lint only on latest version (#5042)
* CI: Check Crystal lint only on latest version

* Apply suggestion from code review

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

---------

Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>
2024-11-07 13:14:36 +00:00
Émilien (perso)
ac6e796c73
checking the status code returned by youtube (#5052)
* checking the status code returned by youtube

* add documentation link

* Update src/invidious/yt_backend/youtube_api.cr

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

---------

Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com>
2024-11-07 14:04:43 +01:00
Samantaz Fox
2e3a7ad044
Update CHANGELOG.md 2024-10-30 17:13:00 +01:00
Samantaz Fox
c427c184e2
Captions: Add "Filipino (auto-generated)" to the list of languages (#4995)
I encountered a wild
[warn] i18n: Missing translation key "Filipino (auto-generated)"
while browsing videos on the test instance.

No related issue.
2024-10-30 17:07:09 +01:00
Samantaz Fox
59acf23c0c
Makefile: Add MT option to enable the 'preview_mt' flag (#4993)
This PR add an MT option to the Makefile. When make is invoked with 'MT=1',
the 'preview_mt' flag is passed to the Crystal compiler.

It doesn't mean that invidious fully supports multi-threading, but at least
it provides an easy way for trying that out.

No related issue.
2024-10-30 17:05:21 +01:00
Samantaz Fox
2eeb6a731d
SigHelper: Reconnect to signature helper (#4991)
Fijxu have been using it for more than 3 weeks on their instance and
they report that it works really well.

This only works if 'inv_sig_helper' itself crashes and restarts (via systemd
or docker restart policy) but it will not work if 'inv_sig_helper' hangs and
stops responding to invidious (but this is an issue with 'inv_sig_helper',
not Invidious).

Closes issue 4926
2024-10-30 17:02:51 +01:00
Samantaz Fox
0fb67cc090
Player: Fix a bug where menus were hard to open (#4750)
The fix basically enlarges the hoverable area in order to avoid getting the
menu closed if the mouse isn't moved fast enough.

Video of the fix:
https://www.loom.com/share/36494a3653984650aea3eaa2af276a35

Closes issue 4749
2024-10-30 16:59:34 +01:00
Samantaz Fox
9957da28dc
Proxy: Use connection pools for images (#4326)
Theoretically this should improve memory usage and performance by quite a bit
as we aren't creating a new HTTP::Client and in a turn a new connection for
every image we request from YouTube.

Closes issue 4009
2024-10-30 13:55:28 +01:00
Samantaz Fox
f326bcf8db
Add support for using Invidious through a HTTP Proxy (#4270)
Partially addresses issue 301
2024-10-30 13:46:49 +01:00
syeopite
d8b893e9ad
Bump CI matrix (#5015) 2024-10-18 21:33:38 +02:00
Émilien (perso)
70e4eb7f5d
Merge pull request #5004 from unixfox/update-mocks
update the mocks with the latest updated data
2024-10-14 00:06:29 +02:00
Emilien Devos
0d03818700 libsqlite3-dev is now missing in the CI env 2024-10-14 00:02:41 +02:00
Emilien Devos
e6f52eaf00 update submodule 2024-10-13 23:57:29 +02:00
Emilien Devos
90544e07b6 update the mocks with the latest updated data 2024-10-13 21:18:21 +02:00
Samantaz Fox
952b3625a0
Add "Filipino (auto-generated)" to the list of caption languages 2024-10-10 20:31:22 +02:00
Samantaz Fox
f51a3b8d2b
Makefile: Add MT option to enable the 'preview_mt' flag 2024-10-09 18:37:08 +02:00
Fijxu
84e4746265
SigHelper: Reconnect to signature helper
Signed-off-by: Fijxu <fijxu@nadeko.net>
2024-10-08 19:09:14 -03:00
Samantaz Fox
a88a723de3
Update CHANGELOG.md 2024-10-08 18:36:41 +02:00
Samantaz Fox
d5f5490aee
Search: Fix 'youtu.be' URLs in sanitizer (#4894)
Use the proper URL argument when transforming youtu.be URLs to their
youtube.com equivalents.

Thanks to Tuhgy on the fediverse for reporting this!

No related issue
2024-10-08 18:03:56 +02:00
Samantaz Fox
82d797b74e
Ameba: Disable Style/RedundantNext rule (#4888)
No related issue
2024-10-08 18:02:47 +02:00
Samantaz Fox
97895a491a
Playlists: Fix 'invalid byte sequence' error when subscribing (#4887)
In Crystal, handling multi-byte sequences in UTF-8 requires understanding that
slicing by bytes can lead to invalid sequences if the slicing isn't aligned
with character boundaries. In this case, attempting to slice a string by bytes
can cut through multi-byte UTF-8 sequences, leading to invalid sequences.

To avoid this, strings should be sliced based on characters rather than bytes.

Fixes issue 4886
2024-10-08 18:01:22 +02:00
Samantaz Fox
0ac9367322
Parse more metadata badges for SearchVideos (#4863)
This PR makes it possible to display badges in the search results for third
party Invidious applications (ex: FreeTube)

See also: https://github.com/FreeTubeApp/FreeTube/pull/5590

No related issue
2024-10-08 17:59:35 +02:00
Samantaz Fox
d3830f7870
Translations update from Hosted Weblate (#4862) 2024-10-08 17:56:39 +02:00
Samantaz Fox
3cfcc16403
Videos: Convert URL before putting result into cache (#4850)
Closes issue 4837
2024-10-08 17:52:34 +02:00
Samantaz Fox
171c0a0814
HTML: Add error message to "search issues on GitHub" link (#4652)
This PR adds the error message to the "search on Github" link located on
the crash page, so that the search bar is already filled and the issues
filtered when the user opens said link.

As seen with #4584 and other critical problems, duplicate issues end up
unnecessarily flooding the issue reports. While this change won't entirely
stop this behavior, this will help the user to easily identify if the error
they have received has been reported yet and discourage them from creating
a duplicate (hopefully...).

No associated issue was open
2024-10-08 17:51:36 +02:00
Samantaz Fox
82ac9a8609
Preferences: Add option to control preloading of video data (#4122)
This PR adds a configuration option to control the preloading of video data on
page load with the HTML5 'preload'[1] attribute on the `<video>` element.

The option is enabled by default, meaning that the `preload` attribute's value
will be 'auto'. If users want to prevent preloading of video data, they
can disable the option, which will set the attribute value to 'none'.

[1](https://www.w3schools.com/tags/att_video_preload.asp)

Closes issue 4110
2024-10-08 17:38:06 +02:00
Samantaz Fox
7c79ee7cc2
Performance: Improve speed of automatic instance redirection (#4193)
The automatic instance redirection implemented in #1940 fetches a new list of
instances each time someone queries the /redirect endpoint. This is extremely
inefficient...

This PR optimizes all that into a background job that only fetches a single
list every 30 minutes. This should performance quite a bit.

No related issue was opened.
2024-10-08 17:31:20 +02:00
ChunkyProgrammer
f6e09250cd
Use "LIVE" instead of "LIVE NOW" when parsing the live_now video badge
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2024-10-07 11:30:33 -04:00
Hosted Weblate
0fecde6917
Update Norwegian Bokmål translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Petter Reinholdtsen <pere-weblate@hungry.com>
2024-10-06 16:16:37 +02:00
Hosted Weblate
66f5b12ecd
Update Serbian (Cyrillic script) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: NEXI <nexiphotographer@gmail.com>
2024-10-06 16:16:37 +02:00
Hosted Weblate
77f57714ea
Update Chinese (Simplified Han script) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
2024-10-06 16:16:37 +02:00
Hosted Weblate
d9afe38504
Update Chinese (Traditional Han script) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff Huang <s8321414@gmail.com>
2024-10-06 16:16:36 +02:00
Hosted Weblate
3af11d800c
Update English (United States) translation
Co-authored-by: Dick Groskamp <dikgro@yahoo.co.uk>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2024-10-06 16:16:36 +02:00
Hosted Weblate
d72531d843
Update Korean translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: xrfmkrh <rF3nMd7sRKezjF2vcEQo@protonmail.com>
2024-10-06 16:16:35 +02:00
Hosted Weblate
ecfcad8d1c
Update Albanian translation
Co-authored-by: Besnik Bleta <besnik@programeshqip.org>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2024-10-06 16:16:35 +02:00
Hosted Weblate
d63b15dc1c
Update Serbian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: NEXI <nexiphotographer@gmail.com>
2024-10-06 16:16:34 +02:00
Hosted Weblate
edb69d601e
Update Persian translation
Co-authored-by: Danial Behzadi <dani.behzi@ubuntu.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2024-10-06 16:16:34 +02:00
Hosted Weblate
51562f4b24
Update Swedish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
2024-10-06 16:16:34 +02:00
Hosted Weblate
76f045b8d7
Update French translation
Co-authored-by: ABCraft19 <lesenfantsbergaoui@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2024-10-06 16:16:33 +02:00
Hosted Weblate
46eaa0f9b8
Update Spanish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
2024-10-06 16:16:33 +02:00
Hosted Weblate
56bccaba77
Update Dutch translation
Update Dutch translation

Update Dutch translation

Co-authored-by: Dick Groskamp <dikgro@yahoo.co.uk>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2024-10-06 16:16:33 +02:00
Hosted Weblate
4e8d03221b
Update Interlingua translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Software In Interlingua <softinterlingua@gmail.com>
2024-10-06 16:16:32 +02:00
Hosted Weblate
5d46eba6f2
Update Arabic translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
2024-10-06 16:16:32 +02:00
Hosted Weblate
d3eedab545
Update Italian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
2024-10-06 16:16:31 +02:00
Hosted Weblate
cd43997bba
Update Polish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Matthaiks <kitynska@gmail.com>
2024-10-06 16:16:31 +02:00
Hosted Weblate
fead7603e6
Update Croatian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
2024-10-06 16:16:31 +02:00
Hosted Weblate
486b5b363c
Update Icelandic translation
Co-authored-by: Sveinn í Felli <sv1@fellsnet.is>
2024-10-06 16:16:30 +02:00
Hosted Weblate
2b3619e489
Update Portuguese translation
Co-authored-by: Henrique Oliveira <ho.henrique@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2024-10-06 16:16:30 +02:00
Hosted Weblate
7a95cb43ef
Update Czech translation
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2024-10-06 16:16:29 +02:00
Hosted Weblate
e09a7de5c7
Update Japanese translation
Update Japanese translation

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>
2024-10-06 16:16:29 +02:00
Hosted Weblate
79d1aaff1a
Update Ukrainian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
2024-10-06 16:16:29 +02:00
Hosted Weblate
d7a5ca8fff
Update Russian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: lotigara <lotigara@yandex.ru>
2024-10-06 16:16:28 +02:00
Hosted Weblate
542d4fe553
Update Greek translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: hompre <46e989cc@opayq.com>
2024-10-06 16:16:28 +02:00
Hosted Weblate
33df8249f1
Update German translation
Update German translation

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>
2024-10-06 16:16:27 +02:00
Hosted Weblate
4e7fd7ac3b
Update Portuguese (Brazil) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jose Delvani <jsdelvani@users.noreply.hosted.weblate.org>
2024-10-06 16:16:27 +02:00
Hosted Weblate
8912e2448d
Update Turkish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
2024-10-06 16:16:26 +02:00
ChunkyProgrammer
98f1e4170b Rename CCommons to ClosedCaptions 2024-09-30 22:02:57 -04:00
ChunkyProgrammer
b384133dc9 Fix tests 2024-09-30 22:02:57 -04:00
ChunkyProgrammer
1961fc3b11 switch to enum flag instead of adding lots of properties to SearchVideo 2024-09-30 22:02:57 -04:00
ChunkyProgrammer
2e649363d2 Parse more metadata badges for SearchVideos 2024-09-30 22:02:57 -04:00
TheFrenchGhosty
53e8a5d62d
Remove myself from CODEOWNERS on the config file (#4942) 2024-09-28 23:54:52 +02:00
Samantaz Fox
5e899d73a9
Search: Fix for youtu.be URL in sanitizer 2024-09-02 18:14:57 +02:00
Thomas Lange
f247b2f862
Update config/config.example.yml
Accept suggested change from @SamantazFox.

Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2024-08-30 19:52:33 +02:00
Dmitry Sandalov
bd34659ff6
Fix 'invalid byte sequence' error when subscribing to playlists ([] accessor with range) 2024-08-29 22:47:59 +02:00
syeopite
f1baeef4bc
Ameba: Disable Style/RedundantNext rule 2024-08-28 23:49:10 -07:00
Dmitry Sandalov
157c4c3e98
Fix 'invalid byte sequence' error when subscribing to playlists 2024-08-28 23:54:31 +02:00
syeopite
75b68618ab
Remove useless proc usage in images.cr 2024-08-24 19:47:42 -07:00
syeopite
003c6f81dc
Preserve connection close header of get_storyboard 2024-08-24 19:47:42 -07:00
syeopite
4bc77b81bf
Move YTIMG_POOLS to connection_pool.cr 2024-08-24 19:47:40 -07:00
syeopite
06e1a508e8
Fix headers not being added in image requests
Regression from #2364
2024-08-24 19:45:52 -07:00
syeopite
52bc9aa328
Refactor duplicate logic in image routes 2024-08-24 19:45:52 -07:00
syeopite
480e073fa9
Use HTTP pools for image requests to YouTube 2024-08-24 19:45:52 -07:00
Samantaz Fox
b2133c6b2c
Videos: Convert URL before putting result into cache 2024-08-24 18:01:56 +02:00
giacomocerquone
288e1dccda Fix player menus hiding onHover 2024-06-13 01:10:35 +02:00
syeopite
9980c0e00f
Update uptime logic to handle updown.io response 2024-05-22 13:28:15 -07:00
syeopite
aa96cf3453
Fix invalid logic for instance uptime comparison 2024-05-22 13:22:00 -07:00
syeopite
41c978d350
Use HTTP::Client directly in instance list job
The HTTP::Client created via `make_client` is affected by the
force_resolve configuration option. However, api.invidious.io
does not support ipv6 and as such any request with ipv6 to
api.invidious.io will instead raise.

Directly calling the HTTP::Client will ignore the force_resolve option
allowing requests to go through ipv4 when needed.
2024-05-22 13:22:00 -07:00
syeopite
cff25a7b25
Refactor instance fetching logic into separate job 2024-05-22 13:22:00 -07:00
syeopite
6b7e730100
Validate override for crystal 1.12.1 2024-05-22 13:10:46 -07:00
syeopite
ccb2a6c58e
Bump http_proxy to v0.10.3 2024-04-28 21:34:05 -07:00
syeopite
3b471ae964
Automatically initialize proxy via stdlib override 2024-04-28 19:43:22 -07:00
syeopite
eb8fcc9e88
Add support for using HTTP proxies 2024-04-28 19:43:17 -07:00
tracedgod
5b11ca22d0 Use string interpolation instead of concatenation 2024-04-28 00:04:30 -04:00
tracedgod
6db4a46c5f update the url_search_issues variable to search for the current error on GitHub instead of showing all issues 2024-04-26 16:01:02 -04:00
Thomas Lange
824cc1a5aa Don't redefine the "preload" option in player.js
If the HTML5 "<video>" element defines the "preload" attribute directly,
it isn't necessary to redefine the "preload" option in the player.js.
2023-09-27 15:36:40 +02:00
Thomas Lange
bf470704a5 Add option to control preloading of video data
Fix #4110 by adding an option to control the preloading of video data on
page load. If disabled ("false"), the browser will not preload any video
data until the user explicitly hits the "Play" button.

If enabled ("true"), the default behavior will be used, which means the
browser decides how much of the video will be preloaded.
2023-09-26 22:21:12 +02:00
18 changed files with 247 additions and 136 deletions

View file

@ -38,10 +38,11 @@ jobs:
matrix:
stable: [true]
crystal:
- 1.9.2
- 1.10.1
- 1.11.2
- 1.12.1
- 1.13.2
- 1.14.0
include:
- crystal: nightly
stable: false
@ -51,6 +52,11 @@ jobs:
with:
submodules: true
- name: Install required APT packages
run: |
sudo apt install -y libsqlite3-dev
shell: bash
- name: Install Crystal
uses: crystal-lang/install-crystal@v1.8.0
with:
@ -59,7 +65,9 @@ jobs:
- name: Cache Shards
uses: actions/cache@v3
with:
path: ./lib
path: |
./lib
./bin
key: shards-${{ hashFiles('shard.lock') }}
- name: Install Shards
@ -71,14 +79,6 @@ jobs:
- name: Run tests
run: crystal spec
- name: Run lint
run: |
if ! crystal tool format --check; then
crystal tool format
git diff
exit 1
fi
- name: Build
run: crystal build --warnings all --error-on-warnings --error-trace src/invidious.cr
@ -124,8 +124,12 @@ jobs:
- name: Test Docker
run: while curl -Isf http://localhost:3000; do sleep 1; done
ameba_lint:
lint:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
with:
@ -145,7 +149,18 @@ jobs:
key: shards-${{ hashFiles('shard.lock') }}
- name: Install Shards
run: shards install
run: |
if ! shards check; then
shards install
fi
- name: Check Crystal formatter compliance
run: |
if ! crystal tool format --check; then
crystal tool format
git diff
exit 1
fi
- name: Run Ameba linter
run: bin/ameba

View file

@ -5,6 +5,12 @@
### Full list of pull requests merged since the last release (newest first)
* Add "Filipino (auto-generated)" to the list of caption languages ([#4995], by @SamantazFox)
* Makefile: Add MT option to enable the 'preview_mt' flag ([#4993], by @SamantazFox)
* SigHelper: Reconnect to signature helper ([#4991], thanks @Fijxu)
* Fix player menus hiding onHover ready ([#4750], thanks @giacomocerquone)
* Use connection pools when requesting images from YouTube ([#4326], thanks @syeopite)
* Add support for using Invidious through a HTTP Proxy ([#4270], thanks @syeopite)
* Search: Fix 'youtu.be' URLs in sanitizer ([#4894], by @SamantazFox)
* Ameba: Disable Style/RedundantNext rule ([#4888], thanks @syeopite)
* Playlists: Fix 'invalid byte sequence' error when subscribing ([#4887], thanks @DmitrySandalov)
@ -22,7 +28,10 @@
[#4122]: https://github.com/iv-org/invidious/pull/4122
[#4193]: https://github.com/iv-org/invidious/pull/4193
[#4270]: https://github.com/iv-org/invidious/pull/4270
[#4326]: https://github.com/iv-org/invidious/pull/4326
[#4652]: https://github.com/iv-org/invidious/pull/4652
[#4750]: https://github.com/iv-org/invidious/pull/4750
[#4850]: https://github.com/iv-org/invidious/pull/4850
[#4862]: https://github.com/iv-org/invidious/pull/4862
[#4863]: https://github.com/iv-org/invidious/pull/4863
@ -33,6 +42,9 @@
[#4928]: https://github.com/iv-org/invidious/pull/4928
[#4930]: https://github.com/iv-org/invidious/pull/4930
[#4942]: https://github.com/iv-org/invidious/pull/4942
[#4991]: https://github.com/iv-org/invidious/pull/4991
[#4993]: https://github.com/iv-org/invidious/pull/4993
[#4995]: https://github.com/iv-org/invidious/pull/4995
## v2.20240825.2 (2024-08-26)

View file

@ -7,6 +7,11 @@ STATIC := 0
NO_DBG_SYMBOLS := 0
# Enable multi-threading.
# Warning: Experimental feature!!
# invidious is not stable when MT is enabled.
MT := 0
FLAGS ?=
@ -19,6 +24,10 @@ ifeq ($(STATIC), 1)
FLAGS += --static
endif
ifeq ($(MT), 1)
FLAGS += -Dpreview_mt
endif
ifeq ($(NO_DBG_SYMBOLS), 1)
FLAGS += --no-debug

View file

@ -68,6 +68,7 @@
.video-js.player-style-youtube .vjs-menu-button-popup .vjs-menu {
margin-bottom: 2em;
padding-top: 2em
}
.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {height: 5px;

View file

@ -173,6 +173,17 @@ https_only: false
##
#force_resolve:
##
## Configuration for using a HTTP proxy
##
## If unset, then no HTTP proxy will be used.
##
http_proxy:
user:
password:
host:
port:
##
## Use Innertube's transcripts API instead of timedtext for closed captions

View file

@ -286,6 +286,7 @@
"Esperanto": "Esperanto",
"Estonian": "Estonian",
"Filipino": "Filipino",
"Filipino (auto-generated)": "Filipino (auto-generated)",
"Finnish": "Finnish",
"French": "French",
"French (auto-generated)": "French (auto-generated)",

2
mocks

@ -1 +1 @@
Subproject commit 11ec372f72747c09d48ffef04843f72be67d5b54
Subproject commit b55d58dea94f7144ff0205857dfa70ec14eaa872

View file

@ -10,7 +10,7 @@ shards:
backtracer:
git: https://github.com/sija/backtracer.cr.git
version: 1.2.1
version: 1.2.2
db:
git: https://github.com/crystal-lang/crystal-db.git
@ -20,6 +20,10 @@ shards:
git: https://github.com/crystal-loot/exception_page.git
version: 0.2.2
http_proxy:
git: https://github.com/mamantoha/http_proxy.git
version: 0.10.3
kemal:
git: https://github.com/kemalcr/kemal.git
version: 1.1.2
@ -42,7 +46,7 @@ shards:
spectator:
git: https://github.com/icy-arctic-fox/spectator.git
version: 0.10.4
version: 0.10.6
sqlite3:
git: https://github.com/crystal-lang/crystal-sqlite3.git

View file

@ -28,6 +28,9 @@ dependencies:
athena-negotiation:
github: athena-framework/negotiation
version: ~> 0.1.1
http_proxy:
github: mamantoha/http_proxy
version: ~> 0.10.3
development_dependencies:
spectator:

View file

@ -17,8 +17,8 @@ Spectator.describe "parse_video_info" do
# Basic video infos
expect(info["title"].as_s).to eq("I Gave My 100,000,000th Subscriber An Island")
expect(info["views"].as_i).to eq(126_573_823)
expect(info["likes"].as_i).to eq(5_157_654)
expect(info["views"].as_i).to eq(220_226_287)
expect(info["likes"].as_i).to eq(6_870_691)
# For some reason the video length from VideoDetails and the
# one from microformat differs by 1s...
@ -48,12 +48,12 @@ Spectator.describe "parse_video_info" do
expect(info["relatedVideos"].as_a.size).to eq(20)
expect(info["relatedVideos"][0]["id"]).to eq("Hwybp38GnZw")
expect(info["relatedVideos"][0]["title"]).to eq("I Built Willy Wonka's Chocolate Factory!")
expect(info["relatedVideos"][0]["id"]).to eq("krsBRQbOPQ4")
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("179877630")
expect(info["relatedVideos"][0]["short_view_count"]).to eq("179M")
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")
# Description
@ -76,11 +76,11 @@ Spectator.describe "parse_video_info" do
expect(info["ucid"].as_s).to eq("UCX6OQ3DkcsbYNE6H8uQQuVA")
expect(info["authorThumbnail"].as_s).to eq(
"https://yt3.ggpht.com/ytc/AL5GRJVuqw82ERvHzsmBxL7avr1dpBtsVIXcEzBPZaloFg=s48-c-k-c0x00ffffff-no-rj"
"https://yt3.ggpht.com/fxGKYucJAVme-Yz4fsdCroCFCrANWqw0ql4GYuvx8Uq4l_euNJHgE-w9MTkLQA805vWCi-kE0g=s48-c-k-c0x00ffffff-no-rj"
)
expect(info["authorVerified"].as_bool).to be_true
expect(info["subCountText"].as_s).to eq("143M")
expect(info["subCountText"].as_s).to eq("320M")
end
it "parses a regular video with no descrition/comments" do
@ -99,8 +99,8 @@ Spectator.describe "parse_video_info" do
# Basic video infos
expect(info["title"].as_s).to eq("Chris Rea - Auberge")
expect(info["views"].as_i).to eq(10_943_126)
expect(info["likes"].as_i).to eq(0)
expect(info["views"].as_i).to eq(14_324_584)
expect(info["likes"].as_i).to eq(35_870)
expect(info["lengthSeconds"].as_i).to eq(283_i64)
expect(info["published"].as_s).to eq("2012-05-21T00:00:00Z")
@ -132,14 +132,14 @@ Spectator.describe "parse_video_info" do
# Related videos
expect(info["relatedVideos"].as_a.size).to eq(19)
expect(info["relatedVideos"].as_a.size).to eq(20)
expect(info["relatedVideos"][0]["id"]).to eq("Ww3KeZ2_Yv4")
expect(info["relatedVideos"][0]["title"]).to eq("Chris Rea")
expect(info["relatedVideos"][0]["author"]).to eq("PanMusic")
expect(info["relatedVideos"][0]["ucid"]).to eq("UCsKAPSuh1iNbLWUga_igPyA")
expect(info["relatedVideos"][0]["view_count"]).to eq("31581")
expect(info["relatedVideos"][0]["short_view_count"]).to eq("31K")
expect(info["relatedVideos"][0]["id"]).to eq("gUUdQfnshJ4")
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")
# Description
@ -156,11 +156,13 @@ Spectator.describe "parse_video_info" do
# Author infos
expect(info["author"].as_s).to eq("ChrisReaOfficial")
expect(info["author"].as_s).to eq("ChrisReaVideos")
expect(info["ucid"].as_s).to eq("UC_5q6nWPbD30-y6oiWF_oNA")
expect(info["authorThumbnail"].as_s).to be_empty
expect(info["authorThumbnail"].as_s).to eq(
"https://yt3.ggpht.com/ytc/AIdro_n71nsegpKfjeRKwn1JJmK5IVMh_7j5m_h3_1KnUUg=s48-c-k-c0x00ffffff-no-rj"
)
expect(info["authorVerified"].as_bool).to be_false
expect(info["subCountText"].as_s).to eq("-")
expect(info["subCountText"].as_s).to eq("3.11K")
end
end

View file

@ -23,6 +23,7 @@ require "kilt"
require "./ext/kemal_content_for.cr"
require "./ext/kemal_static_file_handler.cr"
require "http_proxy"
require "athena-negotiation"
require "openssl/hmac"
require "option_parser"
@ -92,6 +93,10 @@ SOFTWARE = {
YT_POOL = YoutubeConnectionPool.new(YT_URL, capacity: CONFIG.pool_size)
# Image request pool
GGPHT_POOL = YoutubeConnectionPool.new(URI.parse("https://yt3.ggpht.com"), capacity: CONFIG.pool_size)
# CLI
Kemal.config.extra_options do |parser|
parser.banner = "Usage: invidious [arguments]"

View file

@ -55,6 +55,15 @@ struct ConfigPreferences
end
end
struct HTTPProxyConfig
include YAML::Serializable
property user : String
property password : String
property host : String
property port : Int32
end
class Config
include YAML::Serializable
@ -129,6 +138,8 @@ class Config
property host_binding : String = "0.0.0.0"
# Pool size for HTTP requests to youtube.com and ytimg.com (each domain has a separate pool of `pool_size`)
property pool_size : Int32 = 100
# HTTP Proxy configuration
property http_proxy : HTTPProxyConfig? = nil
# Use Innertube's transcripts API instead of timedtext for closed captions
property use_innertube_for_captions : Bool = false

View file

@ -18,6 +18,40 @@ end
class HTTP::Client
property family : Socket::Family = Socket::Family::UNSPEC
# Override stdlib to automatically initialize proxy if configured
#
# Accurate as of crystal 1.12.1
def initialize(@host : String, port = nil, tls : TLSContext = nil)
check_host_only(@host)
{% if flag?(:without_openssl) %}
if tls
raise "HTTP::Client TLS is disabled because `-D without_openssl` was passed at compile time"
end
@tls = nil
{% else %}
@tls = case tls
when true
OpenSSL::SSL::Context::Client.new
when OpenSSL::SSL::Context::Client
tls
when false, nil
nil
end
{% end %}
@port = (port || (@tls ? 443 : 80)).to_i
self.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy
end
def initialize(@io : IO, @host = "", @port = 80)
@reconnect = false
self.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy
end
private def io
io = @io
return io if io

View file

@ -175,8 +175,9 @@ module Invidious::SigHelper
@queue = {} of TransactionID => Transaction
@conn : Connection
@uri_or_path : String
def initialize(uri_or_path)
def initialize(@uri_or_path)
@conn = Connection.new(uri_or_path)
listen
end
@ -186,10 +187,26 @@ module Invidious::SigHelper
LOGGER.debug("SigHelper: Multiplexor listening")
# TODO: reopen socket if unexpectedly closed
spawn do
loop do
receive_data
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

View file

@ -11,29 +11,9 @@ module Invidious::Routes::Images
end
end
# We're encapsulating this into a proc in order to easily reuse this
# portion of the code for each request block below.
request_proc = ->(response : HTTP::Client::Response) {
env.response.status_code = response.status_code
response.headers.each do |key, value|
if !RESPONSE_HEADERS_BLACKLIST.includes?(key.downcase)
env.response.headers[key] = value
end
end
env.response.headers["Access-Control-Allow-Origin"] = "*"
if response.status_code >= 300
env.response.headers.delete("Transfer-Encoding")
return
end
proxy_file(response, env)
}
begin
HTTP::Client.get("https://yt3.ggpht.com#{url}") do |resp|
return request_proc.call(resp)
GGPHT_POOL.client &.get(url, headers) do |resp|
return self.proxy_image(env, resp)
end
rescue ex
end
@ -61,27 +41,10 @@ module Invidious::Routes::Images
end
end
request_proc = ->(response : HTTP::Client::Response) {
env.response.status_code = response.status_code
response.headers.each do |key, value|
if !RESPONSE_HEADERS_BLACKLIST.includes?(key.downcase)
env.response.headers[key] = value
end
end
env.response.headers["Connection"] = "close"
env.response.headers["Access-Control-Allow-Origin"] = "*"
if response.status_code >= 300
return env.response.headers.delete("Transfer-Encoding")
end
proxy_file(response, env)
}
begin
HTTP::Client.get("https://#{authority}.ytimg.com#{url}") do |resp|
return request_proc.call(resp)
get_ytimg_pool(authority).client &.get(url, headers) do |resp|
env.response.headers["Connection"] = "close"
return self.proxy_image(env, resp)
end
rescue ex
end
@ -101,26 +64,9 @@ module Invidious::Routes::Images
end
end
request_proc = ->(response : HTTP::Client::Response) {
env.response.status_code = response.status_code
response.headers.each do |key, value|
if !RESPONSE_HEADERS_BLACKLIST.includes?(key.downcase)
env.response.headers[key] = value
end
end
env.response.headers["Access-Control-Allow-Origin"] = "*"
if response.status_code >= 300 && response.status_code != 404
return env.response.headers.delete("Transfer-Encoding")
end
proxy_file(response, env)
}
begin
HTTP::Client.get("https://i9.ytimg.com#{url}") do |resp|
return request_proc.call(resp)
get_ytimg_pool("i9").client &.get(url, headers) do |resp|
return self.proxy_image(env, resp)
end
rescue ex
end
@ -165,8 +111,7 @@ module Invidious::Routes::Images
if name == "maxres.jpg"
build_thumbnails(id).each do |thumb|
thumbnail_resource_path = "/vi/#{id}/#{thumb[:url]}.jpg"
# This can likely be optimized into a (small) pool sometime in the future.
if HTTP::Client.head("https://i.ytimg.com#{thumbnail_resource_path}").status_code == 200
if get_ytimg_pool("i9").client &.head(thumbnail_resource_path, headers).status_code == 200
name = thumb[:url] + ".jpg"
break
end
@ -181,29 +126,28 @@ module Invidious::Routes::Images
end
end
request_proc = ->(response : HTTP::Client::Response) {
env.response.status_code = response.status_code
response.headers.each do |key, value|
if !RESPONSE_HEADERS_BLACKLIST.includes?(key.downcase)
env.response.headers[key] = value
end
end
env.response.headers["Access-Control-Allow-Origin"] = "*"
if response.status_code >= 300 && response.status_code != 404
return env.response.headers.delete("Transfer-Encoding")
end
proxy_file(response, env)
}
begin
# This can likely be optimized into a (small) pool sometime in the future.
HTTP::Client.get("https://i.ytimg.com#{url}") do |resp|
return request_proc.call(resp)
get_ytimg_pool("i").client &.get(url, headers) do |resp|
return self.proxy_image(env, resp)
end
rescue ex
end
end
private def self.proxy_image(env, response)
env.response.status_code = response.status_code
response.headers.each do |key, value|
if !RESPONSE_HEADERS_BLACKLIST.includes?(key.downcase)
env.response.headers[key] = value
end
end
env.response.headers["Access-Control-Allow-Origin"] = "*"
if response.status_code >= 300
return env.response.headers.delete("Transfer-Encoding")
end
return proxy_file(response, env)
end
end

View file

@ -123,6 +123,7 @@ module Invidious::Videos
"Esperanto",
"Estonian",
"Filipino",
"Filipino (auto-generated)",
"Finnish",
"French",
"French (auto-generated)",

View file

@ -1,17 +1,6 @@
def add_yt_headers(request)
request.headers.delete("User-Agent") if request.headers["User-Agent"] == "Crystal"
request.headers["User-Agent"] ||= "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
request.headers["Accept-Charset"] ||= "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
request.headers["Accept"] ||= "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
request.headers["Accept-Language"] ||= "en-us,en;q=0.5"
# Preserve original cookies and add new YT consent cookie for EU servers
request.headers["Cookie"] = "#{request.headers["cookie"]?}; CONSENT=PENDING+#{Random.rand(100..999)}"
if !CONFIG.cookies.empty?
request.headers["Cookie"] = "#{(CONFIG.cookies.map { |c| "#{c.name}=#{c.value}" }).join("; ")}; #{request.headers["cookie"]?}"
end
end
# Mapping of subdomain => YoutubeConnectionPool
# This is needed as we may need to access arbitrary subdomains of ytimg
private YTIMG_POOLS = {} of String => YoutubeConnectionPool
struct YoutubeConnectionPool
property! url : URI
@ -26,12 +15,16 @@ struct YoutubeConnectionPool
def client(&)
conn = pool.checkout
# Proxy needs to be reinstated every time we get a client from the pool
conn.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy
begin
response = yield conn
rescue ex
conn.close
conn = HTTP::Client.new(url)
conn = HTTP::Client.new(url)
conn.proxy = make_configured_http_proxy_client() if CONFIG.http_proxy
conn.family = CONFIG.force_resolve
conn.family = Socket::Family::INET if conn.family == Socket::Family::UNSPEC
conn.before_request { |r| add_yt_headers(r) } if url.host == "www.youtube.com"
@ -54,6 +47,21 @@ struct YoutubeConnectionPool
end
end
def add_yt_headers(request)
request.headers.delete("User-Agent") if request.headers["User-Agent"] == "Crystal"
request.headers["User-Agent"] ||= "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
request.headers["Accept-Charset"] ||= "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
request.headers["Accept"] ||= "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
request.headers["Accept-Language"] ||= "en-us,en;q=0.5"
# Preserve original cookies and add new YT consent cookie for EU servers
request.headers["Cookie"] = "#{request.headers["cookie"]?}; CONSENT=PENDING+#{Random.rand(100..999)}"
if !CONFIG.cookies.empty?
request.headers["Cookie"] = "#{(CONFIG.cookies.map { |c| "#{c.name}=#{c.value}" }).join("; ")}; #{request.headers["cookie"]?}"
end
end
def make_client(url : URI, region = nil, force_resolve : Bool = false)
client = HTTP::Client.new(url)
@ -77,3 +85,31 @@ def make_client(url : URI, region = nil, force_resolve : Bool = false, &)
client.close
end
end
def make_configured_http_proxy_client
# This method is only called when configuration for an HTTP proxy are set
config_proxy = CONFIG.http_proxy.not_nil!
return HTTP::Proxy::Client.new(
config_proxy.host,
config_proxy.port,
username: config_proxy.user,
password: config_proxy.password,
)
end
# Fetches a HTTP pool for the specified subdomain of ytimg.com
#
# Creates a new one when the specified pool for the subdomain does not exist
def get_ytimg_pool(subdomain)
if pool = YTIMG_POOLS[subdomain]?
return pool
else
LOGGER.info("ytimg_pool: Creating a new HTTP pool for \"https://#{subdomain}.ytimg.com\"")
pool = YoutubeConnectionPool.new(URI.parse("https://#{subdomain}.ytimg.com"), capacity: CONFIG.pool_size)
YTIMG_POOLS[subdomain] = pool
return pool
end
end

View file

@ -638,6 +638,11 @@ module YoutubeAPI
# Send the POST request
body = YT_POOL.client() do |client|
client.post(url, headers: headers, body: data.to_json) do |response|
if response.status_code != 200
raise InfoException.new("Error: non 200 status code. Youtube API returned \
status code #{response.status_code}. See <a href=\"https://docs.invidious.io/youtube-errors-explained/\"> \
https://docs.invidious.io/youtube-errors-explained/</a> for troubleshooting.")
end
self._decompress(response.body_io, response.headers["Content-Encoding"]?)
end
end