diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index de14a7ba2f82d..67b7fa02478e4 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -82,7 +82,6 @@ body:
label: Installation Method
description: When you select an unofficial installation method, you must have verified that the bug is also present in one of the official installation methods. Please make sure you uninstall the unofficial installation before installing one of the official installations. If you can't reproduce this in one of the official installation methods, you should report the bug to the maintainer of the unofficial installation method you used.
options:
- - .apk (Alpine Linux Package)
- .AppImage
- .deb
- .dmg
@@ -93,6 +92,7 @@ body:
- .rpm
- .zip / .7z
- .apk (FreeTubeAndroid Unofficial)
+ - .apk (Alpine Linux Package Unofficial)
- AUR (Unofficial)
- Chocolatey (Unofficial)
- Homebrew (Unofficial)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d22e3d7a77d16..584bdc723b0a0 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -213,27 +213,6 @@ jobs:
name: freetube-${{ steps.versionNumber.outputs.result }}.arm64.rpm
path: build/freetube-${{ steps.versionNumber.outputs.result }}.aarch64.rpm
- - name: Upload Alpine .apk x64 Artifact
- uses: actions/upload-artifact@v4
- if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.runtime, 'linux-x64')
- with:
- name: freetube-${{ steps.versionNumber.outputs.result }}-alpine-amd64.apk
- path: build/freetube-${{ steps.versionNumber.outputs.result }}.apk
-
- - name: Upload Alpine .apk ARMv7l Artifact
- uses: actions/upload-artifact@v4
- if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.runtime, 'linux-armv7l')
- with:
- name: freetube-${{ steps.versionNumber.outputs.result }}-alpine-armv7l.apk
- path: build/freetube-${{ steps.versionNumber.outputs.result }}-armv7l.apk
-
- - name: Upload Alpine .apk ARM64 Artifact
- uses: actions/upload-artifact@v4
- if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.runtime, 'linux-arm64')
- with:
- name: freetube-${{ steps.versionNumber.outputs.result }}-alpine-arm64.apk
- path: build/freetube-${{ steps.versionNumber.outputs.result }}-arm64.apk
-
- name: Upload Pacman .pacman x64 Artifact
uses: actions/upload-artifact@v4
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.runtime, 'linux-x64')
diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml
index 3696960a64f50..2b9c10ac9861f 100644
--- a/.github/workflows/flatpak.yml
+++ b/.github/workflows/flatpak.yml
@@ -19,17 +19,16 @@ jobs:
with:
repository: flathub/io.freetubeapp.FreeTube
token: ${{ secrets.FLATHUB_TOKEN }}
- - name: GitHub API exec action
+ - name: Get Repo Release List
uses: moustacheful/github-api-exec-action@v0
- id: api_results
+ id: list_results
with:
# Command to execute, (e.g: `pulls.create`), see https://octokit.github.io/rest.js/ for available commands
- command: repos.getRelease
+ command: repos.listReleases
payload: >
{
"owner": "FreeTubeApp",
- "repo": "FreeTube",
- "release_id": ${{ secrets.UPLOAD_ID }}
+ "repo": "FreeTube"
}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -39,7 +38,7 @@ jobs:
uses: bluwy/substitute-string-action@v3
id: sub
with:
- _input-text: ${{ fromJson(steps.api_results.outputs.result).tag_name }}
+ _input-text: ${{ fromJson(steps.list_results.outputs.result)[0].tag_name }}
-beta: ''
v: ''
- name: Create Release Branch
@@ -51,24 +50,24 @@ jobs:
with:
version: v${{ steps.sub.outputs.result }}-beta
repository: FreeTubeApp/FreeTube
- file: freetube-${{ steps.sub.outputs.result }}-linux-x64-portable.zip
+ file: freetube-${{ steps.sub.outputs.result }}-beta-linux-x64-portable.zip
- name: Download ARM Release
uses: fabriciobastian/download-release-asset-action@v1.0.6
with:
version: v${{ steps.sub.outputs.result }}-beta
repository: FreeTubeApp/FreeTube
- file: freetube-${{ steps.sub.outputs.result }}-linux-arm64-portable.zip
+ file: freetube-${{ steps.sub.outputs.result }}-beta-linux-arm64-portable.zip
- name: Set x64 Hash Variable
id: hash-x64
run: |
echo 'HASH_X64<> $GITHUB_ENV
- sha256sum freetube-${{ steps.sub.outputs.result }}-linux-x64-portable.zip | awk '{print $1}' >> $GITHUB_ENV
+ sha256sum freetube-${{ steps.sub.outputs.result }}-beta-linux-x64-portable.zip | awk '{print $1}' >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV
- name: Set ARM Hash Variable
id: hash-arm64
run: |
echo 'HASH_ARM64<> $GITHUB_ENV
- sha256sum freetube-${{ steps.sub.outputs.result }}-linux-arm64-portable.zip | awk '{print $1}' >> $GITHUB_ENV
+ sha256sum freetube-${{ steps.sub.outputs.result }}-beta-linux-arm64-portable.zip | awk '{print $1}' >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV
- name: Set Date Variable
id: current-date
@@ -80,7 +79,7 @@ jobs:
uses: mikefarah/yq@v4.45.1
with:
# The Command which should be run
- cmd: yq -i '.modules[0].sources[0].url = "https://github.com/FreeTubeApp/FreeTube/releases/download/v${{ steps.sub.outputs.result }}-beta/freetube-${{ steps.sub.outputs.result }}-linux-x64-portable.zip"' io.freetubeapp.FreeTube.yml
+ cmd: yq -i '.modules[0].sources[0].url = "https://github.com/FreeTubeApp/FreeTube/releases/download/v${{ steps.sub.outputs.result }}-beta/freetube-${{ steps.sub.outputs.result }}-beta-linux-x64-portable.zip"' io.freetubeapp.FreeTube.yml
- name: Update x64 Hash in yml File
uses: mikefarah/yq@v4.45.1
with:
@@ -90,25 +89,24 @@ jobs:
uses: mikefarah/yq@v4.45.1
with:
# The Command which should be run
- cmd: yq -i '.modules[0].sources[1].url = "https://github.com/FreeTubeApp/FreeTube/releases/download/v${{ steps.sub.outputs.result }}-beta/freetube-${{ steps.sub.outputs.result }}-linux-arm64-portable.zip"' io.freetubeapp.FreeTube.yml
+ cmd: yq -i '.modules[0].sources[1].url = "https://github.com/FreeTubeApp/FreeTube/releases/download/v${{ steps.sub.outputs.result }}-beta/freetube-${{ steps.sub.outputs.result }}-beta-linux-arm64-portable.zip"' io.freetubeapp.FreeTube.yml
- name: Update ARM Hash in yml File
uses: mikefarah/yq@v4.45.1
with:
# The Command which should be run
cmd: yq -i '.modules[0].sources[1].sha256 = "${{ env.HASH_ARM64 }}"' io.freetubeapp.FreeTube.yml
- name: Add Patch Notes to XML File
- run: xmlstarlet ed -L -i /application/releases/release[1] -t elem -n releaseTMP -v "" -i //releaseTMP -t attr -n version -v "${{ steps.sub.outputs.result }} Beta" -i //releaseTMP -t attr -n date -v "${{ env.CURRENT_DATE }}" -s //releaseTMP -t elem -n url -v "" -s //releaseTMP/url -t text -n "" -v "https://github.com/FreeTubeApp/FreeTube/releases/tag/v${{ steps.sub.outputs.result }}-beta" -r //releaseTMP -v "release" io.freetubeapp.FreeTube.metainfo.xml
+ run: xmlstarlet ed -L -i /component/releases/release[1] -t elem -n releaseTMP -v "" -i //releaseTMP -t attr -n version -v "${{ steps.sub.outputs.result }} Beta" -i //releaseTMP -t attr -n date -v "${{ env.CURRENT_DATE }}" -s //releaseTMP -t elem -n url -v "" -s //releaseTMP/url -t text -n "" -v "https://github.com/FreeTubeApp/FreeTube/releases/tag/v${{ steps.sub.outputs.result }}-beta" -r //releaseTMP -v "release" io.freetubeapp.FreeTube.metainfo.xml
- name: Remove Release Files
run: |
- rm freetube-${{ steps.sub.outputs.result }}-linux-x64-portable.zip
- rm freetube-${{ steps.sub.outputs.result }}-linux-arm64-portable.zip
+ rm freetube-${{ steps.sub.outputs.result }}-beta-linux-x64-portable.zip
+ rm freetube-${{ steps.sub.outputs.result }}-beta-linux-arm64-portable.zip
- name: Commit Files
uses: stefanzweifel/git-auto-commit-action@v5
with:
# Optional but recommended
# Defaults to "Apply automatic changes"
commit_message: Update files for v${{ steps.sub.outputs.result }}
- token: ${{ secrets.FLATHUB_TOKEN }}
# Optional options appended to `git-commit`
# See https://git-scm.com/docs/git-commit for a list of available options
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 70075ee77e745..40a3f89549672 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -6,6 +6,11 @@ name: Upload Release
# or API.
on:
workflow_dispatch:
+ inputs:
+ releaseId:
+ type: string
+ required: true
+ description: Release ID
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
@@ -94,8 +99,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-linux-x64-portable.zip
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-linux-x64-portable.zip
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}.zip
asset_content_type: application/zip
@@ -105,8 +110,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-linux-x64-portable.7z
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-linux-x64-portable.7z
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}.7z
asset_content_type: application/x-7z-compressed
@@ -116,8 +121,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-linux-armv7l-portable.zip
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-linux-armv7l-portable.zip
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-armv7l.zip
asset_content_type: application/zip
@@ -127,8 +132,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-linux-armv7l-portable.7z
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-linux-armv7l-portable.7z
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-armv7l.7z
asset_content_type: application/x-7z-compressed
@@ -138,8 +143,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-linux-arm64-portable.zip
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-linux-arm64-portable.zip
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-arm64.zip
asset_content_type: application/zip
@@ -149,8 +154,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-linux-arm64-portable.7z
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-linux-arm64-portable.7z
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-arm64.7z
asset_content_type: application/x-7z-compressed
@@ -160,8 +165,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube_${{ steps.getPackageInfo.outputs.version }}_amd64.deb
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube_${{ steps.getPackageInfo.outputs.version }}_beta_amd64.deb
asset_path: build/freetube_${{ steps.getPackageInfo.outputs.version }}_amd64.deb
asset_content_type: application/vnd.debian.binary-package
@@ -171,8 +176,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube_${{ steps.getPackageInfo.outputs.version }}_armv7l.deb
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube_${{ steps.getPackageInfo.outputs.version }}_beta_armv7l.deb
asset_path: build/freetube_${{ steps.getPackageInfo.outputs.version }}_armv7l.deb
asset_content_type: application/vnd.debian.binary-package
@@ -182,8 +187,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube_${{ steps.getPackageInfo.outputs.version }}_arm64.deb
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube_${{ steps.getPackageInfo.outputs.version }}_beta_arm64.deb
asset_path: build/freetube_${{ steps.getPackageInfo.outputs.version }}_arm64.deb
asset_content_type: application/vnd.debian.binary-package
@@ -193,8 +198,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-amd64.AppImage
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-amd64.AppImage
asset_path: build/FreeTube-${{ steps.getPackageInfo.outputs.version }}.AppImage
asset_content_type: application/vnd.appimage
@@ -204,8 +209,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-armv7l.AppImage
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-armv7l.AppImage
asset_path: build/FreeTube-${{ steps.getPackageInfo.outputs.version }}-armv7l.AppImage
asset_content_type: application/vnd.appimage
@@ -215,8 +220,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-arm64.AppImage
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-arm64.AppImage
asset_path: build/FreeTube-${{ steps.getPackageInfo.outputs.version }}-arm64.AppImage
asset_content_type: application/vnd.appimage
@@ -226,8 +231,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}.amd64.rpm
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta.amd64.rpm
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}.x86_64.rpm
asset_content_type: application/x-rpm
@@ -239,52 +244,19 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}.arm64.rpm
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta.arm64.rpm
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}.aarch64.rpm
asset_content_type: application/x-rpm
- - name: Upload Alpine .apk x64 Release
- uses: actions/upload-release-asset@v1
- if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.runtime, 'linux-x64')
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-alpine-amd64.apk
- asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}.apk
- asset_content_type: application/octet-stream
-
- - name: Upload Alpine .apk ARMv7l Release
- uses: actions/upload-release-asset@v1
- if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.runtime, 'linux-armv7l')
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-alpine-armv7l.apk
- asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-armv7l.apk
- asset_content_type: application/octet-stream
-
- - name: Upload Alpine .apk ARM64 Release
- uses: actions/upload-release-asset@v1
- if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.runtime, 'linux-arm64')
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-alpine-arm64.apk
- asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-arm64.apk
- asset_content_type: application/octet-stream
-
- name: Upload Pacman .pacman x64 Release
uses: actions/upload-release-asset@v1
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.runtime, 'linux-x64')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-amd64.pacman
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-amd64.pacman
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}.pacman
asset_content_type: application/x-zstd-compressed-tar
@@ -294,8 +266,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-setup-x64.exe
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-setup-x64.exe
asset_path: build/freetube Setup ${{ steps.getPackageInfo.outputs.version }}.exe
asset_content_type: application/x-ms-dos-executable
@@ -305,8 +277,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-win-x64-portable.exe
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-win-x64-portable.exe
asset_path: build/FreeTube ${{ steps.getPackageInfo.outputs.version }}.exe
asset_content_type: application/x-ms-dos-executable
@@ -316,8 +288,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-win-x64-portable.zip
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-win-x64-portable.zip
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-win.zip
asset_content_type: application/zip
@@ -327,8 +299,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-win-x64-portable.7z
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-win-x64-portable.7z
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-win.7z
asset_content_type: application/x-7z-compressed
@@ -338,8 +310,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-setup-arm64.exe
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-setup-arm64.exe
asset_path: build/freetube Setup ${{ steps.getPackageInfo.outputs.version }}.exe
asset_content_type: application/x-ms-dos-executable
@@ -349,8 +321,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-win-arm64-portable.exe
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-win-arm64-portable.exe
asset_path: build/FreeTube ${{ steps.getPackageInfo.outputs.version }}.exe
asset_content_type: application/x-ms-dos-executable
@@ -360,8 +332,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-win-arm64-portable.zip
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-win-arm64-portable.zip
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-arm64-win.zip
asset_content_type: application/zip
@@ -371,8 +343,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-win-arm64-portable.7z
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-win-arm64-portable.7z
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-arm64-win.7z
asset_content_type: application/x-7z-compressed
@@ -382,8 +354,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-mac-x64.dmg
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-mac-x64.dmg
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}.dmg
asset_content_type: application/x-apple-diskimage
@@ -393,8 +365,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-mac-x64.zip
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-mac-x64.zip
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-mac.zip
asset_content_type: application/zip
@@ -404,8 +376,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-mac-x64.7z
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-mac-x64.7z
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-mac.7z
asset_content_type: application/x-7z-compressed
@@ -415,8 +387,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-mac-arm64.dmg
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-mac-arm64.dmg
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-arm64.dmg
asset_content_type: application/x-apple-diskimage
@@ -426,8 +398,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-mac-arm64.zip
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-mac-arm64.zip
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-arm64-mac.zip
asset_content_type: application/x-apple-diskimage
@@ -437,7 +409,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ secrets.UPLOAD_ID }}/assets{?name,label}
- asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-mac-arm64.7z
+ upload_url: https://uploads.github.com/repos/FreeTubeApp/FreeTube/releases/${{ inputs.releaseId }}/assets{?name,label}
+ asset_name: freetube-${{ steps.getPackageInfo.outputs.version }}-beta-mac-arm64.7z
asset_path: build/freetube-${{ steps.getPackageInfo.outputs.version }}-arm64-mac.7z
asset_content_type: application/x-7z-compressed
diff --git a/.github/workflows/updateSite.yml b/.github/workflows/updateSite.yml
new file mode 100644
index 0000000000000..f38a2e58aff59
--- /dev/null
+++ b/.github/workflows/updateSite.yml
@@ -0,0 +1,68 @@
+# This is a basic workflow that is manually triggered
+
+name: Update Site Version Number
+
+# Controls when the action will run. Workflow runs when manually triggered using the UI
+# or API.
+on:
+ workflow_dispatch:
+ release:
+ types: [published]
+
+# A workflow run is made up of one or more jobs that can run sequentially or in parallel
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ repository: FreeTubeApp/FreeTubeApp.io
+ token: ${{ secrets.FLATHUB_TOKEN }}
+ - name: Get Repo Release List
+ uses: moustacheful/github-api-exec-action@v0
+ id: list_results
+ with:
+ # Command to execute, (e.g: `pulls.create`), see https://octokit.github.io/rest.js/ for available commands
+ command: repos.listReleases
+ payload: >
+ {
+ "owner": "FreeTubeApp",
+ "repo": "FreeTube"
+ }
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Create Current Version Variable
+ uses: bluwy/substitute-string-action@v3
+ id: current
+ with:
+ _input-text: ${{ fromJson(steps.list_results.outputs.result)[0].tag_name }}
+ -beta: ''
+ v: ''
+ - name: Create Previous Version Variable
+ uses: bluwy/substitute-string-action@v3
+ id: previous
+ with:
+ _input-text: ${{ fromJson(steps.list_results.outputs.result)[1].tag_name }}
+ -beta: ''
+ v: ''
+ - name: Set Master Branch
+ # Currently the default branch is master, but if that changes later, then this acts as a failsafe.
+ run: |
+ git checkout master
+ - name: Update index.php
+ run: |
+ sed -i 's/${{ steps.previous.outputs.result }}/${{ steps.current.outputs.result }}/g' src/index.php
+ - name: Commit Files
+ uses: stefanzweifel/git-auto-commit-action@v6
+ with:
+ # Optional but recommended
+ # Defaults to "Apply automatic changes"
+ commit_message: Update version number to v${{ steps.current.outputs.result }}
+
+ # Optional options appended to `git-commit`
+ # See https://git-scm.com/docs/git-commit for a list of available options
+ commit_options: '--no-verify --signoff'
+
+ # Optional: Disable dirty check and always try to create a commit and push
+ skip_dirty_check: true
diff --git a/_scripts/build.js b/_scripts/build.js
index de43f6f7e681b..ee1b7fa1c87e3 100644
--- a/_scripts/build.js
+++ b/_scripts/build.js
@@ -36,7 +36,7 @@ if (platform === 'darwin') {
arch = Arch.armv7l
}
- targets = Platform.LINUX.createTarget(['deb', 'zip', '7z', 'apk', 'rpm', 'AppImage', 'pacman'], arch)
+ targets = Platform.LINUX.createTarget(['deb', 'zip', '7z', 'rpm', 'AppImage', 'pacman'], arch)
}
builder
diff --git a/_scripts/ebuilder.config.js b/_scripts/ebuilder.config.js
index 6929ef6ff2ded..14d0d9df1f262 100644
--- a/_scripts/ebuilder.config.js
+++ b/_scripts/ebuilder.config.js
@@ -46,7 +46,7 @@ const config = {
linux: {
category: 'Network',
icon: '_icons/icon.svg',
- target: ['deb', 'zip', '7z', 'apk', 'rpm', 'AppImage', 'pacman'],
+ target: ['deb', 'zip', '7z', 'rpm', 'AppImage', 'pacman'],
},
// See the following issues for more information
// https://github.com/jordansissel/fpm/issues/1503
diff --git a/_scripts/sigFrameConfig.js b/_scripts/sigFrameConfig.js
new file mode 100644
index 0000000000000..c244fb2a1f9a6
--- /dev/null
+++ b/_scripts/sigFrameConfig.js
@@ -0,0 +1,13 @@
+const { hash } = require('crypto')
+const { join } = require('path')
+const { readFileSync } = require('fs')
+
+const path = join(__dirname, '../src/renderer/sigFrameScript.js')
+const rawScript = readFileSync(path, 'utf8')
+
+const script = rawScript.split(/\r?\n/).map(line => line.trim()).filter(line => !line.startsWith('//')).join('')
+
+module.exports.sigFrameTemplateParameters = {
+ sigFrameSrc: `data:text/html,${encodeURIComponent(``)}`,
+ sigFrameCspHash: `sha512-${hash('sha512', script, 'base64')}`
+}
diff --git a/_scripts/webpack.renderer.config.js b/_scripts/webpack.renderer.config.js
index 11f1cf828711c..f3e8c8e59870f 100644
--- a/_scripts/webpack.renderer.config.js
+++ b/_scripts/webpack.renderer.config.js
@@ -12,6 +12,7 @@ const {
SHAKA_LOCALES_PREBUNDLED,
SHAKA_LOCALES_TO_BE_BUNDLED
} = require('./getShakaLocales')
+const { sigFrameTemplateParameters } = require('./sigFrameConfig')
const isDevMode = process.env.NODE_ENV === 'development'
@@ -132,9 +133,9 @@ const config = {
'process.env.SHAKA_LOCALES_PREBUNDLED': JSON.stringify(SHAKA_LOCALES_PREBUNDLED)
}),
new HtmlWebpackPlugin({
- excludeChunks: ['processTaskWorker'],
filename: 'index.html',
- template: path.resolve(__dirname, '../src/index.ejs')
+ template: path.resolve(__dirname, '../src/index.ejs'),
+ templateParameters: sigFrameTemplateParameters
}),
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
diff --git a/package.json b/package.json
index cd11646dbbfb4..5473fc8244332 100644
--- a/package.json
+++ b/package.json
@@ -2,12 +2,12 @@
"name": "freetube",
"productName": "FreeTube",
"description": "A private YouTube client",
- "version": "0.23.1",
+ "version": "0.23.13",
"license": "AGPL-3.0-or-later",
"main": "./dist/main.js",
"private": true,
"author": {
- "name": "PrestonN",
+ "name": "FreeTube Team",
"email": "FreeTubeApp@protonmail.com",
"url": "https://github.com/FreeTubeApp/FreeTube"
},
@@ -61,20 +61,20 @@
"@fortawesome/vue-fontawesome": "^2.0.10",
"@seald-io/nedb": "^4.0.4",
"autolinker": "^4.1.0",
- "bgutils-js": "^3.1.3",
+ "bgutils-js": "^3.2.0",
"electron-context-menu": "^4.0.4",
"marked": "^15.0.6",
"path-browserify": "^1.0.1",
"portal-vue": "^2.1.7",
"process": "^0.11.10",
- "shaka-player": "^4.13.0",
+ "shaka-player": "^4.13.4",
"swiper": "^11.2.1",
"vue": "^2.7.16",
"vue-i18n": "^8.28.2",
"vue-observe-visibility": "^1.0.0",
"vue-router": "^3.6.5",
"vuex": "^3.6.2",
- "youtubei.js": "^13.0.0"
+ "youtubei.js": "^16.0.0"
},
"devDependencies": {
"@babel/core": "^7.26.7",
@@ -88,7 +88,7 @@
"css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0",
"electron": "^34.0.1",
- "electron-builder": "^25.1.8",
+ "electron-builder": "^26.0.13",
"eslint": "^9.19.0",
"eslint-plugin-jsdoc": "^50.6.3",
"eslint-plugin-jsonc": "^2.19.1",
diff --git a/src/botGuardScript.js b/src/botGuardScript.js
index 11da8855a6472..c648703ac87c6 100644
--- a/src/botGuardScript.js
+++ b/src/botGuardScript.js
@@ -1,37 +1,86 @@
-import { BG } from 'bgutils-js'
+import { BG, buildURL, GOOG_API_KEY } from 'bgutils-js'
// This script has it's own webpack config, as it gets passed as a string to Electron's evaluateJavaScript function
// in src/main/poTokenGenerator.js
-export default async function(visitorData) {
+
+/**
+ * Based on: https://github.com/LuanRT/BgUtils/blob/main/examples/node/innertube-challenge-fetcher-example.ts
+ * @param {string} videoId
+ * @param {import('youtubei.js').Session['context']} context
+ */
+export default async function (videoId, context) {
const requestKey = 'O43z0dpjhgX20SCx4KAo'
- const bgConfig = {
- fetch: (input, init) => fetch(input, init),
- requestKey,
- globalObj: window,
- identifier: visitorData
+ const challengeResponse = await fetch(
+ 'https://www.youtube.com/youtubei/v1/att/get?prettyPrint=false&alt=json',
+ {
+ method: 'POST',
+ headers: {
+ Accept: '*/*',
+ 'Content-Type': 'application/json',
+ 'X-Goog-Visitor-Id': context.client.visitorData,
+ 'X-Youtube-Client-Version': context.client.clientVersion,
+ 'X-Youtube-Client-Name': '1'
+ },
+ body: JSON.stringify({
+ engagementType: 'ENGAGEMENT_TYPE_UNBOUND',
+ context
+ }),
+ }
+ )
+
+ if (!challengeResponse.ok) {
+ throw new Error(`Request to ${challengeResponse.url} failed with status ${challengeResponse.status}\n${await challengeResponse.text()}`)
+ }
+
+ const challengeData = await challengeResponse.json()
+
+ if (!challengeData.bgChallenge) {
+ throw new Error('Failed to get BotGuard challenge')
}
- const challenge = await BG.Challenge.create(bgConfig)
+ let interpreterUrl = challengeData.bgChallenge.interpreterUrl.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue
- if (!challenge) {
- throw new Error('Could not get challenge')
+ if (interpreterUrl.startsWith('//')) {
+ interpreterUrl = `https:${interpreterUrl}`
}
- const interpreterJavascript = challenge.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue
+ const bgScriptResponse = await fetch(interpreterUrl)
+ const interpreterJavascript = await bgScriptResponse.text()
if (interpreterJavascript) {
// eslint-disable-next-line no-new-func
new Function(interpreterJavascript)()
} else {
- console.warn('Unable to load VM.')
+ throw new Error('Could not load VM.')
}
- const poTokenResult = await BG.PoToken.generate({
- program: challenge.program,
- globalName: challenge.globalName,
- bgConfig
+ const botGuard = await BG.BotGuardClient.create({
+ program: challengeData.bgChallenge.program,
+ globalName: challengeData.bgChallenge.globalName,
+ globalObj: window
+ })
+
+ const webPoSignalOutput = []
+ const botGuardResponse = await botGuard.snapshot({ webPoSignalOutput }, 10_000)
+
+ const integrityTokenResponse = await fetch(buildURL('GenerateIT', true), {
+ method: 'POST',
+ headers: {
+ 'content-type': 'application/json+protobuf',
+ 'x-goog-api-key': GOOG_API_KEY,
+ 'x-user-agent': 'grpc-web-javascript/0.1',
+ },
+ body: JSON.stringify([requestKey, botGuardResponse])
})
- return poTokenResult.poToken
+ const response = await integrityTokenResponse.json()
+
+ if (typeof response[0] !== 'string') {
+ throw new Error('Could not get integrity token')
+ }
+
+ const integrityTokenBasedMinter = await BG.WebPoMinter.create({ integrityToken: response[0] }, webPoSignalOutput)
+
+ return await integrityTokenBasedMinter.mintAsWebsafeString(videoId)
}
diff --git a/src/index.ejs b/src/index.ejs
index 40f7075a171a6..ecdc7f73fe4b9 100644
--- a/src/index.ejs
+++ b/src/index.ejs
@@ -13,7 +13,18 @@
- <% if (!process.env.IS_ELECTRON) { %>
+ <% if (process.env.IS_ELECTRON) { %>
+
+ <% } else { %>
+
+
diff --git a/src/renderer/components/SideNav/SideNav.css b/src/renderer/components/SideNav/SideNav.css
index 909d641151584..f41e5a1263461 100644
--- a/src/renderer/components/SideNav/SideNav.css
+++ b/src/renderer/components/SideNav/SideNav.css
@@ -249,3 +249,17 @@
inline-size: 100%;
}
}
+
+@media only screen and (width <= 400px) {
+ /* Hide settings so that they appear in the more dropdown on smaller screens */
+ .smallMobileOnlyHidden {
+ display: none;
+ }
+}
+
+@media only screen and (width > 400px) {
+ .smallMobileOnlyHidden {
+ display: block;
+ }
+}
+
diff --git a/src/renderer/components/SideNav/SideNav.vue b/src/renderer/components/SideNav/SideNav.vue
index 8d3fc99a7c31f..e0542625131eb 100644
--- a/src/renderer/components/SideNav/SideNav.vue
+++ b/src/renderer/components/SideNav/SideNav.vue
@@ -152,7 +152,7 @@
400px) {
+ .smallMobileOnlyShow {
+ display: none;
+ }
+}
diff --git a/src/renderer/components/SideNavMoreOptions/SideNavMoreOptions.vue b/src/renderer/components/SideNavMoreOptions/SideNavMoreOptions.vue
index 6fd3b27f043b5..bfc41c885e4c5 100644
--- a/src/renderer/components/SideNavMoreOptions/SideNavMoreOptions.vue
+++ b/src/renderer/components/SideNavMoreOptions/SideNavMoreOptions.vue
@@ -110,6 +110,24 @@
{{ $t("About.About") }}
+
+
+
+ {{ $t("Settings.Settings") }}
+
+
variant.label === label)
} else if (hasMultipleAudioTracks.value) {
// default audio track
- variants = variants.filter(variant => variant.audioRoles.includes('main'))
+ const filteredVariants = variants.filter(variant => variant.audioRoles.includes('main'))
+ // Sometimes there is nothing marked as main, don't filter in this case
+ if (filteredVariants.length > 0) {
+ variants = filteredVariants
+ }
}
const isPortrait = variants[0].height > variants[0].width
@@ -2257,7 +2261,11 @@ export default defineComponent({
// text related errors aren't serious (captions and seek bar thumbnails), so we should just log them
// TODO: consider only emitting when the severity is crititcal?
- if (!ignoreErrors && error.category !== shaka.util.Error.Category.TEXT) {
+ if (
+ !ignoreErrors &&
+ error.category !== shaka.util.Error.Category.TEXT &&
+ !(error.code === shaka.util.Error.Code.BAD_HTTP_STATUS && error.data[0].startsWith('https://www.youtube.com/api/timedtext'))
+ ) {
// don't react to multiple consecutive errors, otherwise we don't give the format fallback from the previous error a chance to work
ignoreErrors = true
diff --git a/src/renderer/components/ft-toast/ft-toast-events.js b/src/renderer/components/ft-toast/ft-toast-events.js
deleted file mode 100644
index 95b4ffbb1fe98..0000000000000
--- a/src/renderer/components/ft-toast/ft-toast-events.js
+++ /dev/null
@@ -1,2 +0,0 @@
-const events = new EventTarget()
-export default events
diff --git a/src/renderer/components/ft-toast/ft-toast.js b/src/renderer/components/ft-toast/ft-toast.js
deleted file mode 100644
index 15697ca25061d..0000000000000
--- a/src/renderer/components/ft-toast/ft-toast.js
+++ /dev/null
@@ -1,52 +0,0 @@
-import { defineComponent, nextTick } from 'vue'
-import FtToastEvents from './ft-toast-events.js'
-
-let id = 0
-
-export default defineComponent({
- name: 'FtToast',
- data: function () {
- return {
- toasts: []
- }
- },
- mounted: function () {
- FtToastEvents.addEventListener('toast-open', this.open)
- },
- beforeDestroy: function () {
- FtToastEvents.removeEventListener('toast-open', this.open)
- },
- methods: {
- performAction: function (id) {
- const index = this.toasts.findIndex(toast => id === toast.id)
-
- this.toasts[index].action()
- this.remove(index)
- },
- close: function (toast) {
- // Wait for fade-out to finish
- setTimeout(this.remove, 300, 0)
-
- toast.isOpen = false
- },
- open: function ({ detail: { message, time, action } }) {
- const toast = {
- message: message,
- action: action || (() => { }),
- isOpen: false,
- timeout: null,
- id: id++
- }
- toast.timeout = setTimeout(this.close, time || 3000, toast)
- nextTick(() => { toast.isOpen = true })
- if (this.toasts.length > 4) {
- this.remove(0)
- }
- this.toasts.push(toast)
- },
- remove: function(index) {
- const removed = this.toasts.splice(index, 1)
- clearTimeout(removed[0].timeout)
- }
- }
-})
diff --git a/src/renderer/components/ft-toast/ft-toast.vue b/src/renderer/components/ft-toast/ft-toast.vue
deleted file mode 100644
index 6fddb89b3826f..0000000000000
--- a/src/renderer/components/ft-toast/ft-toast.vue
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- {{ toast.message }}
-
-
-
-
-
-
-
diff --git a/src/renderer/components/top-nav/top-nav.vue b/src/renderer/components/top-nav/top-nav.vue
index 328910ce4a78e..b4d509e55a866 100644
--- a/src/renderer/components/top-nav/top-nav.vue
+++ b/src/renderer/components/top-nav/top-nav.vue
@@ -84,6 +84,7 @@
url
+ player.decipher = async (url) => url
let urlTransformer
@@ -859,7 +859,9 @@ export function convertInvidiousToLocalFormat(format) {
const [initStart, initEnd] = format.init.split('-')
const [indexStart, indexEnd] = format.index.split('-')
- const duration = parseInt(parseFloat(new URL(format.url).searchParams.get('dur')) * 1000)
+ const url = new URL(format.url)
+
+ const duration = parseInt(parseFloat(url.searchParams.get('dur')) * 1000)
// only converts the properties that are needed to generate a DASH manifest with YouTube.js
// audioQuality and qualityLabel don't go inside the DASH manifest, but are used by YouTube.js
@@ -896,6 +898,27 @@ export function convertInvidiousToLocalFormat(format) {
})
})
+ if (localFormat.has_audio && url.searchParams.has('xtags')) {
+ const xtags = url.searchParams.get('xtags').split(':')
+
+ localFormat.language = xtags.find((tag) => tag.startsWith('lang='))?.split('=')[1] || null
+ localFormat.is_drc = xtags.includes('drc=1')
+
+ const audioContent = xtags.find((tag) => tag.startsWith('acont='))?.split('=')[1]
+ localFormat.is_dubbed = audioContent === 'dubbed'
+ localFormat.is_descriptive = audioContent === 'descriptive'
+ localFormat.is_secondary = audioContent === 'secondary'
+ localFormat.is_auto_dubbed = audioContent === 'dubbed-auto'
+ localFormat.is_original = audioContent === 'original' ||
+ (
+ !localFormat.is_dubbed &&
+ !localFormat.is_descriptive &&
+ !localFormat.is_secondary &&
+ !localFormat.is_auto_dubbed &&
+ !localFormat.is_drc
+ )
+ }
+
return localFormat
}
diff --git a/src/renderer/helpers/api/local.js b/src/renderer/helpers/api/local.js
index 3c75d60d874c7..c8ee7721f4607 100644
--- a/src/renderer/helpers/api/local.js
+++ b/src/renderer/helpers/api/local.js
@@ -1,4 +1,4 @@
-import { ClientType, Innertube, Misc, Mixins, Parser, UniversalCache, Utils, YT, YTNodes } from 'youtubei.js'
+import { ClientType, Innertube, Misc, Mixins, Parser, Platform, UniversalCache, Utils, YT, YTNodes } from 'youtubei.js'
import Autolinker from 'autolinker'
import { IpcChannels, SEARCH_CHAR_LIMIT } from '../../../constants'
@@ -20,6 +20,58 @@ const TRACKING_PARAM_NAMES = [
'utm_content',
]
+if (process.env.SUPPORTS_LOCAL_API) {
+ Platform.shim.eval = (data, env) => {
+ return new Promise((resolve, reject) => {
+ const properties = []
+
+ if (env.n) {
+ properties.push(`n: exportedVars.nFunction("${env.n}")`)
+ }
+
+ if (env.sig) {
+ properties.push(`sig: exportedVars.sigFunction("${env.sig}")`)
+ }
+
+ // Triggers permission errors if we don't remove it (added by YouTube.js), as sessionStorage isn't accessible in sandboxed cross-origin iframes
+ const modifiedOutput = data.output.replace('const window = Object.assign({}, globalThis);', '')
+
+ const code = `${modifiedOutput}\nreturn {${properties.join(', ')}}`
+
+ // Generate a unique ID, as there may be multiple eval calls going on at the same time (e.g. DASH manifest generation)
+ const messageId = process.env.IS_ELECTRON || crypto.randomUUID
+ ? crypto.randomUUID()
+ : `${Date.now()}-${Math.floor(Math.random() * 10000)}`
+
+ if (process.env.IS_ELECTRON) {
+ const iframe = document.getElementById('sigFrame')
+
+ /** @param {MessageEvent} event */
+ const listener = (event) => {
+ if (event.source === iframe.contentWindow && typeof event.data === 'string') {
+ const data = JSON.parse(event.data)
+
+ if (data.id === messageId) {
+ window.removeEventListener('message', listener)
+
+ if (data.error) {
+ reject(data.error)
+ } else {
+ resolve(data.result)
+ }
+ }
+ }
+ }
+
+ window.addEventListener('message', listener)
+ iframe.contentWindow.postMessage(JSON.stringify({ id: messageId, code }), '*')
+ } else {
+ reject(new Error('Please setup the eval function for the n/sig deciphering'))
+ }
+ })
+ }
+}
+
/**
* Creates a lightweight Innertube instance, which is faster to create or
* an instance that can decode the streaming URLs, which is slower to create
@@ -50,14 +102,59 @@ async function createInnertube({ withPlayer = false, location = undefined, safet
// This setting is enabled by default and results in YouTube.js reusing the same session across different Innertube instances.
// That behavior is highly undesirable for FreeTube, as we want to create a new session every time to limit tracking.
enable_session_cache: false,
+ retrieve_innertube_config: !generateSessionLocally,
+ user_agent: navigator.userAgent,
retrieve_player: !!withPlayer,
+ player_id: '9f4cc5e4',
location: location,
enable_safety_mode: !!safetyMode,
client_type: clientType,
// use browser fetch
- fetch: (input, init) => fetch(input, init),
+ fetch: !withPlayer
+ ? (input, init) => fetch(input, init)
+ : async (input, init) => {
+ if (input.url?.startsWith('https://www.youtube.com/youtubei/v1/player') && init?.headers?.get('X-Youtube-Client-Name') === '2') {
+ const response = await fetch(input, init)
+
+ const responseText = await response.text()
+
+ const json = JSON.parse(responseText)
+
+ if (Array.isArray(json.adSlots)) {
+ let waitSeconds = 0
+
+ for (const adSlot of json.adSlots) {
+ if (adSlot.adSlotRenderer?.adSlotMetadata?.triggerEvent === 'SLOT_TRIGGER_EVENT_BEFORE_CONTENT') {
+ const playerVars = adSlot.adSlotRenderer.fulfillmentContent?.fulfilledLayout?.playerBytesAdLayoutRenderer
+ ?.renderingContent?.instreamVideoAdRenderer?.playerVars
+
+ if (playerVars) {
+ const match = playerVars.match(/length_seconds=([\d.]+)/)
+
+ if (match) {
+ waitSeconds += parseFloat(match[1])
+ }
+ }
+ }
+ }
+
+ if (waitSeconds > 0) {
+ await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000))
+ }
+ }
+
+ // Need to return a new response object, as you can only read the response body once.
+ return new Response(responseText, {
+ status: response.status,
+ statusText: response.statusText,
+ headers: response.headers
+ })
+ }
+
+ return fetch(input, init)
+ },
cache,
generate_session_locally: !!generateSessionLocally
})
@@ -153,8 +250,8 @@ export async function getLocalTrending(location, tab, instance) {
results = []
resultsInstance.videos.forEach(video => {
- if (video.type === 'Video' && !alreadySeenIds.includes(video.id)) {
- alreadySeenIds.push(video.id)
+ if (video.type === 'Video' && !alreadySeenIds.includes(video.video_id)) {
+ alreadySeenIds.push(video.video_id)
results.push(parseLocalListVideo(video))
}
})
@@ -197,23 +294,52 @@ export async function getLocalSearchContinuation(continuationData) {
export async function getLocalVideoInfo(id) {
const webInnertube = await createInnertube({ withPlayer: true, generateSessionLocally: false })
- let poToken
+ // based on the videoId
+ let contentPoToken
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
try {
- poToken = await ipcRenderer.invoke(IpcChannels.GENERATE_PO_TOKEN, webInnertube.session.context.client.visitorData)
+ contentPoToken = await ipcRenderer.invoke(
+ IpcChannels.GENERATE_PO_TOKEN,
+ id,
+ JSON.stringify(webInnertube.session.context)
+ )
- webInnertube.session.po_token = poToken
- webInnertube.session.player.po_token = poToken
+ webInnertube.session.player.po_token = contentPoToken
} catch (error) {
console.error('Local API, poToken generation failed', error)
throw error
}
}
- const info = await webInnertube.getInfo(id)
+ let clientName = webInnertube.session.context.client.clientName
+
+ const info = await webInnertube.getInfo(id, { po_token: contentPoToken })
+
+ // #region temporary workaround for SABR-only responses
+
+ // MWEB doesn't have an audio track selector so it picks the audio track on the server based on the request language.
+
+ const originalAudioTrackFormat = info.streaming_data?.adaptive_formats.find(format => {
+ return format.has_audio && format.is_original && format.language
+ })
+
+ if (originalAudioTrackFormat) {
+ webInnertube.session.context.client.hl = originalAudioTrackFormat.language
+ }
+
+ const mwebInfo = await webInnertube.getBasicInfo(id, { client: 'MWEB', po_token: contentPoToken })
+
+ if (mwebInfo.playability_status.status === 'OK' && mwebInfo.streaming_data) {
+ info.playability_status = mwebInfo.playability_status
+ info.streaming_data = mwebInfo.streaming_data
+
+ clientName = 'MWEB'
+ }
+
+ // #endregion temporary workaround for SABR-only responses
let hasTrailer = info.has_trailer
let trailerIsAgeRestricted = info.getTrailerInfo() === null
@@ -226,16 +352,12 @@ export async function getLocalVideoInfo(id) {
const webEmbeddedInnertube = await createInnertube({ clientType: ClientType.WEB_EMBEDDED })
webEmbeddedInnertube.session.context.client.visitorData = webInnertube.session.context.client.visitorData
- if (poToken) {
- webEmbeddedInnertube.session.po_token = poToken
- }
-
const videoId = hasTrailer && trailerIsAgeRestricted ? info.playability_status.error_screen.video_id : id
// getBasicInfo needs the signature timestamp (sts) from inside the player
webEmbeddedInnertube.session.player = webInnertube.session.player
- const bypassedInfo = await webEmbeddedInnertube.getBasicInfo(videoId, 'WEB_EMBEDDED')
+ const bypassedInfo = await webEmbeddedInnertube.getBasicInfo(videoId, { client: 'WEB_EMBEDDED', po_token: contentPoToken })
if (bypassedInfo.playability_status.status === 'OK' && bypassedInfo.streaming_data) {
info.playability_status = bypassedInfo.playability_status
@@ -247,6 +369,8 @@ export async function getLocalVideoInfo(id) {
hasTrailer = false
trailerIsAgeRestricted = false
+
+ clientName = webEmbeddedInnertube.session.context.client.clientName
}
}
@@ -270,19 +394,36 @@ export async function getLocalVideoInfo(id) {
}
if (info.streaming_data) {
- decipherFormats(info.streaming_data.formats, webInnertube.session.player)
- decipherFormats(info.streaming_data.adaptive_formats, webInnertube.session.player)
+ await decipherFormats(info.streaming_data.formats, webInnertube.session.player)
+
+ const firstFormat = info.streaming_data.adaptive_formats[0]
+
+ if (firstFormat.url || firstFormat.signature_cipher || firstFormat.cipher) {
+ await decipherFormats(info.streaming_data.adaptive_formats, webInnertube.session.player)
+ }
if (info.streaming_data.dash_manifest_url) {
- let url = info.streaming_data.dash_manifest_url
+ info.streaming_data.dash_manifest_url = await decipherDashManifestUrl(
+ info.streaming_data.dash_manifest_url,
+ webInnertube.session.player,
+ contentPoToken
+ )
+ }
+ }
- if (url.includes('?')) {
- url += `&pot=${encodeURIComponent(poToken)}&mpd_version=7`
- } else {
- url += `${url.endsWith('/') ? '' : '/'}pot/${encodeURIComponent(poToken)}/mpd_version/7`
- }
+ if (info.captions?.caption_tracks) {
+ for (const captionTrack of info.captions.caption_tracks) {
+ const url = new URL(captionTrack.base_url)
+
+ url.searchParams.set('potc', '1')
+ url.searchParams.set('pot', contentPoToken)
+ url.searchParams.set('c', clientName)
- info.streaming_data.dash_manifest_url = url
+ // Remove &xosf=1 as it adds `position:63% line:0%` to the subtitle lines
+ // placing them in the top right corner
+ url.searchParams.delete('xosf')
+
+ captionTrack.base_url = url.toString()
}
}
@@ -310,12 +451,55 @@ export async function getLocalComments(id) {
* @param {Misc.Format[]} formats
* @param {import('youtubei.js').Player} player
*/
-function decipherFormats(formats, player) {
+async function decipherFormats(formats, player) {
for (const format of formats) {
// toDash deciphers the format again, so if we overwrite the original URL,
// it breaks because the n param would get deciphered twice and then be incorrect
- format.freeTubeUrl = format.decipher(player)
+ format.freeTubeUrl = await format.decipher(player)
+ }
+}
+
+/**
+ * @param {string} url
+ * @param {import('youtubei.js').Player} player
+ * @param {string} poToken
+ */
+async function decipherDashManifestUrl(url, player, poToken) {
+ const urlObject = new URL(url)
+
+ if (urlObject.searchParams.size > 0) {
+ urlObject.searchParams.set('pot', poToken)
+ urlObject.searchParams.set('mpd_version', '7')
+
+ return await player.decipher(urlObject.toString())
+ }
+
+ // Convert path params to query params
+ const pathParts = urlObject.pathname
+ .replace('/api/manifest/dash', '')
+ .split('/')
+ .filter(part => part.length > 0)
+
+ urlObject.pathname = '/api/manifest/dash'
+
+ for (let i = 0; i + 1 < pathParts.length; i += 2) {
+ urlObject.searchParams.set(pathParts[i], decodeURIComponent(pathParts[i + 1]))
}
+
+ // decipher
+ const deciphered = await player.decipher(urlObject.toString())
+
+ // convert query parameters back to path parameters
+ const decipheredUrlObject = new URL(deciphered)
+
+ for (const [key, value] of decipheredUrlObject.searchParams) {
+ decipheredUrlObject.pathname += `/${key}/${encodeURIComponent(value)}`
+ }
+
+ decipheredUrlObject.search = ''
+ decipheredUrlObject.pathname += `/pot/${encodeURIComponent(poToken)}/mpd_version/7`
+
+ return decipheredUrlObject.toString()
}
export async function getLocalChannelId(url) {
@@ -477,7 +661,7 @@ export async function getLocalChannelCommunity(id) {
try {
const response = await innertube.actions.execute('/browse', {
browseId: id,
- params: 'Egljb21tdW5pdHnyBgQKAkoA'
+ params: 'EgVwb3N0c_IGBAoCSgA%3D'
// protobuf for the community tab (this is the one that YouTube uses,
// it has some empty fields in the protobuf but it doesn't work if you remove them)
})
@@ -486,7 +670,7 @@ export async function getLocalChannelCommunity(id) {
// if the channel doesn't have a community tab, YouTube returns the home tab instead
// so we need to check that we got the right tab
- if (communityTab.current_tab?.endpoint.metadata.url?.endsWith('/community')) {
+ if (communityTab.current_tab?.endpoint.metadata.url?.endsWith('/posts')) {
return parseLocalCommunityPosts(communityTab.posts)
} else {
return []
@@ -1095,7 +1279,7 @@ export function parseLocalListVideo(item) {
return {
type: 'video',
- videoId: video.id,
+ videoId: video.video_id,
title: video.title.text,
author: video.author?.name,
authorId: video.author?.id,
@@ -1135,14 +1319,22 @@ export function parseLocalListVideo(item) {
video.upcoming
)
+ let viewCount = null
+
+ if (video.view_count?.text) {
+ viewCount = video.view_count.text.toLowerCase() === 'no views' ? 0 : extractNumberFromString(video.view_count.text)
+ } else if (video.short_view_count?.text) {
+ viewCount = video.short_view_count.text.toLowerCase() === 'no views' ? 0 : parseLocalSubscriberCount(video.short_view_count.text)
+ }
+
return {
type: 'video',
- videoId: video.id,
+ videoId: video.video_id,
title: video.title.text,
author: video.author.name,
authorId: video.author.id,
description: video.description,
- viewCount: video.view_count?.text == null ? (video.short_view_count.text == null ? null : parseLocalSubscriberCount(video.short_view_count.text)) : extractNumberFromString(video.view_count.text),
+ viewCount,
published,
lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
liveNow: video.is_live,
@@ -1198,6 +1390,52 @@ function parseLockupView(lockupView, channelId = undefined, channelName = undefi
videoCount: extractNumberFromString(thumbnailOverlayBadgeView.badges[0].text)
}
}
+ case 'VIDEO': {
+ let publishedText
+ let lengthSeconds = ''
+ let liveNow = false
+
+ /** @type {YTNodes.ThumbnailOverlayBadgeView | undefined} */
+ const thumbnailOverlayBadgeView = lockupView.content_image?.overlays?.firstOfType(YTNodes.ThumbnailOverlayBadgeView)
+
+ if (thumbnailOverlayBadgeView) {
+ if (thumbnailOverlayBadgeView.badges.some(badge => badge.badge_style === 'THUMBNAIL_OVERLAY_BADGE_STYLE_LIVE')) {
+ liveNow = true
+ } else {
+ const durationBadge = thumbnailOverlayBadgeView.badges.find(badge => /^[\d:]+$/.test(badge.text))
+
+ if (durationBadge) {
+ lengthSeconds = Utils.timeToSeconds(durationBadge.text)
+ }
+
+ publishedText = lockupView.metadata.metadata?.metadata_rows[1].metadata_parts?.[1].text?.text
+ }
+ }
+
+ let viewCount = null
+
+ const viewsText = lockupView.metadata.metadata?.metadata_rows[1]?.metadata_parts?.[0].text?.text
+
+ if (viewsText) {
+ const views = parseLocalSubscriberCount(viewsText)
+
+ if (!isNaN(views)) {
+ viewCount = views
+ }
+ }
+
+ return {
+ type: 'video',
+ videoId: lockupView.content_id,
+ title: lockupView.metadata.title.text,
+ author: lockupView.metadata.metadata?.metadata_rows[0].metadata_parts?.[0].text?.text,
+ authorId: lockupView.metadata.image?.renderer_context?.command_context?.on_tap?.payload.browseId,
+ viewCount,
+ published: calculatePublishedDate(publishedText, liveNow),
+ lengthSeconds,
+ liveNow
+ }
+ }
default:
console.warn(`Unknown lockup content type: ${lockupView.content_type}`, lockupView)
return null
@@ -1316,28 +1554,41 @@ function parseListItem(item) {
}
/**
- * @param {import('youtubei.js').YTNodes.CompactVideo} video
+ * @param {YTNodes.CompactVideo | YTNodes.CompactMovie | YTNodes.LockupView} video
*/
export function parseLocalWatchNextVideo(video) {
- let publishedText
+ if (video.is(YTNodes.CompactMovie)) {
+ return {
+ type: 'video',
+ videoId: video.id,
+ title: video.title.text,
+ author: video.author.name,
+ authorId: video.author.id,
+ lengthSeconds: video.duration.seconds
+ }
+ } else if (video.is(YTNodes.LockupView)) {
+ return parseLockupView(video)
+ } else {
+ let publishedText
- if (video.published != null && !video.published.isEmpty()) {
- publishedText = video.published.text
- }
+ if (video.published != null && !video.published.isEmpty()) {
+ publishedText = video.published.text
+ }
- const published = calculatePublishedDate(publishedText, video.is_live, video.is_premiere)
+ const published = calculatePublishedDate(publishedText, video.is_live, video.is_premiere)
- return {
- type: 'video',
- videoId: video.id,
- title: video.title.text,
- author: video.author.name,
- authorId: video.author.id,
- viewCount: video.view_count == null ? null : extractNumberFromString(video.view_count.text),
- published,
- lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
- liveNow: video.is_live,
- isUpcoming: video.is_premiere
+ return {
+ type: 'video',
+ videoId: video.video_id,
+ title: video.title.text,
+ author: video.author.name,
+ authorId: video.author.id,
+ viewCount: video.view_count == null ? null : extractNumberFromString(video.view_count.text),
+ published,
+ lengthSeconds: isNaN(video.duration.seconds) ? '' : video.duration.seconds,
+ liveNow: video.is_live,
+ isUpcoming: video.is_premiere
+ }
}
}
diff --git a/src/renderer/helpers/utils.js b/src/renderer/helpers/utils.js
index 1537bbc1fb1b0..65e5e19ce947a 100644
--- a/src/renderer/helpers/utils.js
+++ b/src/renderer/helpers/utils.js
@@ -1,5 +1,4 @@
import { IpcChannels } from '../../constants'
-import FtToastEvents from '../components/ft-toast/ft-toast-events'
import i18n from '../i18n/index'
import router from '../router/index'
import { nextTick } from 'vue'
@@ -162,17 +161,21 @@ export function buildVTTFileLocally(storyboard, videoLengthSeconds) {
return vttString
}
+export const ToastEventBus = new EventTarget()
+
/**
* @param {string} message
* @param {number} time
* @param {Function} action
+ * @param {AbortSignal} abortSignal
*/
-export function showToast(message, time = null, action = null) {
- FtToastEvents.dispatchEvent(new CustomEvent('toast-open', {
+export function showToast(message, time = null, action = null, abortSignal = null) {
+ ToastEventBus.dispatchEvent(new CustomEvent('toast-open', {
detail: {
message,
time,
- action
+ action,
+ abortSignal,
}
}))
}
diff --git a/src/renderer/sigFrameScript.js b/src/renderer/sigFrameScript.js
new file mode 100644
index 0000000000000..4d65c4900792f
--- /dev/null
+++ b/src/renderer/sigFrameScript.js
@@ -0,0 +1,19 @@
+// This is injected into the sigFrame iframe
+// See index.ejs and webpack.renderer.config.js
+window.addEventListener('message', (event) => {
+ // eslint-disable-next-line @stylistic/semi
+ const data = JSON.parse(event.data);
+
+ try {
+ window.parent.postMessage(JSON.stringify({
+ id: data.id,
+ // eslint-disable-next-line no-new-func
+ result: new Function(data.code)()
+ }), '*')
+ } catch (error) {
+ window.parent.postMessage(JSON.stringify({
+ id: data.id,
+ error
+ }), '*')
+ }
+})
diff --git a/src/renderer/views/Playlist/Playlist.js b/src/renderer/views/Playlist/Playlist.js
index c3edb557ee193..3493208f24b7f 100644
--- a/src/renderer/views/Playlist/Playlist.js
+++ b/src/renderer/views/Playlist/Playlist.js
@@ -51,6 +51,7 @@ export default defineComponent({
continuationData: this.continuationData,
})
}
+ this.removeToBeDeletedVideosSometimes()
next()
},
data: function () {
@@ -80,9 +81,9 @@ export default defineComponent({
videoSearchQuery: '',
promptOpen: false,
- deletedVideoIds: [],
- deletedPlaylistItemIds: [],
- isUndoToast: false
+ toBeDeletedPlaylistItemIds: [],
+ // Present = shown
+ undoToastAbortController: null,
}
},
computed: {
@@ -192,7 +193,7 @@ export default defineComponent({
const playlistItems = this.getPlaylistItemsWithDuration()
return getSortedPlaylistItems(playlistItems, this.sortOrder, this.currentLocale)
}
- return getSortedPlaylistItems(this.playlistItems, this.sortOrder, this.currentLocale)
+ return getSortedPlaylistItems(this.shownPlaylistItems, this.sortOrder, this.currentLocale)
},
visiblePlaylistItems: function () {
if (!this.isUserPlaylistRequested) {
@@ -243,6 +244,35 @@ export default defineComponent({
sortBySelectValues() {
return this.sortByValues
},
+ totalPlaylistDuration() {
+ const totalSeconds = this.shownPlaylistItems.reduce((acc, video) => {
+ if (typeof video.lengthSeconds !== 'number') {
+ return NaN
+ }
+ return acc + video.lengthSeconds
+ }, 0)
+ return totalSeconds
+ },
+ noPlaylistItemsPendingDeletion() {
+ return this.toBeDeletedPlaylistItemIds.length === 0
+ },
+ shownPlaylistItems() {
+ if (this.noPlaylistItemsPendingDeletion) {
+ return this.playlistItems
+ }
+
+ return this.playlistItems.filter((v) => !this.toBeDeletedPlaylistItemIds.includes(v.playlistItemId))
+ },
+ shownPlaylistItemCount() {
+ return this.shownPlaylistItems.length
+ },
+ shownVideoCount() {
+ if (this.isUserPlaylistRequested) {
+ return this.shownPlaylistItemCount
+ }
+
+ return this.videoCount
+ },
},
watch: {
$route() {
@@ -263,14 +293,16 @@ export default defineComponent({
// Re-fetch from local store when current user playlist updated
this.getPlaylistInfoDebounce()
},
- selectedUserPlaylistVideoCount() {
+ async selectedUserPlaylistVideoCount() {
// Monitoring `selectedUserPlaylistVideos` makes this function called
// Even when the same array object is returned
// So length is monitored instead
// Assuming in user playlist video cannot be swapped without length change
// Re-fetch from local store when current user playlist videos updated
- this.getPlaylistInfoDebounce()
+ // MUST NOT use `getPlaylistInfoDebounce` as it will cause delay in data update
+ // Causing deleted videos to reappear for one frame
+ this.getPlaylistInfo()
},
},
created: function () {
@@ -423,7 +455,6 @@ export default defineComponent({
this.firstVideoPlaylistItemId = ''
}
this.viewCount = 0
- this.videoCount = playlist.videos.length
const dateString = new Date(playlist.lastUpdatedAt)
this.lastUpdated = dateString.toLocaleDateString(this.currentLocale, { year: 'numeric', month: 'short', day: 'numeric' })
this.channelName = ''
@@ -443,7 +474,7 @@ export default defineComponent({
},
getPlaylistItemsWithDuration() {
- const modifiedPlaylistItems = deepCopy(this.playlistItems)
+ const modifiedPlaylistItems = deepCopy(this.shownPlaylistItems)
let anyVideoMissingDuration = false
modifiedPlaylistItems.forEach(video => {
if (videoDurationPresent(video)) { return }
@@ -481,10 +512,10 @@ export default defineComponent({
this.isLoadingMore = true
nextTick(() => {
- if (this.userPlaylistVisibleLimit + 100 < this.videoCount) {
+ if (this.userPlaylistVisibleLimit + 100 < this.shownVideoCount) {
this.userPlaylistVisibleLimit += 100
} else {
- this.userPlaylistVisibleLimit = this.videoCount
+ this.userPlaylistVisibleLimit = this.shownVideoCount
}
this.isLoadingMore = false
@@ -589,46 +620,30 @@ export default defineComponent({
removeVideoFromPlaylist: function (videoId, playlistItemId) {
try {
- const playlistItems = [].concat(this.playlistItems)
- const tempPlaylistItems = [].concat(this.playlistItems)
- let isUndoClicked = false
-
const videoIndex = this.playlistItems.findIndex((video) => {
return video.videoId === videoId && video.playlistItemId === playlistItemId
})
if (videoIndex !== -1) {
- this.deletedVideoIds.push(this.playlistItems[videoIndex].videoId)
- this.deletedPlaylistItemIds.push(this.playlistItems[videoIndex].playlistItemId)
- playlistItems.splice(videoIndex, 1)
- this.playlistItems = playlistItems
+ this.toBeDeletedPlaylistItemIds.push(this.playlistItems[videoIndex].playlistItemId)
// Only show toast when no existing toast shown
- if (!this.isUndoToast) {
- this.isUndoToast = true
+ if (this.undoToastAbortController == null) {
+ this.undoToastAbortController = new AbortController()
+ const timeoutMs = 5 * 1000
+ const actualRemoveVideosTimeout = setTimeout(() => {
+ this.removeToBeDeletedVideosSometimes()
+ }, timeoutMs)
showToast(
this.$t('User Playlists.SinglePlaylistView.Toast["Video has been removed. Click here to undo."]'),
- 5000,
+ timeoutMs,
() => {
- this.playlistItems = tempPlaylistItems
- isUndoClicked = true
- this.isUndoToast = false
- this.deletedVideoIds = []
- this.deletedPlaylistItemIds = []
- }
+ clearTimeout(actualRemoveVideosTimeout)
+ this.toBeDeletedPlaylistItemIds = []
+ this.undoToastAbortController = null
+ },
+ this.undoToastAbortController.signal,
)
- setTimeout(() => {
- if (!isUndoClicked) {
- this.removeVideos({
- _id: this.playlistId,
- videoIds: this.deletedVideoIds,
- playlistItemIds: this.deletedPlaylistItemIds,
- })
- this.deletedVideoIds = []
- this.deletedPlaylistItemIds = []
- this.isUndoToast = false
- }
- }, 5000)
}
}
} catch (e) {
@@ -637,6 +652,20 @@ export default defineComponent({
}
},
+ async removeToBeDeletedVideosSometimes() {
+ if (this.isLoading) { return }
+
+ if (this.toBeDeletedPlaylistItemIds.length > 0) {
+ await this.removeVideos({
+ _id: this.playlistId,
+ playlistItemIds: this.toBeDeletedPlaylistItemIds,
+ })
+ this.toBeDeletedPlaylistItemIds = []
+ this.undoToastAbortController?.abort()
+ this.undoToastAbortController = null
+ }
+ },
+
updatePageTitle() {
const playlistTitle = this.playlistTitle
const channelName = this.channelName
diff --git a/src/renderer/views/Playlist/Playlist.vue b/src/renderer/views/Playlist/Playlist.vue
index 8a673fbdd138c..c23997bc0fa9c 100644
--- a/src/renderer/views/Playlist/Playlist.vue
+++ b/src/renderer/views/Playlist/Playlist.vue
@@ -1,6 +1,6 @@
item.type === 'CompactVideo' || item.type === 'CompactMovie')
+ ?.filter((item) => {
+ return item.type === 'CompactVideo' || item.type === 'CompactMovie' ||
+ (item.type === 'LockupView' && item.content_type === 'VIDEO')
+ })
.map(parseLocalWatchNextVideo) ?? []
// place watched recommended videos last
@@ -695,25 +699,29 @@ export default defineComponent({
/** @type {import('../../helpers/api/local').LocalFormat[]} */
const formats = [...result.streaming_data.formats, ...result.streaming_data.adaptive_formats]
- const downloadLinks = formats.map((format) => {
- const qualityLabel = format.quality_label ?? format.bitrate
- const fps = format.fps ? `${format.fps}fps` : 'kbps'
- const type = format.mime_type.split(';')[0]
- let label = `${qualityLabel} ${fps} - ${type}`
+ const downloadLinks = []
- if (format.has_audio !== format.has_video) {
- if (format.has_video) {
- label += ` ${this.$t('Video.video only')}`
- } else {
- label += ` ${this.$t('Video.audio only')}`
+ for (const format of formats) {
+ if (format.freeTubeUrl) {
+ const qualityLabel = format.quality_label ?? format.bitrate
+ const fps = format.fps ? `${format.fps}fps` : 'kbps'
+ const type = format.mime_type.split(';')[0]
+ let label = `${qualityLabel} ${fps} - ${type}`
+
+ if (format.has_audio !== format.has_video) {
+ if (format.has_video) {
+ label += ` ${this.$t('Video.video only')}`
+ } else {
+ label += ` ${this.$t('Video.audio only')}`
+ }
}
- }
- return {
- url: format.freeTubeUrl,
- label: label
+ downloadLinks.push({
+ url: format.freeTubeUrl,
+ label: label
+ })
}
- })
+ }
if (result.captions) {
const captionTracks = result.captions?.caption_tracks?.map((caption) => {
@@ -779,7 +787,7 @@ export default defineComponent({
return
}
- if (result.streaming_data?.adaptive_formats.length > 0) {
+ if (result.streaming_data?.adaptive_formats.length > 0 && result.streaming_data.adaptive_formats[0].freeTubeUrl) {
this.vrProjection = result.streaming_data.adaptive_formats
.find(format => {
return format.has_video &&
@@ -1031,10 +1039,14 @@ export default defineComponent({
},
/**
- * @param {string} description
+ * @param {string?} description
*/
extractChaptersFromDescription: function (description) {
+ if (description == null) { return [] }
+
+ /** @type {{title: string, timestamp: string, startSeconds: number, endSeconds: number}[]} */
const chapters = []
+
// HH:MM:SS Text
// MM:SS Text
// HH:MM:SS - Text // separator is one of '-', '–', '•', '—'
@@ -1448,8 +1460,10 @@ export default defineComponent({
* @param {boolean} includeThumbnails
*/
createLocalDashManifest: async function (videoInfo, includeThumbnails = false) {
- const xmlData = await videoInfo.toDash(undefined, undefined, {
- include_thumbnails: includeThumbnails
+ const xmlData = await videoInfo.toDash({
+ manifest_options: {
+ include_thumbnails: includeThumbnails,
+ },
})
return `data:application/dash+xml;charset=UTF-8,${encodeURIComponent(xmlData)}`
@@ -1610,7 +1624,8 @@ export default defineComponent({
}
const url = new URL(trackToTranslate.base_url)
- url.searchParams.set('fmt', 'vtt')
+ // Requesting fmt=vtt with the tlang parameter set returns HTTP 429 errors, but requesting srt instead seems to work
+ url.searchParams.set('fmt', 'srt')
url.searchParams.set('tlang', translationCode)
const label = this.$t('Video.Player.TranslatedCaptionTemplate', {
@@ -1622,7 +1637,7 @@ export default defineComponent({
url: url.toString(),
label,
language: translationCode,
- mimeType: 'text/vtt',
+ mimeType: 'text/srt',
isAutotranslated: true
}
},
diff --git a/static/invidious-instances.json b/static/invidious-instances.json
index 85e94a22aad8b..e2446ed9c1c3d 100644
--- a/static/invidious-instances.json
+++ b/static/invidious-instances.json
@@ -27,4 +27,4 @@
"url": "https://invidious.privacyredirect.com",
"cors": true
}
-]
\ No newline at end of file
+]
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index 3012b22228be3..a41b9c3dd77c1 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -1015,7 +1015,7 @@ Tooltips:
FreeTube will automatically attempt to use your non-preferred API as a fallback
method when enabled.
Thumbnail Preference: All thumbnails throughout FreeTube will be replaced with
- a frame of the video instead of the default thumbnail.
+ a frame of the video, blurred or hidden instead of the default thumbnail.
Invidious Instance: The Invidious instance that FreeTube will connect to for API
calls.
Region for Trending: The region of trends allows you to pick which country's trending
@@ -1029,7 +1029,7 @@ Tooltips:
Proxy Videos Through Invidious: Will connect to Invidious to serve videos instead
of making a direct connection to YouTube.
Default Video Format: Set the formats used when a video plays. DASH formats can
- play higher qualities. Legacy formats are limited to a max of 720p but use less
+ play higher qualities. Legacy formats are limited to a max of 360p but use less
bandwidth. Audio formats are audio only streams.
Scroll Playback Rate Over Video Player: While the cursor is over the video, press and
hold the Control key (Command Key on Mac) and scroll the mouse wheel forwards or backwards to control
@@ -1059,7 +1059,7 @@ Tooltips:
method for grabbing your subscription feed. RSS is faster and prevents IP blocking,
but doesn't provide certain information like video duration, live status or community posts
Fetch Automatically: When enabled, FreeTube will automatically fetch
- your subscription feed when a new window is opened and when switching profile.
+ your subscription feed on startup and when a new window is opened.
Experimental Settings:
Replace HTTP Cache: Disables Electron's disk based HTTP cache and enables a custom in-memory image cache. Will lead to increased RAM usage.
SponsorBlock Settings:
diff --git a/yarn.lock b/yarn.lock
index c10ec43b577e1..2872afb23d7d4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -881,16 +881,24 @@
resolved "https://registry.yarnpkg.com/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#519c1549b0e147759e7825701ecffd25e5819f7b"
integrity sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==
-"@electron/asar@^3.2.7":
- version "3.2.13"
- resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.13.tgz#56565ea423ead184465adfa72663b2c70d9835f2"
- integrity sha512-pY5z2qQSwbFzJsBdgfJIzXf5ElHTVMutC2dxh0FD60njknMu3n1NnTABOcQwbb5/v5soqE79m9UjaJryBf3epg==
+"@electron/asar@3.2.18", "@electron/asar@^3.2.7":
+ version "3.2.18"
+ resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.18.tgz#fa607f829209bab8b9e0ce6658d3fe81b2cba517"
+ integrity sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==
dependencies:
- "@types/glob" "^7.1.0"
commander "^5.0.0"
glob "^7.1.6"
minimatch "^3.0.4"
+"@electron/fuses@^1.8.0":
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/@electron/fuses/-/fuses-1.8.0.tgz#ad34d3cc4703b1258b83f6989917052cfc1490a0"
+ integrity sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==
+ dependencies:
+ chalk "^4.1.1"
+ fs-extra "^9.0.1"
+ minimist "^1.2.5"
+
"@electron/get@^2.0.0":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.2.tgz#ae2a967b22075e9c25aaf00d5941cd79c21efd7e"
@@ -906,6 +914,21 @@
optionalDependencies:
global-agent "^3.0.0"
+"@electron/node-gyp@https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2":
+ version "10.2.0-electron.1"
+ resolved "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2"
+ dependencies:
+ env-paths "^2.2.0"
+ exponential-backoff "^3.1.1"
+ glob "^8.1.0"
+ graceful-fs "^4.2.6"
+ make-fetch-happen "^10.2.1"
+ nopt "^6.0.0"
+ proc-log "^2.0.1"
+ semver "^7.3.5"
+ tar "^6.2.1"
+ which "^2.0.2"
+
"@electron/notarize@2.5.0":
version "2.5.0"
resolved "https://registry.yarnpkg.com/@electron/notarize/-/notarize-2.5.0.tgz#d4d25356adfa29df4a76bd64a8bd347237cd251e"
@@ -927,11 +950,12 @@
minimist "^1.2.6"
plist "^3.0.5"
-"@electron/rebuild@3.6.1":
- version "3.6.1"
- resolved "https://registry.yarnpkg.com/@electron/rebuild/-/rebuild-3.6.1.tgz#59e8e36c3f6e6b94a699425dfb61f0394c3dd4df"
- integrity sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==
+"@electron/rebuild@3.7.2":
+ version "3.7.2"
+ resolved "https://registry.yarnpkg.com/@electron/rebuild/-/rebuild-3.7.2.tgz#8d808b29159c50086d27a5dec72b40bf16b4b582"
+ integrity sha512-19/KbIR/DAxbsCkiaGMXIdPnMCJLkcf8AvGnduJtWBs/CBwiAjY1apCqOLVxrXg+rtXFCngbXhBanWjxLUt1Mg==
dependencies:
+ "@electron/node-gyp" "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2"
"@malept/cross-spawn-promise" "^2.0.0"
chalk "^4.0.0"
debug "^4.1.1"
@@ -940,7 +964,6 @@
got "^11.7.0"
node-abi "^3.45.0"
node-api-version "^0.2.0"
- node-gyp "^9.0.0"
ora "^5.1.0"
read-binary-file-arch "^1.0.6"
semver "^7.3.5"
@@ -1030,11 +1053,6 @@
"@eslint/core" "^0.10.0"
levn "^0.4.1"
-"@fastify/busboy@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8"
- integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==
-
"@fortawesome/fontawesome-common-types@6.7.2":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz#7123d74b0c1e726794aed1184795dbce12186470"
@@ -1607,14 +1625,6 @@
dependencies:
"@types/node" "*"
-"@types/glob@^7.1.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
- integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
- dependencies:
- "@types/minimatch" "*"
- "@types/node" "*"
-
"@types/html-minifier-terser@^6.0.0":
version "6.1.0"
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@@ -1678,11 +1688,6 @@
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==
-"@types/minimatch@*":
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
- integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
-
"@types/ms@*":
version "0.7.31"
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
@@ -2121,10 +2126,10 @@ acorn-jsx@^5.3.2:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
-acorn@^8.14.0, acorn@^8.5.0, acorn@^8.8.0, acorn@^8.8.2, acorn@^8.9.0:
- version "8.14.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
- integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
+acorn@^8.14.0, acorn@^8.5.0, acorn@^8.8.2, acorn@^8.9.0:
+ version "8.15.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
+ integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
agent-base@6, agent-base@^6.0.2:
version "6.0.2"
@@ -2229,35 +2234,35 @@ anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
-app-builder-bin@5.0.0-alpha.10:
- version "5.0.0-alpha.10"
- resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz#cf12e593b6b847fb9d04027fa755c6c6610d778b"
- integrity sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==
+app-builder-bin@5.0.0-alpha.12:
+ version "5.0.0-alpha.12"
+ resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz#2daf82f8badc698e0adcc95ba36af4ff0650dc80"
+ integrity sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w==
-app-builder-lib@25.1.8:
- version "25.1.8"
- resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-25.1.8.tgz#ae376039c5f269c7d562af494a087e5bc6310f1b"
- integrity sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==
+app-builder-lib@26.0.13:
+ version "26.0.13"
+ resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-26.0.13.tgz#d70efc3107ff20944853419aabe34c3663d302f6"
+ integrity sha512-Iov4wX9dHRiI1rxRFk7k3X4QtltsTXN+8Kge7B5DO+pTjKxSNhJ1S+wjJAQM5VatxGxwX7HnxmHLt5PRI5AYSw==
dependencies:
"@develar/schema-utils" "~2.6.5"
+ "@electron/asar" "3.2.18"
+ "@electron/fuses" "^1.8.0"
"@electron/notarize" "2.5.0"
"@electron/osx-sign" "1.3.1"
- "@electron/rebuild" "3.6.1"
+ "@electron/rebuild" "3.7.2"
"@electron/universal" "2.0.1"
"@malept/flatpak-bundler" "^0.4.0"
"@types/fs-extra" "9.0.13"
async-exit-hook "^2.0.1"
- bluebird-lst "^1.0.9"
- builder-util "25.1.7"
- builder-util-runtime "9.2.10"
+ builder-util "26.0.13"
+ builder-util-runtime "9.3.2"
chromium-pickle-js "^0.2.0"
config-file-ts "0.2.8-rc1"
debug "^4.3.4"
dotenv "^16.4.5"
dotenv-expand "^11.0.6"
ejs "^3.1.8"
- electron-publish "25.1.7"
- form-data "^4.0.0"
+ electron-publish "26.0.13"
fs-extra "^10.1.0"
hosted-git-info "^4.1.0"
is-ci "^3.0.0"
@@ -2266,30 +2271,18 @@ app-builder-lib@25.1.8:
json5 "^2.2.3"
lazy-val "^1.0.5"
minimatch "^10.0.0"
+ plist "3.1.0"
resedit "^1.7.0"
- sanitize-filename "^1.6.3"
semver "^7.3.8"
tar "^6.1.12"
temp-file "^3.4.0"
-
-"aproba@^1.0.3 || ^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
- integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
+ tiny-async-pool "1.3.0"
are-docs-informative@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963"
integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==
-are-we-there-yet@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd"
- integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==
- dependencies:
- delegates "^1.0.0"
- readable-stream "^3.6.0"
-
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
@@ -2511,10 +2504,10 @@ batch@0.6.1:
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=
-bgutils-js@^3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/bgutils-js/-/bgutils-js-3.1.3.tgz#0bdd3aac1748ebe51777b72b346ecbe8722760f8"
- integrity sha512-MHpm4hAHAwen0tzc0asziZ0FIsrM4q6zau8d49bzPtcyb5QzJge3OjtRVUzsjXdvrnbVOeETNrdA4q8IxkAL1g==
+bgutils-js@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/bgutils-js/-/bgutils-js-3.2.0.tgz#bfc892b2a39db737827dd204bf7868214a6283f0"
+ integrity sha512-CacO15JvxbclbLeCAAm9DETGlLuisRGWpPigoRvNsccSCPEC4pwYwA2g2x/pv7Om/sk79d4ib35V5HHmxPBpDg==
big.js@^5.2.2:
version "5.2.2"
@@ -2535,14 +2528,7 @@ bl@^4.1.0:
inherits "^2.0.4"
readable-stream "^3.4.0"
-bluebird-lst@^1.0.9:
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz#a64a0e4365658b9ab5fe875eb9dfb694189bb41c"
- integrity sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==
- dependencies:
- bluebird "^3.5.5"
-
-bluebird@^3.1.1, bluebird@^3.5.5:
+bluebird@^3.1.1:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@@ -2641,35 +2627,36 @@ buffer@^6.0.3:
base64-js "^1.3.1"
ieee754 "^1.2.1"
-builder-util-runtime@9.2.10:
- version "9.2.10"
- resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz#a0f7d9e214158402e78b74a745c8d9f870c604bc"
- integrity sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==
+builder-util-runtime@9.3.2:
+ version "9.3.2"
+ resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.3.2.tgz#2a69a239b50e26accf4ed4ea1730406a3117213c"
+ integrity sha512-7QDXJ1FwT6d9ZhG4kuObUUPY8/ENBS/Ky26O4hR5vbeoRGavgekS2Jxv+8sCn/v23aPGU2DXRWEeJuijN2ooYA==
dependencies:
debug "^4.3.4"
sax "^1.2.4"
-builder-util@25.1.7:
- version "25.1.7"
- resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-25.1.7.tgz#a07b404f0cb1a635aa165902be65297d58932ff8"
- integrity sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==
+builder-util@26.0.13:
+ version "26.0.13"
+ resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-26.0.13.tgz#a2c11f8e89e5392719e540d610d70d8413943d74"
+ integrity sha512-6b64uHzywaL2KAG+rVcqk/Prta1m3I2Jo1d4d2CrApb6EeSk2V384tmSL0EniH+P8jaNbMp6qhg7cIALw32zRA==
dependencies:
"7zip-bin" "~5.2.0"
"@types/debug" "^4.1.6"
- app-builder-bin "5.0.0-alpha.10"
- bluebird-lst "^1.0.9"
- builder-util-runtime "9.2.10"
+ app-builder-bin "5.0.0-alpha.12"
+ builder-util-runtime "9.3.2"
chalk "^4.1.2"
- cross-spawn "^7.0.3"
+ cross-spawn "^7.0.6"
debug "^4.3.4"
fs-extra "^10.1.0"
http-proxy-agent "^7.0.0"
https-proxy-agent "^7.0.0"
is-ci "^3.0.0"
js-yaml "^4.1.0"
+ sanitize-filename "^1.6.3"
source-map-support "^0.5.19"
stat-mode "^1.0.0"
temp-file "^3.4.0"
+ tiny-async-pool "1.3.0"
builtin-modules@^3.3.0:
version "3.3.0"
@@ -2804,7 +2791,7 @@ caniuse-lite@^1.0.30001663:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz#99fc5ea0d9c6e96897a104a8352604378377f949"
integrity sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==
-chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
+chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -2948,11 +2935,6 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-color-support@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
- integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
-
colord@^2.9.3:
version "2.9.3"
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
@@ -3048,11 +3030,6 @@ connect-history-api-fallback@^2.0.0:
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8"
integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
-console-control-strings@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
- integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
-
consolidate@^0.15.1:
version "0.15.1"
resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7"
@@ -3438,11 +3415,6 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
-delegates@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
- integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
-
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@@ -3493,14 +3465,14 @@ dir-glob@^3.0.1:
dependencies:
path-type "^4.0.0"
-dmg-builder@25.1.8:
- version "25.1.8"
- resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-25.1.8.tgz#41f3b725edd896156e891016a44129e1bd580430"
- integrity sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==
+dmg-builder@26.0.13:
+ version "26.0.13"
+ resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-26.0.13.tgz#c5dc2db3b23e278ce146e68b45523b3f5f1780c9"
+ integrity sha512-OBa6xbQwFAm6gbbClkuGrxnOLbrPauv3yaugnGtIHsn7BvFSmMhZzhmcJQMrAGzDW2M3n/RmG/5mgOYUagqoeg==
dependencies:
- app-builder-lib "25.1.8"
- builder-util "25.1.7"
- builder-util-runtime "9.2.10"
+ app-builder-lib "26.0.13"
+ builder-util "26.0.13"
+ builder-util-runtime "9.3.2"
fs-extra "^10.1.0"
iconv-lite "^0.6.2"
js-yaml "^4.1.0"
@@ -3641,16 +3613,16 @@ ejs@^3.1.8:
dependencies:
jake "^10.8.5"
-electron-builder@^25.1.8:
- version "25.1.8"
- resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-25.1.8.tgz#b0e310f1600787610bb84c3f39bc7aadb2548486"
- integrity sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==
+electron-builder@^26.0.13:
+ version "26.0.13"
+ resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-26.0.13.tgz#7ae15b8a61e428f436cae61831dfd28f6d977540"
+ integrity sha512-DbTHV16W03TcGImDXeFulmCGIaFIh4nyXTLm5CE3ml47R54AaAlEWI+Jj1/kMsK2rproHw3eoR4l/3P7hlJ0fA==
dependencies:
- app-builder-lib "25.1.8"
- builder-util "25.1.7"
- builder-util-runtime "9.2.10"
+ app-builder-lib "26.0.13"
+ builder-util "26.0.13"
+ builder-util-runtime "9.3.2"
chalk "^4.1.2"
- dmg-builder "25.1.8"
+ dmg-builder "26.0.13"
fs-extra "^10.1.0"
is-ci "^3.0.0"
lazy-val "^1.0.5"
@@ -3680,15 +3652,16 @@ electron-is-dev@^3.0.1:
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-3.0.1.tgz#1cbc79b1dd046787903acd357efdfab6549dc17a"
integrity sha512-8TjjAh8Ec51hUi3o4TaU0mD3GMTOESi866oRNavj9A3IQJ7pmv+MJVmdZBFGw4GFT36X7bkqnuDNYvkQgvyI8Q==
-electron-publish@25.1.7:
- version "25.1.7"
- resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-25.1.7.tgz#14e50c2a3fafdc1c454eadbbc47ead89a48bb554"
- integrity sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==
+electron-publish@26.0.13:
+ version "26.0.13"
+ resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-26.0.13.tgz#04340520e6e9de5262fecfa011658cfcc3fc8917"
+ integrity sha512-O5hfHSwli5cegQ4JS3Dp0dZcheex6UCRE/qYyRQvhB6DhSwojiwTnAGEuQCJXc8K8Zxz2lku5Du3VwYHf8d5Lw==
dependencies:
"@types/fs-extra" "^9.0.11"
- builder-util "25.1.7"
- builder-util-runtime "9.2.10"
+ builder-util "26.0.13"
+ builder-util-runtime "9.3.2"
chalk "^4.1.2"
+ form-data "^4.0.0"
fs-extra "^10.1.0"
lazy-val "^1.0.5"
mime "^2.5.2"
@@ -3707,10 +3680,10 @@ electron@^34.0.1:
"@types/node" "^20.9.0"
extract-zip "^2.0.1"
-eme-encryption-scheme-polyfill@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.2.0.tgz#b642e460b0cb46a9a51a1069fde3ba2c01565af9"
- integrity sha512-wfgRcR2cGAX0WKbPahhI13dr2mzFQ/rMnoDibRIdScQlv9S0rtnXd25XF17IUdNJw/voJoFpYQp33C3xkFnyEw==
+eme-encryption-scheme-polyfill@^2.2.1:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.2.3.tgz#420608d56b995dfc440cfefba3cc06faaf02a95d"
+ integrity sha512-N0nlJZVaBqGWzvvFb6ZAxvTVeZ4v9cXCzrSFE0zCbNbviiygWI0gDJiRc4o5SqFrPVYBSQr5z5SzKjIvvxL+Vw==
emoji-regex@^10.0.0, emoji-regex@^10.3.0:
version "10.3.0"
@@ -4779,20 +4752,6 @@ functions-have-names@^1.2.2, functions-have-names@^1.2.3:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
-gauge@^4.0.3:
- version "4.0.4"
- resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce"
- integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==
- dependencies:
- aproba "^1.0.3 || ^2.0.0"
- color-support "^1.1.3"
- console-control-strings "^1.1.0"
- has-unicode "^2.0.1"
- signal-exit "^3.0.7"
- string-width "^4.2.3"
- strip-ansi "^6.0.1"
- wide-align "^1.1.5"
-
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -4919,7 +4878,7 @@ glob@^10.3.12, glob@^10.3.3:
package-json-from-dist "^1.0.0"
path-scurry "^1.11.1"
-glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
+glob@^7.1.3, glob@^7.1.6:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -4931,7 +4890,7 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^8.0.1:
+glob@^8.0.1, glob@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
@@ -5145,11 +5104,6 @@ has-tostringtag@^1.0.2:
dependencies:
has-symbols "^1.0.3"
-has-unicode@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
- integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
-
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
@@ -5949,13 +5903,6 @@ jest-worker@^29.7.0:
merge-stream "^2.0.0"
supports-color "^8.0.0"
-jintr@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/jintr/-/jintr-3.2.0.tgz#38bfc2311c7ed4412ebe0bfc5c2e700f8f433921"
- integrity sha512-psD1yf05kMKDNsUdW1l5YhO59pHScQ6OIHHb8W5SKSM2dCOFPsqolmIuSHgVA8+3Dc47NJR181CXZ4alCAPTkA==
- dependencies:
- acorn "^8.8.0"
-
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -6349,7 +6296,7 @@ lru-cache@^7.7.1:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
-make-fetch-happen@^10.0.3:
+make-fetch-happen@^10.2.1:
version "10.2.1"
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164"
integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==
@@ -6447,6 +6394,11 @@ merge2@^1.3.0, merge2@^1.4.1:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+meriyah@^6.1.4:
+ version "6.1.4"
+ resolved "https://registry.yarnpkg.com/meriyah/-/meriyah-6.1.4.tgz#2d49a8934fbcd9205c20564579c3560d9b1e077b"
+ integrity sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==
+
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -6548,6 +6500,11 @@ minimist@^1.2.0, minimist@^1.2.6:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
+minimist@^1.2.5:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
minipass-collect@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
@@ -6721,23 +6678,6 @@ node-forge@^1:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
-node-gyp@^9.0.0:
- version "9.4.1"
- resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185"
- integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==
- dependencies:
- env-paths "^2.2.0"
- exponential-backoff "^3.1.1"
- glob "^7.1.4"
- graceful-fs "^4.2.6"
- make-fetch-happen "^10.0.3"
- nopt "^6.0.0"
- npmlog "^6.0.0"
- rimraf "^3.0.2"
- semver "^7.3.5"
- tar "^6.1.2"
- which "^2.0.2"
-
node-releases@^2.0.18:
version "2.0.18"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
@@ -6789,16 +6729,6 @@ npm-run-all2@^7.0.2:
shell-quote "^1.7.3"
which "^5.0.0"
-npmlog@^6.0.0:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830"
- integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==
- dependencies:
- are-we-there-yet "^3.0.0"
- console-control-strings "^1.1.0"
- gauge "^4.0.3"
- set-blocking "^2.0.0"
-
nth-check@^2.0.1, nth-check@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
@@ -7190,15 +7120,7 @@ pkg-dir@^7.0.0:
dependencies:
find-up "^6.3.0"
-plist@^3.0.4:
- version "3.0.5"
- resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987"
- integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==
- dependencies:
- base64-js "^1.5.1"
- xmlbuilder "^9.0.7"
-
-plist@^3.0.5, plist@^3.1.0:
+plist@3.1.0, plist@^3.0.5, plist@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9"
integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==
@@ -7207,6 +7129,14 @@ plist@^3.0.5, plist@^3.1.0:
base64-js "^1.5.1"
xmlbuilder "^15.1.1"
+plist@^3.0.4:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987"
+ integrity sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==
+ dependencies:
+ base64-js "^1.5.1"
+ xmlbuilder "^9.0.7"
+
pluralize@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
@@ -7522,6 +7452,11 @@ pretty-error@^4.0.0:
lodash "^4.17.20"
renderkid "^3.0.0"
+proc-log@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685"
+ integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==
+
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -7692,7 +7627,7 @@ readable-stream@^3.0.6:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
-readable-stream@^3.4.0, readable-stream@^3.6.0:
+readable-stream@^3.4.0:
version "3.6.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
@@ -8121,7 +8056,7 @@ semver-compare@^1.0.0:
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
-"semver@2 || 3 || 4 || 5":
+"semver@2 || 3 || 4 || 5", semver@^5.5.0:
version "5.7.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
@@ -8192,11 +8127,6 @@ serve-static@1.16.2:
parseurl "~1.3.3"
send "0.19.0"
-set-blocking@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
- integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
-
set-function-length@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed"
@@ -8248,12 +8178,12 @@ setprototypeof@1.2.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
-shaka-player@^4.13.0:
- version "4.13.0"
- resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-4.13.0.tgz#03d4469ea2af6ab740f591c4e906503e7403b3d5"
- integrity sha512-FZToyYl7IXvt1oMEWVbU0tjWJpJ+oSYpUibNMxSqVQx2pLfSBDfEuodGJV+pRgiydYQ58+VFLftAylDx/Qvfdw==
+shaka-player@^4.13.4:
+ version "4.13.4"
+ resolved "https://registry.yarnpkg.com/shaka-player/-/shaka-player-4.13.4.tgz#c0b572e7bff9ed670986bd4530d0f1a68e022d26"
+ integrity sha512-dVEpbTO27gxM7GZO+4Ks9WVcL9xCLiZe9GTmlRIF7E8KaX8NlbCPHKtSK65HmJS1dfBeqd69/Kpm8724bJOWOw==
dependencies:
- eme-encryption-scheme-polyfill "^2.2.0"
+ eme-encryption-scheme-polyfill "^2.2.1"
shallow-clone@^3.0.0:
version "3.0.1"
@@ -8298,7 +8228,7 @@ side-channel@^1.0.6:
get-intrinsic "^1.2.4"
object-inspect "^1.13.1"
-signal-exit@^3.0.2, signal-exit@^3.0.7:
+signal-exit@^3.0.2:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
@@ -8522,7 +8452,7 @@ statuses@2.0.1:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
-"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -8866,7 +8796,7 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
-tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.1.2:
+tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
@@ -8922,6 +8852,13 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
+tiny-async-pool@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz#c013e1b369095e7005db5595f95e646cca6ef8a5"
+ integrity sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==
+ dependencies:
+ semver "^5.5.0"
+
tmp-promise@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7"
@@ -8965,7 +8902,7 @@ ts-api-utils@^1.3.0:
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
-tslib@^2.0.0, tslib@^2.0.3, tslib@^2.3.1, tslib@^2.5.0, tslib@^2.6.2, tslib@^2.6.3, tslib@^2.8.1:
+tslib@^2.0.0, tslib@^2.0.3, tslib@^2.3.1, tslib@^2.6.2, tslib@^2.6.3, tslib@^2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
@@ -9117,13 +9054,6 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
-undici@^5.19.1:
- version "5.28.5"
- resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.5.tgz#b2b94b6bf8f1d919bc5a6f31f2c01deb02e54d4b"
- integrity sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==
- dependencies:
- "@fastify/busboy" "^2.0.0"
-
unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
@@ -9580,13 +9510,6 @@ which@^5.0.0:
dependencies:
isexe "^3.1.1"
-wide-align@^1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
- integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
- dependencies:
- string-width "^1.0.2 || 2 || 3 || 4"
-
wildcard@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
@@ -9718,16 +9641,14 @@ yocto-queue@^0.1.0:
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
yocto-queue@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
- integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.2.1.tgz#36d7c4739f775b3cbc28e6136e21aa057adec418"
+ integrity sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==
-youtubei.js@^13.0.0:
- version "13.0.0"
- resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-13.0.0.tgz#f036bc6f5cb9ef8bd73da7e1079a2b70776bb2f7"
- integrity sha512-b1QkN9bfgphK+5tI4qteSK54kNxmPhoedvMw0jl4uSn+L8gbDbJ4z52amNuYNcOdp4X/SI3JuUb+f5V0DPJ8Vw==
+youtubei.js@^16.0.0:
+ version "16.0.0"
+ resolved "https://registry.yarnpkg.com/youtubei.js/-/youtubei.js-16.0.0.tgz#b88537c4d89468216c50058cd295ce3c1da8adf2"
+ integrity sha512-aMx+ulnrxzsgVsxTr7gbBVnIjti2NQUlMwCoo1/MzICCJS3iMLOPUFdo7bSpwskL6ljzQ/LxmmB4WQC3FtkBlA==
dependencies:
"@bufbuild/protobuf" "^2.0.0"
- jintr "^3.2.0"
- tslib "^2.5.0"
- undici "^5.19.1"
+ meriyah "^6.1.4"