From 73bf956af5ff2595c4840397924127e7cf329598 Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Wed, 19 Feb 2025 21:08:45 -0500 Subject: [PATCH 01/44] captions: provide "w", "o", "-", "+" keydowns for player from YT --- assets/css/player.css | 18 +++++++++++++++--- assets/js/player.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/assets/css/player.css b/assets/css/player.css index 9cb400ad..028d5631 100644 --- a/assets/css/player.css +++ b/assets/css/player.css @@ -71,8 +71,10 @@ padding-top: 2em } -.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {height: 5px; -margin-bottom: 10px;} +.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control { + height: 5px; + margin-bottom: 10px; +} ul.vjs-menu-content::-webkit-scrollbar { display: none; @@ -82,10 +84,20 @@ ul.vjs-menu-content::-webkit-scrollbar { cursor: none; } +/* Customizable CSS in player.js */ +.vjs-text-track-display > div > div +{ + background-color: rgba(0, 0, 0, 0); /* caption window background: toggle with "w" event */ +} + +/* Customizable CSS in player.js */ .video-js .vjs-text-track-display > div > div > div { - background-color: rgba(0, 0, 0, 0.75) !important; + font-size: 27px !important; /* Toggle with "-/=" event */ + background-color: rgba(0, 0, 0, 0.75) !important; /* caption background: toggle with "w" event */ + color: rgb(255, 255, 255, 1) !important; /* caption text: toggle with "o" event */ border-radius: 9px !important; padding: 5px !important; + line-height: 1.5 !important; } .vjs-play-control, diff --git a/assets/js/player.js b/assets/js/player.js index 353a5296..c74a68a4 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -2,9 +2,16 @@ var player_data = JSON.parse(document.getElementById('player_data').textContent); var video_data = JSON.parse(document.getElementById('video_data').textContent); +var player_css = [...Array.from(document.styleSheets).find(sS => sS.href?.includes('player.css')).cssRules] +var caption_background_css = player_css.find(rule => rule.selectorText === '.vjs-text-track-display > div > div'); +var caption_text_css = player_css.find(rule => rule.selectorText === '.video-js .vjs-text-track-display > div > div > div'); + var options = { liveui: true, playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], + captionSizes: ['22px', '27px', '32px', '37px'], + captionBackground: [0, 0.5, 0.8, 1].map(a => 'rgba(0, 0, 0, ' + a + ')'), + captionOpacity: [0.4, 0.7, 1].map(a => 'rgba(255, 255, 255, ' + a + ')'), controlBar: { children: [ 'playToggle', @@ -591,6 +598,31 @@ function increase_playback_rate(steps) { player.playbackRate(options.playbackRates[newIndex]); } +function increase_caption_size(steps) { + const maxIndex = options.captionSizes.length - 1; + const font_size = caption_text_css.style.getPropertyValue('font-size'); + const curIndex = options.captionSizes.indexOf(font_size); + let newIndex = curIndex + steps; + newIndex = helpers.clamp(newIndex, 0, maxIndex); + caption_text_css.style.setProperty('font-size', options.captionSizes[newIndex], 'important'); +} + +function toggle_caption_window() { + const numOptions = options.captionBackground.length; + const backgroundColor = caption_background_css.style.getPropertyValue('background-color'); + const curIndex = options.captionBackground.indexOf(backgroundColor); + const newIndex = (curIndex + 1) % numOptions; + caption_background_css.style.setProperty('background-color', options.captionBackground[newIndex], 'important'); +} + +function toggle_caption_opacity() { + const numOptions = options.captionOpacity.length; + const opacity = caption_text_css.style.getPropertyValue('color'); + const curIndex = options.captionOpacity.indexOf(opacity); + const newIndex = (curIndex + 1) % numOptions; + caption_text_css.style.setProperty('color', options.captionOpacity[newIndex], 'important'); +} + addEventListener('keydown', function (e) { if (e.target.tagName.toLowerCase() === 'input') { // Ignore input when focus is on certain elements, e.g. form fields. @@ -686,6 +718,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); From bc3b3f6d69977e799f4d4e99d5c0283916d0ca83 Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Thu, 20 Mar 2025 10:09:43 -0400 Subject: [PATCH 02/44] updated caption features to use videojs interface --- assets/css/player.css | 11 +-------- assets/js/player.js | 52 ++++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/assets/css/player.css b/assets/css/player.css index 028d5631..60f3ce73 100644 --- a/assets/css/player.css +++ b/assets/css/player.css @@ -84,17 +84,8 @@ ul.vjs-menu-content::-webkit-scrollbar { cursor: none; } -/* Customizable CSS in player.js */ -.vjs-text-track-display > div > div -{ - background-color: rgba(0, 0, 0, 0); /* caption window background: toggle with "w" event */ -} - -/* Customizable CSS in player.js */ .video-js .vjs-text-track-display > div > div > div { - font-size: 27px !important; /* Toggle with "-/=" event */ - background-color: rgba(0, 0, 0, 0.75) !important; /* caption background: toggle with "w" event */ - color: rgb(255, 255, 255, 1) !important; /* caption text: toggle with "o" event */ + background-color: rgba(0, 0, 0, 0.75) !important; border-radius: 9px !important; padding: 5px !important; line-height: 1.5 !important; diff --git a/assets/js/player.js b/assets/js/player.js index c74a68a4..dce432cb 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -2,16 +2,12 @@ var player_data = JSON.parse(document.getElementById('player_data').textContent); var video_data = JSON.parse(document.getElementById('video_data').textContent); -var player_css = [...Array.from(document.styleSheets).find(sS => sS.href?.includes('player.css')).cssRules] -var caption_background_css = player_css.find(rule => rule.selectorText === '.vjs-text-track-display > div > div'); -var caption_text_css = player_css.find(rule => rule.selectorText === '.video-js .vjs-text-track-display > div > div > div'); - var options = { liveui: true, playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0], - captionSizes: ['22px', '27px', '32px', '37px'], - captionBackground: [0, 0.5, 0.8, 1].map(a => 'rgba(0, 0, 0, ' + a + ')'), - captionOpacity: [0.4, 0.7, 1].map(a => 'rgba(255, 255, 255, ' + a + ')'), + fontPercent: [0.5, 0.75, 1.25, 1.5, 1.75, 2, 3, 4], + windowOpacity: ['0', '0.5', '1'], + textOpacity: ['0.5', '1'], controlBar: { children: [ 'playToggle', @@ -543,9 +539,9 @@ const toggle_captions = (function () { bindChange('off'); track.mode = mode; setTimeout(function () { - bindChange('on'); + bindChange('on'); }, 0); - } + } bindChange('on'); return function () { @@ -586,6 +582,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(); } @@ -599,28 +602,31 @@ function increase_playback_rate(steps) { } function increase_caption_size(steps) { - const maxIndex = options.captionSizes.length - 1; - const font_size = caption_text_css.style.getPropertyValue('font-size'); - const curIndex = options.captionSizes.indexOf(font_size); + 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); - caption_text_css.style.setProperty('font-size', options.captionSizes[newIndex], 'important'); + player.textTrackSettings.setValues({ fontPercent: options.fontPercent[newIndex] }); + update_captions(); } function toggle_caption_window() { - const numOptions = options.captionBackground.length; - const backgroundColor = caption_background_css.style.getPropertyValue('background-color'); - const curIndex = options.captionBackground.indexOf(backgroundColor); + const numOptions = options.windowOpacity.length; + const windowOpacity = player.textTrackSettings.getValues().windowOpacity || '0'; + const curIndex = options.windowOpacity.indexOf(windowOpacity); const newIndex = (curIndex + 1) % numOptions; - caption_background_css.style.setProperty('background-color', options.captionBackground[newIndex], 'important'); + player.textTrackSettings.setValues({ windowOpacity: options.windowOpacity[newIndex] }); + update_captions(); } - -function toggle_caption_opacity() { - const numOptions = options.captionOpacity.length; - const opacity = caption_text_css.style.getPropertyValue('color'); - const curIndex = options.captionOpacity.indexOf(opacity); + + 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; - caption_text_css.style.setProperty('color', options.captionOpacity[newIndex], 'important'); + player.textTrackSettings.setValues({ textOpacity: options.textOpacity[newIndex] }); + update_captions(); } addEventListener('keydown', function (e) { From e67a30b124debf30363e5e576f089b26c46f7c93 Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Thu, 20 Mar 2025 10:29:26 -0400 Subject: [PATCH 03/44] formatting --- assets/js/player.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/js/player.js b/assets/js/player.js index dce432cb..d6f2ec64 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -539,9 +539,9 @@ const toggle_captions = (function () { bindChange('off'); track.mode = mode; setTimeout(function () { - bindChange('on'); + bindChange('on'); }, 0); - } + } bindChange('on'); return function () { @@ -584,9 +584,9 @@ 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(); - } + if (document.body.querySelector('.vjs-text-track-cue')) { + toggle_captions(); toggle_captions(); + } } function toggle_fullscreen() { @@ -620,7 +620,7 @@ function toggle_caption_window() { update_captions(); } - function toggle_caption_opacity() { +function toggle_caption_opacity() { const numOptions = options.textOpacity.length; const textOpacity = player.textTrackSettings.getValues().textOpacity || '1'; const curIndex = options.textOpacity.indexOf(textOpacity); From bef2d7b6b515bc90d8a58e3fa9776ab52fc48039 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 01:07:40 -0400 Subject: [PATCH 04/44] 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 --- .github/workflows/build-nightly-container.yml | 55 +++++++------------ .github/workflows/build-stable-container.yml | 55 +++++++------------ .github/workflows/ci.yml | 20 ++----- docker-compose-arm64.yml | 55 +++++++++++++++++++ 4 files changed, 101 insertions(+), 84 deletions(-) create mode 100644 docker-compose-arm64.yml diff --git a/.github/workflows/build-nightly-container.yml b/.github/workflows/build-nightly-container.yml index 4149bd0b..3277c015 100644 --- a/.github/workflows/build-nightly-container.yml +++ b/.github/workflows/build-nightly-container.yml @@ -17,17 +17,27 @@ on: jobs: release: - runs-on: ubuntu-latest + strategy: + matrix: + include: + - os: ubuntu-latest + platforms: linux/amd64 + name: "AMD64" + dockerfile: "docker/Dockerfile" + tag_suffix: "" + # GitHub doesn't has a ubuntu-latest-arm runner + - os: ubuntu-24.04-arm + platforms: 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 - - 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" diff --git a/.github/workflows/build-stable-container.yml b/.github/workflows/build-stable-container.yml index 1a23e68c..1498dc2e 100644 --- a/.github/workflows/build-stable-container.yml +++ b/.github/workflows/build-stable-container.yml @@ -8,17 +8,27 @@ on: jobs: release: - runs-on: ubuntu-latest + strategy: + matrix: + include: + - os: ubuntu-latest + platforms: linux/amd64 + name: "AMD64" + dockerfile: "docker/Dockerfile" + tag_suffix: "" + # GitHub doesn't has a ubuntu-latest-arm runner + - os: ubuntu-24.04-arm + platforms: 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 - - 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" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d6a930a..c8805d10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,26 +100,16 @@ jobs: build-docker-arm64: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: arm64 + - name: Build Docker + run: docker compose -f docker-compose-arm64.yml build --build-arg release=0 - - 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: Run Docker + run: docker compose -f docker-compose-arm64.yml up -d - name: Test Docker run: while curl -Isf http://localhost:3000; do sleep 1; done diff --git a/docker-compose-arm64.yml b/docker-compose-arm64.yml new file mode 100644 index 00000000..ba9e0a3f --- /dev/null +++ b/docker-compose-arm64.yml @@ -0,0 +1,55 @@ +# Warning: This docker-compose file is made for development purposes. +# Using it will build an image from the locally cloned repository. +# +# If you want to use Invidious in production, see the docker-compose.yml file provided +# in the installation documentation: https://docs.invidious.io/installation/ + +version: "3" +services: + + invidious: + build: + context: . + dockerfile: docker/Dockerfile.arm64 + restart: unless-stopped + ports: + - "127.0.0.1:3000:3000" + environment: + # Please read the following file for a comprehensive list of all available + # configuration options and their associated syntax: + # https://github.com/iv-org/invidious/blob/master/config/config.example.yml + INVIDIOUS_CONFIG: | + db: + dbname: invidious + user: kemal + password: kemal + host: invidious-db + port: 5432 + check_tables: true + # external_port: + # domain: + # https_only: false + # 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 + interval: 30s + timeout: 5s + retries: 2 + + invidious-db: + image: docker.io/library/postgres:14 + restart: unless-stopped + volumes: + - postgresdata:/var/lib/postgresql/data + - ./config/sql:/config/sql + - ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh + environment: + POSTGRES_DB: invidious + POSTGRES_USER: kemal + POSTGRES_PASSWORD: kemal + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + +volumes: + postgresdata: From cef0097a309847f6075d7e9173c0362dcc83c757 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 15:28:14 -0400 Subject: [PATCH 05/44] CI: fix typo on matrix platforms --- .github/workflows/build-nightly-container.yml | 4 ++-- .github/workflows/build-stable-container.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-nightly-container.yml b/.github/workflows/build-nightly-container.yml index 3277c015..6b9d4a87 100644 --- a/.github/workflows/build-nightly-container.yml +++ b/.github/workflows/build-nightly-container.yml @@ -21,13 +21,13 @@ jobs: matrix: include: - os: ubuntu-latest - platforms: linux/amd64 + platform: linux/amd64 name: "AMD64" dockerfile: "docker/Dockerfile" tag_suffix: "" # GitHub doesn't has a ubuntu-latest-arm runner - os: ubuntu-24.04-arm - platforms: linux/arm64/v8 + platform: linux/arm64/v8 name: "ARM64" dockerfile: "docker/Dockerfile.arm64" tag_suffix: "-arm64" diff --git a/.github/workflows/build-stable-container.yml b/.github/workflows/build-stable-container.yml index 1498dc2e..07a3520b 100644 --- a/.github/workflows/build-stable-container.yml +++ b/.github/workflows/build-stable-container.yml @@ -12,13 +12,13 @@ jobs: matrix: include: - os: ubuntu-latest - platforms: linux/amd64 + platform: linux/amd64 name: "AMD64" dockerfile: "docker/Dockerfile" tag_suffix: "" # GitHub doesn't has a ubuntu-latest-arm runner - os: ubuntu-24.04-arm - platforms: linux/arm64/v8 + platform: linux/arm64/v8 name: "ARM64" dockerfile: "docker/Dockerfile.arm64" tag_suffix: "-arm64" From 1d2f4b68133231c66e18d878706e8e263e47a66f Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 15:29:24 -0400 Subject: [PATCH 06/44] CI: fix typo on comment about the os used on the ARM64 builder --- .github/workflows/build-nightly-container.yml | 2 +- .github/workflows/build-stable-container.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-nightly-container.yml b/.github/workflows/build-nightly-container.yml index 6b9d4a87..1a5abeea 100644 --- a/.github/workflows/build-nightly-container.yml +++ b/.github/workflows/build-nightly-container.yml @@ -25,7 +25,7 @@ jobs: name: "AMD64" dockerfile: "docker/Dockerfile" tag_suffix: "" - # GitHub doesn't has a ubuntu-latest-arm runner + # GitHub doesn't have a ubuntu-latest-arm runner - os: ubuntu-24.04-arm platform: linux/arm64/v8 name: "ARM64" diff --git a/.github/workflows/build-stable-container.yml b/.github/workflows/build-stable-container.yml index 07a3520b..7c2a276b 100644 --- a/.github/workflows/build-stable-container.yml +++ b/.github/workflows/build-stable-container.yml @@ -16,7 +16,7 @@ jobs: name: "AMD64" dockerfile: "docker/Dockerfile" tag_suffix: "" - # GitHub doesn't has a ubuntu-latest-arm runner + # GitHub doesn't have a ubuntu-latest-arm runner - os: ubuntu-24.04-arm platform: linux/arm64/v8 name: "ARM64" From 94f0a7a9d22e46e58447810fbb0da05508162fad Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 15:31:17 -0400 Subject: [PATCH 07/44] CI: remove --build-arg Dockerfile and Dockerfile.arm64 already build Invidious without release mode if `release` argument is not present. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8805d10..1bb92101 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,7 +90,7 @@ jobs: - uses: actions/checkout@v4 - name: Build Docker - run: docker compose build --build-arg release=0 + run: docker compose build - name: Run Docker run: docker compose up -d @@ -106,7 +106,7 @@ jobs: - uses: actions/checkout@v4 - name: Build Docker - run: docker compose -f docker-compose-arm64.yml build --build-arg release=0 + run: docker compose -f docker-compose-arm64.yml build - name: Run Docker run: docker compose -f docker-compose-arm64.yml up -d From 1d664c759f17b5455d1ffbbe5e276a35dd4202e9 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 16:33:03 -0400 Subject: [PATCH 08/44] CI: Use matrix for `build-docker` on ci.yml --- .github/workflows/ci.yml | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1bb92101..51a5052d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,14 +83,22 @@ jobs: run: crystal build --warnings all --error-on-warnings --error-trace src/invidious.cr build-docker: + strategy: + matrix: + include: + - os: ubuntu-latest + docker_compose_file: "docker-compose.yml" + # GitHub doesn't have a ubuntu-latest-arm runner + - os: ubuntu-24.04-arm + docker_compose_file: "docker-compose-arm64.yml" - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Build Docker - run: docker compose build + run: docker compose -f ${{ matrix.docker_compose_file }} build - name: Run Docker run: docker compose up -d @@ -98,22 +106,6 @@ jobs: - name: Test Docker run: while curl -Isf http://localhost:3000; do sleep 1; done - build-docker-arm64: - - runs-on: ubuntu-24.04-arm - - steps: - - uses: actions/checkout@v4 - - - name: Build Docker - run: docker compose -f docker-compose-arm64.yml build - - - name: Run Docker - run: docker compose -f docker-compose-arm64.yml up -d - - - name: Test Docker - run: while curl -Isf http://localhost:3000; do sleep 1; done - lint: runs-on: ubuntu-latest From a3375e512edf00c5c0c00089d370392c37bbe550 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 17:43:03 -0400 Subject: [PATCH 09/44] CI: Add name attribute to `build-docker` job --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51a5052d..80cb81a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,10 +88,13 @@ jobs: include: - os: ubuntu-latest docker_compose_file: "docker-compose.yml" + name: "AMD64" # GitHub doesn't have a ubuntu-latest-arm runner - os: ubuntu-24.04-arm docker_compose_file: "docker-compose-arm64.yml" + name: "ARM64" + name: Test ${{ matrix.name }} Docker build runs-on: ${{ matrix.os }} steps: From 033a44fab574df56dd63a41d63d089b84cdb31f5 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 17:58:24 -0400 Subject: [PATCH 10/44] CI: Also use `matrix.docker_compose_file` for `Run Docker` step --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80cb81a0..d3b6455a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,7 +104,7 @@ jobs: run: docker compose -f ${{ matrix.docker_compose_file }} build - name: Run Docker - run: docker compose up -d + run: docker compose -f ${{ matrix.docker_compose_file }} up -d - name: Test Docker run: while curl -Isf http://localhost:3000; do sleep 1; done From 381074fce1f3e405d8c527a672f524c4700aead5 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 19:38:21 -0400 Subject: [PATCH 11/44] CI: Replace Dockerfile path depending of the os used --- .github/workflows/ci.yml | 10 +++++--- docker-compose-arm64.yml | 55 ---------------------------------------- 2 files changed, 6 insertions(+), 59 deletions(-) delete mode 100644 docker-compose-arm64.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3b6455a..7a5e8850 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,11 +87,9 @@ jobs: matrix: include: - os: ubuntu-latest - docker_compose_file: "docker-compose.yml" name: "AMD64" # GitHub doesn't have a ubuntu-latest-arm runner - os: ubuntu-24.04-arm - docker_compose_file: "docker-compose-arm64.yml" name: "ARM64" name: Test ${{ matrix.name }} Docker build @@ -100,11 +98,15 @@ jobs: steps: - uses: actions/checkout@v4 + - 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 -f ${{ matrix.docker_compose_file }} build + run: docker compose build - name: Run Docker - run: docker compose -f ${{ matrix.docker_compose_file }} up -d + run: docker compose up -d - name: Test Docker run: while curl -Isf http://localhost:3000; do sleep 1; done diff --git a/docker-compose-arm64.yml b/docker-compose-arm64.yml deleted file mode 100644 index ba9e0a3f..00000000 --- a/docker-compose-arm64.yml +++ /dev/null @@ -1,55 +0,0 @@ -# Warning: This docker-compose file is made for development purposes. -# Using it will build an image from the locally cloned repository. -# -# If you want to use Invidious in production, see the docker-compose.yml file provided -# in the installation documentation: https://docs.invidious.io/installation/ - -version: "3" -services: - - invidious: - build: - context: . - dockerfile: docker/Dockerfile.arm64 - restart: unless-stopped - ports: - - "127.0.0.1:3000:3000" - environment: - # Please read the following file for a comprehensive list of all available - # configuration options and their associated syntax: - # https://github.com/iv-org/invidious/blob/master/config/config.example.yml - INVIDIOUS_CONFIG: | - db: - dbname: invidious - user: kemal - password: kemal - host: invidious-db - port: 5432 - check_tables: true - # external_port: - # domain: - # https_only: false - # 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 - interval: 30s - timeout: 5s - retries: 2 - - invidious-db: - image: docker.io/library/postgres:14 - restart: unless-stopped - volumes: - - postgresdata:/var/lib/postgresql/data - - ./config/sql:/config/sql - - ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh - environment: - POSTGRES_DB: invidious - POSTGRES_USER: kemal - POSTGRES_PASSWORD: kemal - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] - -volumes: - postgresdata: From cc643f209a95cbf9fcc7e97ae3587454b35c3bc5 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 15 May 2025 17:49:54 -0400 Subject: [PATCH 12/44] CI: Fix build-docker job not checking if Invidious starts successfully or not --- .github/workflows/ci.yml | 12 +++++++++++- docker-compose.yml | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a5e8850..27debc1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,11 +105,21 @@ jobs: - name: Build Docker 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 + + - 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: diff --git a/docker-compose.yml b/docker-compose.yml index afda8726..0de51feb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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: From f9472e4e4b910acb9962159e97b37c4d95f8b804 Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Mon, 19 May 2025 22:34:59 -0400 Subject: [PATCH 13/44] revert format --- assets/css/player.css | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/assets/css/player.css b/assets/css/player.css index 60f3ce73..d95549ac 100644 --- a/assets/css/player.css +++ b/assets/css/player.css @@ -71,10 +71,8 @@ padding-top: 2em } -.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control { - height: 5px; - margin-bottom: 10px; -} +.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {height: 5px; +margin-bottom: 10px;} ul.vjs-menu-content::-webkit-scrollbar { display: none; From 6497e1c41888756b0f624df725712bf3b00d49c2 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 22 May 2025 16:06:13 -0400 Subject: [PATCH 14/44] YtAPI: Bump client versions --- src/invidious/yt_backend/youtube_api.cr | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index b40092a1..1f21ddf0 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -6,10 +6,10 @@ module YoutubeAPI extend self # For Android versions, see https://en.wikipedia.org/wiki/Android_version_history - private ANDROID_APP_VERSION = "19.32.34" - private ANDROID_VERSION = "12" + private ANDROID_APP_VERSION = "19.35.36" + private ANDROID_VERSION = "13" private ANDROID_USER_AGENT = "com.google.android.youtube/#{ANDROID_APP_VERSION} (Linux; U; Android #{ANDROID_VERSION}; US) gzip" - private ANDROID_SDK_VERSION = 31_i64 + private ANDROID_SDK_VERSION = 33_i64 private ANDROID_TS_APP_VERSION = "1.9" private ANDROID_TS_USER_AGENT = "com.google.android.youtube/1.9 (Linux; U; Android 12; US) gzip" @@ -49,7 +49,7 @@ module YoutubeAPI ClientType::Web => { name: "WEB", name_proto: "1", - version: "2.20240814.00.00", + version: "2.20250222.10.00", screen: "WATCH_FULL_SCREEN", os_name: "Windows", os_version: WINDOWS_VERSION, @@ -58,7 +58,7 @@ module YoutubeAPI ClientType::WebEmbeddedPlayer => { name: "WEB_EMBEDDED_PLAYER", name_proto: "56", - version: "1.20240812.01.00", + version: "1.20250219.01.00", screen: "EMBED", os_name: "Windows", os_version: WINDOWS_VERSION, @@ -67,7 +67,7 @@ module YoutubeAPI ClientType::WebMobile => { name: "MWEB", name_proto: "2", - version: "2.20240813.02.00", + version: "2.20250224.01.00", os_name: "Android", os_version: ANDROID_VERSION, platform: "MOBILE", @@ -75,7 +75,7 @@ module YoutubeAPI ClientType::WebScreenEmbed => { name: "WEB", name_proto: "1", - version: "2.20240814.00.00", + version: "2.20250222.10.00", screen: "EMBED", os_name: "Windows", os_version: WINDOWS_VERSION, @@ -84,7 +84,7 @@ module YoutubeAPI ClientType::WebCreator => { name: "WEB_CREATOR", name_proto: "62", - version: "1.20240918.03.00", + version: "1.20241203.01.00", os_name: "Windows", os_version: WINDOWS_VERSION, platform: "DESKTOP", @@ -170,7 +170,7 @@ module YoutubeAPI ClientType::TvHtml5 => { name: "TVHTML5", name_proto: "7", - version: "7.20240813.07.00", + version: "7.20250219.14.00", }, ClientType::TvHtml5ScreenEmbed => { name: "TVHTML5_SIMPLY_EMBEDDED_PLAYER", From 97354adf0fc359d2898f69613d1ab668aaf6931f Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 22 May 2025 17:15:45 -0400 Subject: [PATCH 15/44] Update src/invidious/yt_backend/youtube_api.cr Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com> --- src/invidious/yt_backend/youtube_api.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index 1f21ddf0..bedbb978 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -8,7 +8,7 @@ module YoutubeAPI # For Android versions, see https://en.wikipedia.org/wiki/Android_version_history private ANDROID_APP_VERSION = "19.35.36" private ANDROID_VERSION = "13" - private ANDROID_USER_AGENT = "com.google.android.youtube/#{ANDROID_APP_VERSION} (Linux; U; Android #{ANDROID_VERSION}; US) gzip" + private ANDROID_USER_AGENT = "com.google.android.youtube/#{ANDROID_APP_VERSION} (Linux; U; Android #{ANDROID_VERSION}; en_US; SM-S908E Build/TP1A.220624.014) gzip" private ANDROID_SDK_VERSION = 33_i64 private ANDROID_TS_APP_VERSION = "1.9" From 3a8d4f333f1ef5b42a4eb0a2e8b5743b646862cb Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 22 May 2025 17:17:01 -0400 Subject: [PATCH 16/44] update IOS_APP_VERSION --- src/invidious/yt_backend/youtube_api.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index bedbb978..5f89d0e6 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -17,7 +17,7 @@ module YoutubeAPI # For Apple device names, see https://gist.github.com/adamawolf/3048717 # For iOS versions, see https://en.wikipedia.org/wiki/IOS_version_history#Releases, # then go to the dedicated article of the major version you want. - private IOS_APP_VERSION = "19.32.8" + private IOS_APP_VERSION = "20.11.6" private IOS_USER_AGENT = "com.google.ios.youtube/#{IOS_APP_VERSION} (iPhone14,5; U; CPU iOS 17_6 like Mac OS X;)" private IOS_VERSION = "17.6.1.21G93" # Major.Minor.Patch.Build From 09d342b84d4639026b90beb3f95403f6cf93275a Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 22 May 2025 17:55:46 -0400 Subject: [PATCH 17/44] Update src/invidious/yt_backend/youtube_api.cr Co-authored-by: syeopite <70992037+syeopite@users.noreply.github.com> --- src/invidious/yt_backend/youtube_api.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index 5f89d0e6..9f2078c7 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -18,8 +18,8 @@ module YoutubeAPI # For iOS versions, see https://en.wikipedia.org/wiki/IOS_version_history#Releases, # then go to the dedicated article of the major version you want. private IOS_APP_VERSION = "20.11.6" - private IOS_USER_AGENT = "com.google.ios.youtube/#{IOS_APP_VERSION} (iPhone14,5; U; CPU iOS 17_6 like Mac OS X;)" - private IOS_VERSION = "17.6.1.21G93" # Major.Minor.Patch.Build + private IOS_USER_AGENT = "com.google.ios.youtube/#{IOS_APP_VERSION} (iPhone14,5; U; CPU iOS 18_5 like Mac OS X;)" + private IOS_VERSION = "18.5.0.22F76" # Major.Minor.Patch.Build private WINDOWS_VERSION = "10.0" From b859faebf057b7dde7733cfb68b9a3b77d648133 Mon Sep 17 00:00:00 2001 From: syeopite <70992037+syeopite@users.noreply.github.com> Date: Wed, 28 May 2025 15:34:49 +0000 Subject: [PATCH 18/44] Remove `@iv-org/developers` from codeowners (#5314) --- .github/CODEOWNERS | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9ca09368..9f17bb40 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,3 @@ -# Default and lowest precedence. If none of the below matches, @iv-org/developers would be requested for review. -* @iv-org/developers - docker-compose.yml @unixfox docker/ @unixfox kubernetes/ @unixfox From df8839d1f018644afecb15e144f228d811708f8f Mon Sep 17 00:00:00 2001 From: syeopite <70992037+syeopite@users.noreply.github.com> Date: Wed, 28 May 2025 20:18:51 +0000 Subject: [PATCH 19/44] Make base-Invidious video info extraction more resilient (#5312) Try next fallback client if one raises Convert `dig` to `dig?` Optimize companionless stream retrieval --- src/invidious/videos/parser.cr | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 15bd00b6..feb58440 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -82,7 +82,7 @@ def extract_video_info(video_id : String) "reason" => JSON::Any.new(reason), } end - elsif video_id != player_response.dig("videoDetails", "videoId") + elsif video_id != player_response.dig?("videoDetails", "videoId") # YouTube may return a different video player response than expected. # See: https://github.com/TeamNewPipe/NewPipe/issues/8713 # Line to be reverted if one day we solve the video not available issue. @@ -109,21 +109,33 @@ def extract_video_info(video_id : String) params["reason"] = JSON::Any.new(reason) if reason if !CONFIG.invidious_companion.present? - if player_response["streamingData"]? && player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil? + if player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil? LOGGER.warn("Missing URLs for adaptive formats, falling back to other YT clients.") - players_fallback = [YoutubeAPI::ClientType::TvHtml5, YoutubeAPI::ClientType::WebMobile] + players_fallback = {YoutubeAPI::ClientType::TvHtml5, YoutubeAPI::ClientType::WebMobile} + players_fallback.each do |player_fallback| client_config.client_type = player_fallback - player_fallback_response = try_fetch_streaming_data(video_id, client_config) - if player_fallback_response && player_fallback_response["streamingData"]? && - player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url") + + next if !(player_fallback_response = try_fetch_streaming_data(video_id, client_config)) + + if player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url") streaming_data = player_response["streamingData"].as_h streaming_data["adaptiveFormats"] = player_fallback_response["streamingData"]["adaptiveFormats"] player_response["streamingData"] = JSON::Any.new(streaming_data) break end + rescue InfoException + next LOGGER.warn("Failed to fetch streams with #{player_fallback}") end end + + # Seems like video page can still render even without playable streams. + # its better than nothing. + # + # # Were we able to find playable video streams? + # if player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil? + # # No :( + # end end {"captions", "playabilityStatus", "playerConfig", "storyboards"}.each do |f| @@ -154,7 +166,7 @@ def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConf playability_status = response["playabilityStatus"]["status"] LOGGER.debug("try_fetch_streaming_data: [#{id}] Got playabilityStatus == #{playability_status}.") - if id != response.dig("videoDetails", "videoId") + if id != response.dig?("videoDetails", "videoId") # YouTube may return a different video player response than expected. # See: https://github.com/TeamNewPipe/NewPipe/issues/8713 raise InfoException.new( From 4daf1f081828dd9137e58bf7a2cc79872f7afa6f Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 12 Jun 2025 01:24:45 -0400 Subject: [PATCH 20/44] Add `TvSimply` client Data taken from: https://github.com/LuanRT/YouTube.js/commit/8cf658151fc4e4266fadfb7e53dd5db3db693355, https://github.com/LuanRT/YouTube.js/commit/689fb0b90edab6f0e4326a35144541d68f72fe01 and https://github.com/LuanRT/YouTube.js/commit/b15f623dab3acb44eaef33175df2d22d35be2979 --- src/invidious/yt_backend/youtube_api.cr | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index b40092a1..78915aef 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -42,6 +42,7 @@ module YoutubeAPI TvHtml5 TvHtml5ScreenEmbed + TvSimply end # List of hard-coded values used by the different clients @@ -178,6 +179,11 @@ module YoutubeAPI version: "2.0", screen: "EMBED", }, + ClientType::TvSimply => { + name: "TVHTML5_SIMPLY", + name_proto: "74", + version: "1.0", + }, } #################################################################### From 37be513e142061067604d7ec1981fe7a309f8713 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 12 Jun 2025 01:25:59 -0400 Subject: [PATCH 21/44] Add fallback to TvSimply client --- src/invidious/videos/parser.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index feb58440..5be59352 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -111,7 +111,7 @@ def extract_video_info(video_id : String) if !CONFIG.invidious_companion.present? if player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil? LOGGER.warn("Missing URLs for adaptive formats, falling back to other YT clients.") - players_fallback = {YoutubeAPI::ClientType::TvHtml5, YoutubeAPI::ClientType::WebMobile} + players_fallback = {YoutubeAPI::ClientType::TvHtml5, YoutubeAPI::ClientType::TvSimply, YoutubeAPI::ClientType::WebMobile} players_fallback.each do |player_fallback| client_config.client_type = player_fallback From 0c96e0977fd805731d8fdbe97afac1ee22b6626a Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 12 Jun 2025 16:06:04 -0400 Subject: [PATCH 22/44] check for signatureCipher too --- src/invidious/videos/parser.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 5be59352..212b3b35 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -118,7 +118,7 @@ def extract_video_info(video_id : String) next if !(player_fallback_response = try_fetch_streaming_data(video_id, client_config)) - if player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url") + if player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url") || player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "signatureCipher") streaming_data = player_response["streamingData"].as_h streaming_data["adaptiveFormats"] = player_fallback_response["streamingData"]["adaptiveFormats"] player_response["streamingData"] = JSON::Any.new(streaming_data) From b1e7e0c45e8cfe0ca262dc5774c8ccca3fc6db66 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 12 Jun 2025 16:18:01 -0400 Subject: [PATCH 23/44] replace url by signatureCipher if url is not present --- src/invidious/videos/parser.cr | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 212b3b35..e58c0e8f 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -146,6 +146,9 @@ def extract_video_info(video_id : String) if streaming_data = player_response["streamingData"]? %w[formats adaptiveFormats].each do |key| streaming_data.as_h[key]?.try &.as_a.each do |format| + if format.as_h["url"].nil? + format.as_h["url"] = format.as_h["signatureCipher"] + end format.as_h["url"] = JSON::Any.new(convert_url(format)) end end From 01cdb384e0629ade15a83f8a2bcb50722d03340c Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 12 Jun 2025 17:25:19 -0400 Subject: [PATCH 24/44] add suggestions from syeopite --- src/invidious/videos/parser.cr | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index e58c0e8f..6892b37c 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -146,10 +146,11 @@ def extract_video_info(video_id : String) if streaming_data = player_response["streamingData"]? %w[formats adaptiveFormats].each do |key| streaming_data.as_h[key]?.try &.as_a.each do |format| - if format.as_h["url"].nil? - format.as_h["url"] = format.as_h["signatureCipher"] + format = format.as_h + if format["url"]?.nil? + format["url"] = format["signatureCipher"] end - format.as_h["url"] = JSON::Any.new(convert_url(format)) + format["url"] = JSON::Any.new(convert_url(format)) end end From 8cd9d53fb1ff4a8a15d208f587a0a4ce330890bd Mon Sep 17 00:00:00 2001 From: Fijxu Date: Thu, 12 Jun 2025 18:44:01 -0400 Subject: [PATCH 25/44] show message when connection to the database is not possible --- src/invidious.cr | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 69f8a26c..d1f84f39 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -60,7 +60,13 @@ alias IV = Invidious CONFIG = Config.load HMAC_KEY = CONFIG.hmac_key -PG_DB = DB.open CONFIG.database_url +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 ARCHIVE_URL = URI.parse("https://archive.org") PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com") REDDIT_URL = URI.parse("https://www.reddit.com") From cf0a68bd77251528713404822a19c411a1c0aaca Mon Sep 17 00:00:00 2001 From: Fijxu Date: Sun, 15 Jun 2025 16:51:04 -0400 Subject: [PATCH 26/44] store adaptiveFormats data into a variable --- src/invidious/videos/parser.cr | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 6892b37c..178b905b 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -118,9 +118,10 @@ def extract_video_info(video_id : String) next if !(player_fallback_response = try_fetch_streaming_data(video_id, client_config)) - if player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url") || player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "signatureCipher") + adaptive_formats = player_fallback_response.dig?("streamingData", "adaptiveFormats") + if adaptive_formats && (adaptive_formats.dig?(0, "url") || adaptive_formats.dig?(0, "signatureCipher")) streaming_data = player_response["streamingData"].as_h - streaming_data["adaptiveFormats"] = player_fallback_response["streamingData"]["adaptiveFormats"] + streaming_data["adaptiveFormats"] = adaptive_formats player_response["streamingData"] = JSON::Any.new(streaming_data) break end From d51e1cb0514fe2be4b94f7233e36a8aada542496 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Sun, 15 Jun 2025 17:45:53 -0400 Subject: [PATCH 27/44] remove fallback to TV client --- src/invidious/videos/parser.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 178b905b..5335aa79 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -111,7 +111,7 @@ def extract_video_info(video_id : String) if !CONFIG.invidious_companion.present? if player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil? LOGGER.warn("Missing URLs for adaptive formats, falling back to other YT clients.") - players_fallback = {YoutubeAPI::ClientType::TvHtml5, YoutubeAPI::ClientType::TvSimply, YoutubeAPI::ClientType::WebMobile} + players_fallback = {YoutubeAPI::ClientType::TvSimply, YoutubeAPI::ClientType::WebMobile} players_fallback.each do |player_fallback| client_config.client_type = player_fallback From 8723fdca06510a2ab64c194fa284f011fd327e42 Mon Sep 17 00:00:00 2001 From: Fijxu Date: Sat, 21 Jun 2025 12:02:32 -0400 Subject: [PATCH 28/44] Update src/invidious.cr Co-authored-by: Samantaz Fox --- src/invidious.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index d1f84f39..2d244dd2 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -62,8 +62,8 @@ HMAC_KEY = CONFIG.hmac_key PG_DB = begin DB.open CONFIG.database_url -rescue ex - puts "Failed to connect to PostgreSQL database: #{ex.cause.try &.message}" +rescue exc + puts "Failed to connect to PostgreSQL database: #{exc.cause.try &.message}" puts "Check your 'config.yml' database settings or PostgreSQL settings." exit(1) end From f3f6937ffcc703f11173653dc250c7f5bac5d736 Mon Sep 17 00:00:00 2001 From: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Wed, 25 Jun 2025 22:22:30 -0400 Subject: [PATCH 29/44] Fix community tab not loading --- src/invidious/channels/community.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 49ffd990..6a296009 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -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 "community" + initial_data = YoutubeAPI.browse(ucid, params: "EgVwb3N0c_IGBAoCSgA%3D") items = [] of JSON::Any extract_items(initial_data) do |item| From b9171d9dab7e6791376c4cc899ed3b6fa16e5f19 Mon Sep 17 00:00:00 2001 From: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Wed, 25 Jun 2025 22:34:26 -0400 Subject: [PATCH 30/44] Update protobuf for individual community post --- src/invidious/channels/community.cr | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 6a296009..c0688416 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -3,7 +3,7 @@ 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? - # EgVwb3N0c_IGBAoCSgA%3D is the protobuf object to load "community" + # EgVwb3N0c_IGBAoCSgA%3D is the protobuf object to load "posts" initial_data = YoutubeAPI.browse(ucid, params: "EgVwb3N0c_IGBAoCSgA%3D") items = [] of JSON::Any @@ -26,21 +26,18 @@ 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) } .try { |i| Protodec::Any.from_json(i) } .try { |i| Base64.urlsafe_encode(i) } .try { |i| URI.encode_www_form(i) } - initial_data = YoutubeAPI.browse(ucid, params: params) + initial_data = YoutubeAPI.browse("FEpost_detail", params: params) items = [] of JSON::Any extract_items(initial_data) do |item| From 4155f15bf73ede39434e7aa3878e295d5d203c04 Mon Sep 17 00:00:00 2001 From: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:33:28 -0400 Subject: [PATCH 31/44] update resolve_url api to better support new post endpoint --- src/invidious/routes/api/v1/misc.cr | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/invidious/routes/api/v1/misc.cr b/src/invidious/routes/api/v1/misc.cr index 4f5b58da..40f2a439 100644 --- a/src/invidious/routes/api/v1/misc.cr +++ b/src/invidious/routes/api/v1/misc.cr @@ -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 From 436f955e0f20c9e398e3587175f3071dcec153d4 Mon Sep 17 00:00:00 2001 From: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:34:30 -0400 Subject: [PATCH 32/44] update fetch_community_post_comments protobuf to match currently used protobuf, add sort_by option --- src/invidious/channels/community.cr | 9 +++++++++ src/invidious/comments/youtube.cr | 26 ++++++++++++++----------- src/invidious/routes/api/v1/channels.cr | 6 ++++-- src/invidious/routes/channels.cr | 2 +- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index c0688416..8927c81b 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -24,6 +24,15 @@ 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 = { "56:embedded" => { diff --git a/src/invidious/comments/youtube.cr b/src/invidious/comments/youtube.cr index 0716fcde..18403656 100644 --- a/src/invidious/comments/youtube.cr +++ b/src/invidious/comments/youtube.cr @@ -16,34 +16,38 @@ 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") 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, + "15:varint" => 2_i64, + "25:varint" => 0_i64, "29:string" => post_id, "30:string" => ucid, }, + "7:varint" => 0_i64, "8:string" => "comments-section", }, } + case sort_by + when "top" + object["53:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 0_i64 + when "new", "newest" + object["53:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 1_i64 + else # top + object["53:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 0_i64 + end + object_parsed = object.try { |i| Protodec::Any.cast_json(i) } .try { |i| Protodec::Any.from_json(i) } .try { |i| Base64.urlsafe_encode(i) } object2 = { "80226972:embedded" => { - "2:string" => ucid, + "2:string" => "FEcomment_post_detail_page_web_top_level", "3:string" => object_parsed, }, } diff --git a/src/invidious/routes/api/v1/channels.cr b/src/invidious/routes/api/v1/channels.cr index a940ee68..503b8c05 100644 --- a/src/invidious/routes/api/v1/channels.cr +++ b/src/invidious/routes/api/v1/channels.cr @@ -436,7 +436,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 +460,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 diff --git a/src/invidious/routes/channels.cr b/src/invidious/routes/channels.cr index 508aa3e4..6d2b4465 100644 --- a/src/invidious/routes/channels.cr +++ b/src/invidious/routes/channels.cr @@ -284,7 +284,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 From f8febbe2b2fbc618e96cad027619b1acbc8509f4 Mon Sep 17 00:00:00 2001 From: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:53:07 -0400 Subject: [PATCH 33/44] format changes --- src/invidious/channels/community.cr | 12 ++++++------ src/invidious/routes/api/v1/misc.cr | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 8927c81b..43843b11 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -26,9 +26,9 @@ 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) } + .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 @@ -36,10 +36,10 @@ end def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode) object = { "56:embedded" => { - "2:string" => ucid, - "3:string" => post_id.to_s, + "2:string" => ucid, + "3:string" => post_id.to_s, "11:string" => ucid, - } + }, } params = object.try { |i| Protodec::Any.cast_json(i) } .try { |i| Protodec::Any.from_json(i) } diff --git a/src/invidious/routes/api/v1/misc.cr b/src/invidious/routes/api/v1/misc.cr index 40f2a439..4ae877a8 100644 --- a/src/invidious/routes/api/v1/misc.cr +++ b/src/invidious/routes/api/v1/misc.cr @@ -197,8 +197,8 @@ module Invidious::Routes::API::V1::Misc .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) + 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 From b0c9f87fbea9a527b1e96774de97dc366e76df12 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Thu, 26 Jun 2025 19:09:52 +0000 Subject: [PATCH 34/44] Fix missing .id to retrieve first playlist video ID This was missed in the review of PR 5196 --- src/invidious/routes/embed.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/routes/embed.cr b/src/invidious/routes/embed.cr index 930e4915..721a57f8 100644 --- a/src/invidious/routes/embed.cr +++ b/src/invidious/routes/embed.cr @@ -20,7 +20,7 @@ module Invidious::Routes::Embed return error_template(500, ex) end - url = "/embed/#{first_playlist_video}?#{env.params.query}" + url = "/embed/#{first_playlist_video.id}?#{env.params.query}" if env.params.query.size > 0 url += "?#{env.params.query}" From 64ac3b5203a94291bad709ffcaae665e50544485 Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Thu, 26 Jun 2025 18:40:06 -0400 Subject: [PATCH 35/44] add missing noreferrers --- assets/js/player.js | 4 ++-- assets/js/watch.js | 2 +- src/invidious/frontend/watch_page.cr | 2 +- src/invidious/views/user/data_control.ecr | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/js/player.js b/assets/js/player.js index f32c9b56..1a20c932 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -180,7 +180,7 @@ var shareOptions = { }; if (location.pathname.startsWith('/embed/')) { - var overlay_content = '

' + player_data.title + '

'; + var overlay_content = '

' + player_data.title + '

'; player.overlay({ overlays: [ { start: 'loadstart', content: overlay_content, end: 'playing', align: 'top'}, @@ -450,7 +450,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') } }); diff --git a/assets/js/watch.js b/assets/js/watch.js index d869d40d..ee9c29e8 100644 --- a/assets/js/watch.js +++ b/assets/js/watch.js @@ -141,7 +141,7 @@ function get_reddit_comments() { \

\ \ - {redditPermalinkText} \ + {redditPermalinkText} \ \ \
{contentHtml}
\ diff --git a/src/invidious/frontend/watch_page.cr b/src/invidious/frontend/watch_page.cr index 15d925e3..c0926164 100644 --- a/src/invidious/frontend/watch_page.cr +++ b/src/invidious/frontend/watch_page.cr @@ -34,7 +34,7 @@ module Invidious::Frontend::WatchPage str << " class=\"pure-form pure-form-stacked\"" str << " action='#{url}'" str << " method='post'" - str << " rel='noopener'" + str << " rel='noopener noreferrer'" str << " target='_blank'>" str << '\n' diff --git a/src/invidious/views/user/data_control.ecr b/src/invidious/views/user/data_control.ecr index 9ce42c99..e57926f5 100644 --- a/src/invidious/views/user/data_control.ecr +++ b/src/invidious/views/user/data_control.ecr @@ -14,7 +14,7 @@
From 803311713d43860bcdbba81008544ef2d67bc657 Mon Sep 17 00:00:00 2001 From: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:34:45 -0400 Subject: [PATCH 36/44] make `sort_by` code more legible --- src/invidious/comments/youtube.cr | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/invidious/comments/youtube.cr b/src/invidious/comments/youtube.cr index 18403656..e923b2f8 100644 --- a/src/invidious/comments/youtube.cr +++ b/src/invidious/comments/youtube.cr @@ -17,11 +17,20 @@ module Invidious::Comments end 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" => "posts", "53:embedded" => { "4:embedded" => { - "6:varint" => 0_i64, + "6:varint" => sort_by_val, "15:varint" => 2_i64, "25:varint" => 0_i64, "29:string" => post_id, @@ -32,15 +41,6 @@ module Invidious::Comments }, } - case sort_by - when "top" - object["53:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 0_i64 - when "new", "newest" - object["53:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 1_i64 - else # top - object["53:embedded"].as(Hash)["4:embedded"].as(Hash)["6:varint"] = 0_i64 - end - object_parsed = object.try { |i| Protodec::Any.cast_json(i) } .try { |i| Protodec::Any.from_json(i) } .try { |i| Base64.urlsafe_encode(i) } @@ -324,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, @@ -344,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) } From 227c041b86c97a5197c673af7efadbaf649f812d Mon Sep 17 00:00:00 2001 From: Nami Sunami Date: Sat, 28 Jun 2025 11:38:31 +0200 Subject: [PATCH 37/44] fix(config.example.yml): Fix typo (effet -> effect) --- config/config.example.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.example.yml b/config/config.example.yml index 8d3e6212..e8ab658b 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -865,7 +865,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: From 24252b836ceee3bbcfe98a91963439b1bee44dcc Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Mon, 30 Jun 2025 22:38:30 -0400 Subject: [PATCH 38/44] add back semicolon --- assets/js/player.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/player.js b/assets/js/player.js index 1a20c932..7ab3d0e7 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -450,7 +450,7 @@ if (!video_data.params.listen && video_data.params.annotations) { if (target === 'current') { location.href = path; } else if (target === 'new') { - open(path, '_blank', 'noopener,noreferrer') + open(path, '_blank', 'noopener,noreferrer'); } }); From a84bb1d22ed4d59deb50d8ecf72fac1e3a8f3ff4 Mon Sep 17 00:00:00 2001 From: fieryhenry <74794355+fieryhenry@users.noreply.github.com> Date: Fri, 18 Jul 2025 19:02:50 +0000 Subject: [PATCH 39/44] 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 --- assets/js/notifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/notifications.js b/assets/js/notifications.js index 55b7a15c..b8d73a82 100644 --- a/assets/js/notifications.js +++ b/assets/js/notifications.js @@ -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); if (notification_count > 0) { notification_ticker.innerHTML = '' + notification_count + ' '; From 3335bc8c388677517e5c4b86eb917fd3fdace2f8 Mon Sep 17 00:00:00 2001 From: fieryhenry <74794355+fieryhenry@users.noreply.github.com> Date: Fri, 18 Jul 2025 19:07:41 +0000 Subject: [PATCH 40/44] 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 --- assets/js/notifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/notifications.js b/assets/js/notifications.js index b8d73a82..16d9866d 100644 --- a/assets/js/notifications.js +++ b/assets/js/notifications.js @@ -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_NOTIF_COUNT); + const notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0; if (notification_count > 0) { notification_ticker.innerHTML = '' + notification_count + ' '; From 1ae0f45b0e5dca696986925a06ef4f4b4f43894b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 15:06:16 +0200 Subject: [PATCH 41/44] Bump actions/checkout from 4 to 5 (#5415) 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-nightly-container.yml | 2 +- .github/workflows/build-stable-container.yml | 2 +- .github/workflows/ci.yml | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-nightly-container.yml b/.github/workflows/build-nightly-container.yml index 4149bd0b..2f46997a 100644 --- a/.github/workflows/build-nightly-container.yml +++ b/.github/workflows/build-nightly-container.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/.github/workflows/build-stable-container.yml b/.github/workflows/build-stable-container.yml index 1a23e68c..74fbd74b 100644 --- a/.github/workflows/build-stable-container.yml +++ b/.github/workflows/build-stable-container.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d6a930a..7a4a7003 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: stable: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: true @@ -87,7 +87,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Build Docker run: docker compose build --build-arg release=0 @@ -103,7 +103,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -131,7 +131,7 @@ jobs: continue-on-error: true steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: true From 875d8e7e41b58cce007cd3cc5fa8d615815c593a Mon Sep 17 00:00:00 2001 From: Eugene Pakhomov Date: Wed, 13 Aug 2025 13:26:48 +0300 Subject: [PATCH 42/44] Persist caption settings --- assets/js/player.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/player.js b/assets/js/player.js index f32c9b56..cce0b030 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -5,6 +5,7 @@ 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], + persistTextTrackSettings: true, controlBar: { children: [ 'playToggle', From dd8086e6d9d4fe79e5645cdc494a22877624cc86 Mon Sep 17 00:00:00 2001 From: Kristian Vos Date: Wed, 13 Aug 2025 15:43:54 +0200 Subject: [PATCH 43/44] fix: fetching channel playlists returned 500 error --- src/invidious/channels/playlists.cr | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/invidious/channels/playlists.cr b/src/invidious/channels/playlists.cr index 9b45d0c8..cba1abd9 100644 --- a/src/invidious/channels/playlists.cr +++ b/src/invidious/channels/playlists.cr @@ -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 || "") From 67f93e55d8e9be4c81a58920c4651d2eb1327fd6 Mon Sep 17 00:00:00 2001 From: syeopite Date: Sat, 23 Aug 2025 03:35:59 -0700 Subject: [PATCH 44/44] 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. --- src/invidious.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 2d244dd2..197b150c 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -62,8 +62,8 @@ HMAC_KEY = CONFIG.hmac_key PG_DB = begin DB.open CONFIG.database_url -rescue exc - puts "Failed to connect to PostgreSQL database: #{exc.cause.try &.message}" +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 @@ -227,8 +227,8 @@ error 404 do |env| Invidious::Routes::ErrorRoutes.error_404(env) end -error 500 do |env, ex| - error_template(500, ex) +error 500 do |env, exception| + error_template(500, exception) end static_headers do |env|