mirror of
https://github.com/BluemediaDev/muse.git
synced 2025-06-27 17:22:42 +02:00
Merge branch 'master' into feature/select-dotenv-path
This commit is contained in:
commit
ce8edf4145
25 changed files with 529 additions and 350 deletions
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
|
108
.github/workflows/pr-release.yml
vendored
Normal file
108
.github/workflows/pr-release.yml
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
name: Release snapshot of PR
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Build snapshot of PR"]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY_IMAGE: ghcr.io/museofficial/muse
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-and-comment:
|
||||||
|
name: Release snapshot and comment in PR
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
|
- name: Download images
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/images
|
||||||
|
pattern: image-linux-*
|
||||||
|
merge-multiple: true
|
||||||
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
github-token: ${{ secrets.GH_PAT }}
|
||||||
|
|
||||||
|
- name: Load image
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
docker load -i /tmp/images/image-linux-amd64.tar
|
||||||
|
docker load -i /tmp/images/image-linux-arm64.tar
|
||||||
|
|
||||||
|
- name: Download SHA
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/SHA
|
||||||
|
pattern: sha
|
||||||
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
github-token: ${{ secrets.GH_PAT }}
|
||||||
|
|
||||||
|
- name: Read SHA
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "SHA=$(cat /tmp/SHA/sha/sha.txt | tr -d '\n')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Push images
|
||||||
|
run: |
|
||||||
|
docker push ${{ env.REGISTRY_IMAGE }}:${{ env.SHA }}-linux-amd64
|
||||||
|
docker push ${{ env.REGISTRY_IMAGE }}:${{ env.SHA }}-linux-arm64
|
||||||
|
|
||||||
|
- name: Download Docker metadata
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/metadata
|
||||||
|
pattern: metadata
|
||||||
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
github-token: ${{ secrets.GH_PAT }}
|
||||||
|
|
||||||
|
- name: Read the metadata.json file
|
||||||
|
id: metadata_reader
|
||||||
|
uses: juliangruber/read-file-action@v1.0.0
|
||||||
|
with:
|
||||||
|
path: /tmp/metadata/metadata/metadata.json
|
||||||
|
|
||||||
|
- name: Download PR number
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/pull_request_number
|
||||||
|
pattern: pull_request_number
|
||||||
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
github-token: ${{ secrets.GH_PAT }}
|
||||||
|
|
||||||
|
- name: Read PR number
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "PR_NUMBER=$(cat /tmp/pull_request_number/pull_request_number/pull_request_number.txt | tr -d '\n')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create manifest list and push
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create $(cat /tmp/metadata/metadata/metadata.json | jq -cr '.tags | map("-t " + .) | join(" ")') ${{ env.REGISTRY_IMAGE }}:${{ env.SHA }}-linux-amd64 ${{ env.REGISTRY_IMAGE }}:${{ env.SHA }}-linux-arm64
|
||||||
|
|
||||||
|
- name: Create comment
|
||||||
|
uses: marocchino/sticky-pull-request-comment@v2
|
||||||
|
with:
|
||||||
|
header: "pr-release"
|
||||||
|
number: ${{ env.PR_NUMBER }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||||
|
message: |
|
||||||
|
#### :package: :robot: A new release has been made for this pull request.
|
||||||
|
|
||||||
|
To play around with this PR, pull `${{ env.REGISTRY_IMAGE }}:pr-${{ env.PR_NUMBER }}`.
|
||||||
|
|
||||||
|
Images are available for x86_64 and ARM64.
|
||||||
|
|
||||||
|
> Latest commit: ${{ env.SHA }}
|
101
.github/workflows/pr-snapshot.yml
vendored
Normal file
101
.github/workflows/pr-snapshot.yml
vendored
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
name: Build snapshot of PR
|
||||||
|
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY_IMAGE: ghcr.io/museofficial/muse
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build snapshot
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
runner-platform:
|
||||||
|
- ubuntu-latest
|
||||||
|
- namespace-profile-default-arm64
|
||||||
|
include:
|
||||||
|
- runner-platform: ubuntu-latest
|
||||||
|
build-arch: linux/amd64
|
||||||
|
- runner-platform: namespace-profile-default-arm64
|
||||||
|
build-arch: linux/arm64
|
||||||
|
runs-on: ${{ matrix.runner-platform }}
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.build-arch }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY_IMAGE }}
|
||||||
|
tags: type=ref,event=pr
|
||||||
|
|
||||||
|
- name: Set up Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get current time
|
||||||
|
uses: josStorer/get-current-time@v2
|
||||||
|
id: current-time
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
id: build
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
outputs: type=docker,dest=/tmp/image-${{ env.PLATFORM_PAIR }}.tar
|
||||||
|
platforms: ${{ matrix.build-arch }}
|
||||||
|
tags: |
|
||||||
|
${{ env.REGISTRY_IMAGE }}:${{ github.sha }}-${{ env.PLATFORM_PAIR }}
|
||||||
|
build-args: |
|
||||||
|
COMMIT_HASH=${{ github.sha }}
|
||||||
|
BUILD_DATE=${{ steps.current-time.outputs.time }}
|
||||||
|
|
||||||
|
- name: Export Docker meta output
|
||||||
|
shell: bash
|
||||||
|
run: echo $DOCKER_METADATA_OUTPUT_JSON > /tmp/metadata.json
|
||||||
|
|
||||||
|
- name: Upload metadata
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: metadata
|
||||||
|
path: /tmp/metadata.json
|
||||||
|
overwrite: true
|
||||||
|
|
||||||
|
- name: Export SHA
|
||||||
|
run: |
|
||||||
|
echo "${{ github.sha }}" > /tmp/sha.txt
|
||||||
|
|
||||||
|
- name: Upload SHA
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sha
|
||||||
|
path: /tmp/sha.txt
|
||||||
|
overwrite: true
|
||||||
|
|
||||||
|
- name: Upload image
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: image-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: /tmp/image-${{ env.PLATFORM_PAIR }}.tar
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
- name: Save PR number in artifact
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
PR_NUMBER: ${{ github.event.number }}
|
||||||
|
run: echo $PR_NUMBER > /tmp/pull_request_number.txt
|
||||||
|
- name: Upload PR number
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: pull_request_number
|
||||||
|
path: /tmp/pull_request_number.txt
|
||||||
|
overwrite: true
|
91
.github/workflows/pr.yml
vendored
91
.github/workflows/pr.yml
vendored
|
@ -1,91 +0,0 @@
|
||||||
name: PR Workflow
|
|
||||||
|
|
||||||
on: pull_request_target
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release-snapshot:
|
|
||||||
name: Release snapshot
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
runner-platform:
|
|
||||||
- ubuntu-latest
|
|
||||||
- buildjet-4vcpu-ubuntu-2204-arm
|
|
||||||
include:
|
|
||||||
- runner-platform: ubuntu-latest
|
|
||||||
build-arch: linux/amd64
|
|
||||||
tagged-platform: amd64
|
|
||||||
- runner-platform: buildjet-4vcpu-ubuntu-2204-arm
|
|
||||||
build-arch: linux/arm64
|
|
||||||
tagged-platform: arm64
|
|
||||||
runs-on: ${{ matrix.runner-platform }}
|
|
||||||
steps:
|
|
||||||
- name: Set up Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Cache Docker layers
|
|
||||||
# AWS data transfer is pricy
|
|
||||||
if: ${{ matrix.runner-platform != 'buildjet-4vcpu-ubuntu-2204-arm' }}
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-prs-${{ matrix.build-arch }}-${{ github.event.pull_request.head.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-prs-${{ matrix.build-arch }}
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
|
||||||
|
|
||||||
- name: Get current time
|
|
||||||
uses: josStorer/get-current-time@v2
|
|
||||||
id: current-time
|
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
tags: codetheweb/muse:${{ github.event.pull_request.head.sha }}-${{ matrix.tagged-platform }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache,mode=min
|
|
||||||
platforms: ${{ matrix.build-arch }}
|
|
||||||
build-args: |
|
|
||||||
COMMIT_HASH=${{ github.sha }}
|
|
||||||
BUILD_DATE=${{ steps.current-time.outputs.time }}
|
|
||||||
|
|
||||||
combine-and-comment:
|
|
||||||
name: Combine platform tags and leave comment
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: release-snapshot
|
|
||||||
steps:
|
|
||||||
- name: Set up Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Combine tags
|
|
||||||
run: docker buildx imagetools create -t 'codetheweb/muse:pr-${{ github.event.number }}' -t 'codetheweb/muse:${{ github.event.pull_request.head.sha }}' 'codetheweb/muse:${{ github.event.pull_request.head.sha }}-arm64' 'codetheweb/muse:${{ github.event.pull_request.head.sha }}-amd64'
|
|
||||||
|
|
||||||
- name: Create comment
|
|
||||||
uses: marocchino/sticky-pull-request-comment@v2
|
|
||||||
with:
|
|
||||||
header: "pr-release"
|
|
||||||
message: |
|
|
||||||
#### :package: A new release has been made for this pull request.
|
|
||||||
|
|
||||||
To play around with this PR, pull `codetheweb/muse:pr-${{ github.event.number }}` or `codetheweb/muse:${{ github.event.pull_request.head.sha }}`.
|
|
||||||
|
|
||||||
Images are available for x86_64 and ARM64.
|
|
||||||
|
|
||||||
> Latest commit: ${{ github.event.pull_request.head.sha }}
|
|
76
.github/workflows/publish.yml
vendored
76
.github/workflows/publish.yml
vendored
|
@ -5,34 +5,32 @@ on:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY_IMAGE: ghcr.io/museofficial/muse
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
runner-platform:
|
runner-platform:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
- buildjet-4vcpu-ubuntu-2204-arm
|
- namespace-profile-default-arm64
|
||||||
include:
|
include:
|
||||||
- runner-platform: ubuntu-latest
|
- runner-platform: ubuntu-latest
|
||||||
build-arch: linux/amd64
|
build-arch: linux/amd64
|
||||||
tagged-platform: amd64
|
tagged-platform: amd64
|
||||||
- runner-platform: buildjet-4vcpu-ubuntu-2204-arm
|
- runner-platform: namespace-profile-default-arm64
|
||||||
build-arch: linux/arm64
|
build-arch: linux/arm64
|
||||||
tagged-platform: arm64
|
tagged-platform: arm64
|
||||||
runs-on: ${{ matrix.runner-platform }}
|
runs-on: ${{ matrix.runner-platform }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Buildx
|
- name: Set up Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Cache Docker layers
|
|
||||||
# AWS data transfer is pricy
|
|
||||||
if: ${{ matrix.runner-platform != 'buildjet-4vcpu-ubuntu-2204-arm' }}
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-prs-${{ matrix.build-arch }}-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-prs-${{ matrix.build-arch }}
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
|
@ -40,19 +38,26 @@ jobs:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Get current time
|
- name: Get current time
|
||||||
uses: josStorer/get-current-time@v2
|
uses: josStorer/get-current-time@v2
|
||||||
id: current-time
|
id: current-time
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
id: docker_build
|
id: docker_build
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: codetheweb/muse:${{ github.sha }}-${{ matrix.tagged-platform }}
|
tags: |
|
||||||
|
codetheweb/muse:${{ github.sha }}-${{ matrix.tagged-platform }}
|
||||||
|
${{ env.REGISTRY_IMAGE }}:${{ github.sha }}-${{ matrix.tagged-platform }}
|
||||||
platforms: ${{ matrix.build-arch }}
|
platforms: ${{ matrix.build-arch }}
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache,mode=min
|
|
||||||
build-args: |
|
build-args: |
|
||||||
COMMIT_HASH=${{ github.sha }}
|
COMMIT_HASH=${{ github.sha }}
|
||||||
BUILD_DATE=${{ steps.current-time.outputs.time }}
|
BUILD_DATE=${{ steps.current-time.outputs.time }}
|
||||||
|
@ -61,6 +66,11 @@ jobs:
|
||||||
name: Combine platform tags
|
name: Combine platform tags
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: publish
|
needs: publish
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
@ -73,21 +83,37 @@ jobs:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Get tags
|
- name: Login to GitHub Container Registry
|
||||||
id: get-tags
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get tags (Docker Hub)
|
||||||
|
id: get-tags-dockerhub
|
||||||
uses: Surgo/docker-smart-tag-action@v1
|
uses: Surgo/docker-smart-tag-action@v1
|
||||||
with:
|
with:
|
||||||
docker_image: codetheweb/muse
|
docker_image: codetheweb/muse
|
||||||
|
|
||||||
- name: Combine tags
|
- name: Get tags (ghcr.io)
|
||||||
run: docker buildx imagetools create $(echo '${{ steps.get-tags.outputs.tag }}' | tr "," "\0" | xargs -0 printf -- '-t %s ') 'codetheweb/muse:${{ github.sha }}-arm64' 'codetheweb/muse:${{ github.sha }}-amd64'
|
id: get-tags-ghcr
|
||||||
|
uses: Surgo/docker-smart-tag-action@v1
|
||||||
|
with:
|
||||||
|
docker_image: ${{ env.REGISTRY_IMAGE }}
|
||||||
|
|
||||||
|
- name: Combine tags (Docker Hub)
|
||||||
|
run: docker buildx imagetools create $(echo '${{ steps.get-tags-dockerhub.outputs.tag }}' | tr "," "\0" | xargs -0 printf -- '-t %s ') 'codetheweb/muse:${{ github.sha }}-arm64' 'codetheweb/muse:${{ github.sha }}-amd64'
|
||||||
|
|
||||||
|
- name: Combine tags (GitHub Container Registry)
|
||||||
|
run: docker buildx imagetools create $(echo '${{ steps.get-tags-ghcr.outputs.tag }}' | tr "," "\0" | xargs -0 printf -- '-t %s ') '${{ env.REGISTRY_IMAGE }}:${{ github.sha }}-arm64' '${{ env.REGISTRY_IMAGE }}:${{ github.sha }}-amd64'
|
||||||
|
|
||||||
- name: Update Docker Hub description
|
- name: Update Docker Hub description
|
||||||
uses: peter-evans/dockerhub-description@v2.4.3
|
uses: peter-evans/dockerhub-description@v2.4.3
|
||||||
env:
|
with:
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
DOCKERHUB_REPOSITORY: codetheweb/muse
|
repository: codetheweb/muse
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Create GitHub release
|
name: Create GitHub release
|
||||||
|
|
2
.github/workflows/release-comment.yml
vendored
2
.github/workflows/release-comment.yml
vendored
|
@ -8,6 +8,6 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: apexskier/github-release-commenter@v1
|
- uses: apexskier/github-release-commenter@v1
|
||||||
with:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||||
comment-template: |
|
comment-template: |
|
||||||
🚀 Released in {release_link}.
|
🚀 Released in {release_link}.
|
||||||
|
|
2
.github/workflows/type-check.yml
vendored
2
.github/workflows/type-check.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -6,7 +6,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [2.9.4] - 2024-08-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- An optional `page-size` to `/queue` command
|
||||||
|
- Add `default-queue-page-size` setting
|
||||||
|
|
||||||
|
## [2.9.3] - 2024-08-19
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- bumped @discordjs/voice
|
||||||
|
- bumped @distube/ytdl-core
|
||||||
|
|
||||||
|
## [2.9.2] - 2024-08-18
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Muse has new maintainers! I ([@codetheweb](https://github.com/codetheweb)) am stepping aside as I haven't used Muse myself for a few years and haven't been able to spend as much time on Muse as I'd like. See [this issue](https://github.com/museofficial/muse/issues/1063) for details. Welcome @museofficial/maintainers!
|
||||||
|
- This repository has been moved to museofficial/muse.
|
||||||
|
- Docker images are now published to `ghcr.io/museofficial/muse`. **Please update your image source if you use Docker**.
|
||||||
|
|
||||||
|
## [2.9.1] - 2024-08-04
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- bumped ytdl-core
|
||||||
|
|
||||||
|
## [2.9.0] - 2024-07-17
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- A `skip` option to the `/play` command
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed playback issue
|
||||||
- Audioplayer not stopping properly
|
- Audioplayer not stopping properly
|
||||||
|
|
||||||
## [2.8.1] - 2024-04-28
|
## [2.8.1] - 2024-04-28
|
||||||
|
@ -311,7 +344,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Added
|
### Added
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|
||||||
[unreleased]: https://github.com/codetheweb/muse/compare/v2.8.1...HEAD
|
[unreleased]: https://github.com/codetheweb/muse/compare/v2.9.4...HEAD
|
||||||
|
[2.9.4]: https://github.com/codetheweb/muse/compare/v2.9.3...v2.9.4
|
||||||
|
[2.9.3]: https://github.com/codetheweb/muse/compare/v2.9.2...v2.9.3
|
||||||
|
[2.9.2]: https://github.com/codetheweb/muse/compare/v2.9.1...v2.9.2
|
||||||
|
[2.9.1]: https://github.com/codetheweb/muse/compare/v2.9.0...v2.9.1
|
||||||
|
[2.9.0]: https://github.com/codetheweb/muse/compare/v2.8.1...v2.9.0
|
||||||
[2.8.1]: https://github.com/codetheweb/muse/compare/v2.8.0...v2.8.1
|
[2.8.1]: https://github.com/codetheweb/muse/compare/v2.8.0...v2.8.1
|
||||||
[2.8.0]: https://github.com/codetheweb/muse/compare/v2.7.1...v2.8.0
|
[2.8.0]: https://github.com/codetheweb/muse/compare/v2.7.1...v2.8.0
|
||||||
[2.7.1]: https://github.com/codetheweb/muse/compare/v2.7.0...v2.7.1
|
[2.7.1]: https://github.com/codetheweb/muse/compare/v2.7.0...v2.7.1
|
||||||
|
|
11
Dockerfile
11
Dockerfile
|
@ -1,4 +1,4 @@
|
||||||
FROM node:18.7.0-slim AS base
|
FROM node:18-bullseye-slim AS base
|
||||||
|
|
||||||
# openssl will be a required package if base is updated to 18.16+ due to node:*-slim base distro change
|
# openssl will be a required package if base is updated to 18.16+ due to node:*-slim base distro change
|
||||||
# https://github.com/prisma/prisma/issues/19729#issuecomment-1591270599
|
# https://github.com/prisma/prisma/issues/19729#issuecomment-1591270599
|
||||||
|
@ -19,7 +19,6 @@ FROM base AS dependencies
|
||||||
WORKDIR /usr/app
|
WORKDIR /usr/app
|
||||||
|
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
COPY patches ./patches
|
|
||||||
COPY yarn.lock .
|
COPY yarn.lock .
|
||||||
|
|
||||||
RUN yarn install --prod
|
RUN yarn install --prod
|
||||||
|
@ -49,10 +48,10 @@ COPY . .
|
||||||
ARG COMMIT_HASH=unknown
|
ARG COMMIT_HASH=unknown
|
||||||
ARG BUILD_DATE=unknown
|
ARG BUILD_DATE=unknown
|
||||||
|
|
||||||
ENV DATA_DIR /data
|
ENV DATA_DIR=/data
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV=production
|
||||||
ENV COMMIT_HASH $COMMIT_HASH
|
ENV COMMIT_HASH=$COMMIT_HASH
|
||||||
ENV BUILD_DATE $BUILD_DATE
|
ENV BUILD_DATE=$BUILD_DATE
|
||||||
ENV ENV_FILE /config
|
ENV ENV_FILE /config
|
||||||
|
|
||||||
CMD ["tini", "--", "node", "--enable-source-maps", "dist/scripts/migrate-and-start.js"]
|
CMD ["tini", "--", "node", "--enable-source-maps", "dist/scripts/migrate-and-start.js"]
|
||||||
|
|
18
README.md
18
README.md
|
@ -1,8 +1,10 @@
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img width="250" height="250" src="https://raw.githubusercontent.com/codetheweb/muse/master/.github/logo.png">
|
<img width="250" height="250" src="https://raw.githubusercontent.com/museofficial/muse/master/.github/logo.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
🚨: v1.0.0 was a breaking change. Please take a look at the [release notes](https://github.com/codetheweb/muse/releases/tag/v1.0.0) for upgrade instructions
|
> [!WARNING]
|
||||||
|
> I ([@codetheweb](https://github.com/codetheweb)) am no longer the primary maintainer of Muse. **If you use the Docker image, update your image source to `ghcr.io/museofficial/muse`.** We are currently publishing new releases to both `ghcr.io/museofficial/muse` and `codetheweb/muse`, but this may change in the future.
|
||||||
|
> Thank you to all the people who stepped up to help maintain Muse!
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ A 64-bit OS is required to run Muse.
|
||||||
|
|
||||||
The `master` branch acts as the developing / bleeding edge branch and is not guaranteed to be stable.
|
The `master` branch acts as the developing / bleeding edge branch and is not guaranteed to be stable.
|
||||||
|
|
||||||
When running a production instance, I recommend that you use the [latest release](https://github.com/codetheweb/muse/releases/).
|
When running a production instance, I recommend that you use the [latest release](https://github.com/museofficial/muse/releases/).
|
||||||
|
|
||||||
|
|
||||||
### 🐳 Docker
|
### 🐳 Docker
|
||||||
|
@ -56,7 +58,7 @@ There are a variety of image tags available:
|
||||||
(Replace empty config strings with correct values.)
|
(Replace empty config strings with correct values.)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it -v "$(pwd)/data":/data -e DISCORD_TOKEN='' -e SPOTIFY_CLIENT_ID='' -e SPOTIFY_CLIENT_SECRET='' -e YOUTUBE_API_KEY='' codetheweb/muse:latest
|
docker run -it -v "$(pwd)/data":/data -e DISCORD_TOKEN='' -e SPOTIFY_CLIENT_ID='' -e SPOTIFY_CLIENT_SECRET='' -e YOUTUBE_API_KEY='' ghcr.io/museofficial/muse:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
This starts Muse and creates a data directory in your current directory.
|
This starts Muse and creates a data directory in your current directory.
|
||||||
|
@ -70,7 +72,7 @@ version: '3.4'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
muse:
|
muse:
|
||||||
image: codetheweb/muse:latest
|
image: ghcr.io/museofficial/muse:latest
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- ./muse:/data
|
- ./muse:/data
|
||||||
|
@ -84,16 +86,16 @@ services:
|
||||||
### Node.js
|
### Node.js
|
||||||
|
|
||||||
**Prerequisites**:
|
**Prerequisites**:
|
||||||
* Node.js (16.x is recommended because it's the current LTS version)
|
* Node.js (18.17.0 or later is required and latest 18.x.x LTS is recommended)
|
||||||
* ffmpeg (4.1 or later)
|
* ffmpeg (4.1 or later)
|
||||||
|
|
||||||
1. `git clone https://github.com/codetheweb/muse.git && cd muse`
|
1. `git clone https://github.com/museofficial/muse.git && cd muse`
|
||||||
2. Copy `.env.example` to `.env` and populate with values
|
2. Copy `.env.example` to `.env` and populate with values
|
||||||
3. I recommend checking out a tagged release with `git checkout v[latest release]`
|
3. I recommend checking out a tagged release with `git checkout v[latest release]`
|
||||||
4. `yarn install` (or `npm i`)
|
4. `yarn install` (or `npm i`)
|
||||||
5. `yarn start` (or `npm run start`)
|
5. `yarn start` (or `npm run start`)
|
||||||
|
|
||||||
**Note**: if you're on Windows, you may need to manually set the ffmpeg path. See [#345](https://github.com/codetheweb/muse/issues/345) for details.
|
**Note**: if you're on Windows, you may need to manually set the ffmpeg path. See [#345](https://github.com/museofficial/muse/issues/345) for details.
|
||||||
|
|
||||||
## ⚙️ Additional configuration (advanced)
|
## ⚙️ Additional configuration (advanced)
|
||||||
|
|
||||||
|
|
5
RELEASING.md
Normal file
5
RELEASING.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Releasing
|
||||||
|
|
||||||
|
1. Confirm that CHANGELOG.md is updated (any new changes should go under "Unreleased" at the top).
|
||||||
|
2. On the master branch, run `yarn release` and follow the prompts.
|
||||||
|
3. After a new tag is pushed from the above step, the [publish workflow](./.github/workflows/publish.yml) will automatically build & push Docker images and create a GitHub release for the tag.
|
|
@ -0,0 +1,19 @@
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Setting" (
|
||||||
|
"guildId" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"playlistLimit" INTEGER NOT NULL DEFAULT 50,
|
||||||
|
"secondsToWaitAfterQueueEmpties" INTEGER NOT NULL DEFAULT 30,
|
||||||
|
"leaveIfNoListeners" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"queueAddResponseEphemeral" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"autoAnnounceNextSong" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"defaultVolume" INTEGER NOT NULL DEFAULT 100,
|
||||||
|
"defaultQueuePageSize" INTEGER NOT NULL DEFAULT 10,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Setting" ("autoAnnounceNextSong", "createdAt", "defaultVolume", "guildId", "leaveIfNoListeners", "playlistLimit", "queueAddResponseEphemeral", "secondsToWaitAfterQueueEmpties", "updatedAt") SELECT "autoAnnounceNextSong", "createdAt", "defaultVolume", "guildId", "leaveIfNoListeners", "playlistLimit", "queueAddResponseEphemeral", "secondsToWaitAfterQueueEmpties", "updatedAt" FROM "Setting";
|
||||||
|
DROP TABLE "Setting";
|
||||||
|
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
11
package.json
11
package.json
|
@ -1,15 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "muse",
|
"name": "muse",
|
||||||
"version": "2.8.1",
|
"version": "2.9.4",
|
||||||
"description": "🎧 a self-hosted Discord music bot that doesn't suck ",
|
"description": "🎧 a self-hosted Discord music bot that doesn't suck ",
|
||||||
"repository": "git@github.com:codetheweb/muse.git",
|
"repository": "git@github.com:museofficial/muse.git",
|
||||||
"author": "Max Isom <hi@maxisom.me>",
|
"author": "Max Isom <hi@maxisom.me>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
"types": "dts/types",
|
"types": "dts/types",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=18.17.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src"
|
"src"
|
||||||
|
@ -88,7 +88,9 @@
|
||||||
"@discordjs/builders": "1.1.0",
|
"@discordjs/builders": "1.1.0",
|
||||||
"@discordjs/opus": "^0.8.0",
|
"@discordjs/opus": "^0.8.0",
|
||||||
"@discordjs/rest": "1.0.1",
|
"@discordjs/rest": "1.0.1",
|
||||||
"@discordjs/voice": "0.11.0",
|
"@discordjs/voice": "0.17.0",
|
||||||
|
"@distube/ytdl-core": "^4.14.4",
|
||||||
|
"@distube/ytsr": "^2.0.0",
|
||||||
"@prisma/client": "4.16.0",
|
"@prisma/client": "4.16.0",
|
||||||
"@types/libsodium-wrappers": "^0.7.9",
|
"@types/libsodium-wrappers": "^0.7.9",
|
||||||
"array-shuffle": "^3.0.0",
|
"array-shuffle": "^3.0.0",
|
||||||
|
@ -126,7 +128,6 @@
|
||||||
"sync-fetch": "^0.3.1",
|
"sync-fetch": "^0.3.1",
|
||||||
"tsx": "3.8.2",
|
"tsx": "3.8.2",
|
||||||
"xbytes": "^1.7.0",
|
"xbytes": "^1.7.0",
|
||||||
"ytdl-core": "^4.11.5",
|
|
||||||
"ytsr": "^3.8.4"
|
"ytsr": "^3.8.4"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
|
|
@ -1,172 +0,0 @@
|
||||||
diff --git a/node_modules/ytdl-core/lib/sig.js b/node_modules/ytdl-core/lib/sig.js
|
|
||||||
index eb7bfaa..b2eee87 100644
|
|
||||||
--- a/node_modules/ytdl-core/lib/sig.js
|
|
||||||
+++ b/node_modules/ytdl-core/lib/sig.js
|
|
||||||
@@ -3,6 +3,9 @@ const Cache = require('./cache');
|
|
||||||
const utils = require('./utils');
|
|
||||||
const vm = require('vm');
|
|
||||||
|
|
||||||
+
|
|
||||||
+let nTransformWarning = false;
|
|
||||||
+
|
|
||||||
// A shared cache to keep track of html5player js functions.
|
|
||||||
exports.cache = new Cache();
|
|
||||||
|
|
||||||
@@ -23,6 +26,49 @@ exports.getFunctions = (html5playerfile, options) => exports.cache.getOrSet(html
|
|
||||||
return functions;
|
|
||||||
});
|
|
||||||
|
|
||||||
+// eslint-disable-next-line max-len
|
|
||||||
+// https://github.com/TeamNewPipe/NewPipeExtractor/blob/41c8dce452aad278420715c00810b1fed0109adf/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java#L816
|
|
||||||
+const DECIPHER_REGEXPS = [
|
|
||||||
+ '(?:\\b|[^a-zA-Z0-9$])([a-zA-Z0-9$]{2,})\\s*=\\s*function\\(\\s*a\\s*\\)' +
|
|
||||||
+ '\\s*\\{\\s*a\\s*=\\s*a\\.split\\(\\s*""\\s*\\)',
|
|
||||||
+ '\\bm=([a-zA-Z0-9$]{2,})\\(decodeURIComponent\\(h\\.s\\)\\)',
|
|
||||||
+ '\\bc&&\\(c=([a-zA-Z0-9$]{2,})\\(decodeURIComponent\\(c\\)\\)',
|
|
||||||
+ '([\\w$]+)\\s*=\\s*function\\((\\w+)\\)\\{\\s*\\2=\\s*\\2\\.split\\(""\\)\\s*;',
|
|
||||||
+ '\\b([\\w$]{2,})\\s*=\\s*function\\((\\w+)\\)\\{\\s*\\2=\\s*\\2\\.split\\(""\\)\\s*;',
|
|
||||||
+ '\\bc\\s*&&\\s*d\\.set\\([^,]+\\s*,\\s*(:encodeURIComponent\\s*\\()([a-zA-Z0-9$]+)\\(',
|
|
||||||
+];
|
|
||||||
+
|
|
||||||
+const DECIPHER_ARGUMENT = 'sig';
|
|
||||||
+const N_ARGUMENT = 'ncode';
|
|
||||||
+
|
|
||||||
+const matchGroup1 = (regex, str) => {
|
|
||||||
+ const match = str.match(new RegExp(regex));
|
|
||||||
+ if (!match) throw new Error(`Could not match ${regex}`);
|
|
||||||
+ return match[1];
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+const getFuncName = (body, regexps) => {
|
|
||||||
+ try {
|
|
||||||
+ let fn;
|
|
||||||
+ for (const regex of regexps) {
|
|
||||||
+ try {
|
|
||||||
+ fn = matchGroup1(regex, body);
|
|
||||||
+ const idx = fn.indexOf('[0]');
|
|
||||||
+ if (idx > -1) fn = matchGroup1(`${fn.slice(0, 3)}=\\[([a-zA-Z0-9$\\[\\]]{2,})\\]`, body);
|
|
||||||
+ } catch (err) {
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ if (!fn || fn.includes('[')) throw Error("Couldn't find fn name");
|
|
||||||
+ return fn;
|
|
||||||
+ } catch (e) {
|
|
||||||
+ throw Error(`Please open an issue on ytdl-core GitHub: ${e.message}`);
|
|
||||||
+ }
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+const getDecipherFuncName = body => getFuncName(body, DECIPHER_REGEXPS);
|
|
||||||
+
|
|
||||||
+
|
|
||||||
/**
|
|
||||||
* Extracts the actions that should be taken to decipher a signature
|
|
||||||
* and tranform the n parameter
|
|
||||||
@@ -31,44 +77,45 @@ exports.getFunctions = (html5playerfile, options) => exports.cache.getOrSet(html
|
|
||||||
* @returns {Array.<string>}
|
|
||||||
*/
|
|
||||||
exports.extractFunctions = body => {
|
|
||||||
+ body = body.replace(/\n|\r/g, '');
|
|
||||||
const functions = [];
|
|
||||||
- const extractManipulations = caller => {
|
|
||||||
- const functionName = utils.between(caller, `a=a.split("");`, `.`);
|
|
||||||
- if (!functionName) return '';
|
|
||||||
- const functionStart = `var ${functionName}={`;
|
|
||||||
- const ndx = body.indexOf(functionStart);
|
|
||||||
- if (ndx < 0) return '';
|
|
||||||
- const subBody = body.slice(ndx + functionStart.length - 1);
|
|
||||||
- return `var ${functionName}=${utils.cutAfterJS(subBody)}`;
|
|
||||||
- };
|
|
||||||
+ // This is required function, so we can't continue if it's not found.
|
|
||||||
const extractDecipher = () => {
|
|
||||||
- const functionName = utils.between(body, `a.set("alr","yes");c&&(c=`, `(decodeURIC`);
|
|
||||||
- if (functionName && functionName.length) {
|
|
||||||
- const functionStart = `${functionName}=function(a)`;
|
|
||||||
- const ndx = body.indexOf(functionStart);
|
|
||||||
- if (ndx >= 0) {
|
|
||||||
- const subBody = body.slice(ndx + functionStart.length);
|
|
||||||
- let functionBody = `var ${functionStart}${utils.cutAfterJS(subBody)}`;
|
|
||||||
- functionBody = `${extractManipulations(functionBody)};${functionBody};${functionName}(sig);`;
|
|
||||||
- functions.push(functionBody);
|
|
||||||
- }
|
|
||||||
+ const decipherFuncName = getDecipherFuncName(body);
|
|
||||||
+ try {
|
|
||||||
+ const functionPattern = `(${decipherFuncName.replace(/\$/g, '\\$')}=function\\([a-zA-Z0-9_]+\\)\\{.+?\\})`;
|
|
||||||
+ const decipherFunction = `var ${matchGroup1(functionPattern, body)};`;
|
|
||||||
+ const helperObjectName = matchGroup1(';([A-Za-z0-9_\\$]{2,})\\.\\w+\\(', decipherFunction)
|
|
||||||
+ .replace(/\$/g, '\\$');
|
|
||||||
+ const helperPattern = `(var ${helperObjectName}=\\{[\\s\\S]+?\\}\\};)`;
|
|
||||||
+ const helperObject = matchGroup1(helperPattern, body);
|
|
||||||
+ const callerFunction = `${decipherFuncName}(${DECIPHER_ARGUMENT});`;
|
|
||||||
+ const resultFunction = helperObject + decipherFunction + callerFunction;
|
|
||||||
+ functions.push(resultFunction);
|
|
||||||
+ } catch (err) {
|
|
||||||
+ throw Error(`Could not parse decipher function: ${err}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
- const extractNCode = () => {
|
|
||||||
- let functionName = utils.between(body, `&&(b=a.get("n"))&&(b=`, `(b)`);
|
|
||||||
- if (functionName.includes('[')) functionName = utils.between(body, `var ${functionName.split('[')[0]}=[`, `]`);
|
|
||||||
- if (functionName && functionName.length) {
|
|
||||||
- const functionStart = `${functionName}=function(a)`;
|
|
||||||
- const ndx = body.indexOf(functionStart);
|
|
||||||
- if (ndx >= 0) {
|
|
||||||
- const subBody = body.slice(ndx + functionStart.length);
|
|
||||||
- const functionBody = `var ${functionStart}${utils.cutAfterJS(subBody)};${functionName}(ncode);`;
|
|
||||||
- functions.push(functionBody);
|
|
||||||
+ // This is optional, so we can continue if it's not found, but it will bottleneck the download.
|
|
||||||
+ const extractNTransform = () => {
|
|
||||||
+ let nFuncName = utils.between(body, `(b=a.get("n"))&&(b=`, `(b)`);
|
|
||||||
+ if (nFuncName.includes('[')) nFuncName = utils.between(body, `${nFuncName.split('[')[0]}=[`, `]`);
|
|
||||||
+ if (nFuncName && nFuncName.length) {
|
|
||||||
+ const nBegin = `${nFuncName}=function(a)`;
|
|
||||||
+ const nEnd = '.join("")};';
|
|
||||||
+ const nFunction = utils.between(body, nBegin, nEnd);
|
|
||||||
+ if (nFunction) {
|
|
||||||
+ const callerFunction = `${nFuncName}(${N_ARGUMENT});`;
|
|
||||||
+ const resultFunction = nBegin + nFunction + nEnd + callerFunction;
|
|
||||||
+ functions.push(resultFunction);
|
|
||||||
+ } else if (!nTransformWarning) {
|
|
||||||
+ console.warn('Could not parse n transform function, please report it on @distube/ytdl-core GitHub.');
|
|
||||||
+ nTransformWarning = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
extractDecipher();
|
|
||||||
- extractNCode();
|
|
||||||
+ extractNTransform();
|
|
||||||
return functions;
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -82,22 +129,25 @@ exports.extractFunctions = body => {
|
|
||||||
exports.setDownloadURL = (format, decipherScript, nTransformScript) => {
|
|
||||||
const decipher = url => {
|
|
||||||
const args = querystring.parse(url);
|
|
||||||
- if (!args.s || !decipherScript) return args.url;
|
|
||||||
+ if (!args.s) return args.url;
|
|
||||||
const components = new URL(decodeURIComponent(args.url));
|
|
||||||
- components.searchParams.set(args.sp ? args.sp : 'signature',
|
|
||||||
- decipherScript.runInNewContext({ sig: decodeURIComponent(args.s) }));
|
|
||||||
+ const context = {};
|
|
||||||
+ context[DECIPHER_ARGUMENT] = decodeURIComponent(args.s);
|
|
||||||
+ components.searchParams.set(args.sp || 'sig', decipherScript.runInNewContext(context));
|
|
||||||
return components.toString();
|
|
||||||
};
|
|
||||||
- const ncode = url => {
|
|
||||||
+ const nTransform = url => {
|
|
||||||
const components = new URL(decodeURIComponent(url));
|
|
||||||
const n = components.searchParams.get('n');
|
|
||||||
if (!n || !nTransformScript) return url;
|
|
||||||
- components.searchParams.set('n', nTransformScript.runInNewContext({ ncode: n }));
|
|
||||||
+ const context = {};
|
|
||||||
+ context[N_ARGUMENT] = n;
|
|
||||||
+ components.searchParams.set('n', nTransformScript.runInNewContext(context));
|
|
||||||
return components.toString();
|
|
||||||
};
|
|
||||||
const cipher = !format.url;
|
|
||||||
const url = format.url || format.signatureCipher || format.cipher;
|
|
||||||
- format.url = cipher ? ncode(decipher(url)) : ncode(url);
|
|
||||||
+ format.url = cipher ? nTransform(decipher(url)) : nTransform(url);
|
|
||||||
delete format.signatureCipher;
|
|
||||||
delete format.cipher;
|
|
||||||
};
|
|
|
@ -31,6 +31,7 @@ model Setting {
|
||||||
queueAddResponseEphemeral Boolean @default(false)
|
queueAddResponseEphemeral Boolean @default(false)
|
||||||
autoAnnounceNextSong Boolean @default(false)
|
autoAnnounceNextSong Boolean @default(false)
|
||||||
defaultVolume Int @default(100)
|
defaultVolume Int @default(100)
|
||||||
|
defaultQueuePageSize Int @default(10)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,15 @@ export default class implements Command {
|
||||||
.setMinValue(0)
|
.setMinValue(0)
|
||||||
.setMaxValue(100)
|
.setMaxValue(100)
|
||||||
.setRequired(true)))
|
.setRequired(true)))
|
||||||
|
.addSubcommand(subcommand => subcommand
|
||||||
|
.setName('set-default-queue-page-size')
|
||||||
|
.setDescription('set the default page size of the /queue command')
|
||||||
|
.addIntegerOption(option => option
|
||||||
|
.setName('page-size')
|
||||||
|
.setDescription('page size of the /queue command')
|
||||||
|
.setMinValue(1)
|
||||||
|
.setMaxValue(30)
|
||||||
|
.setRequired(true)))
|
||||||
.addSubcommand(subcommand => subcommand
|
.addSubcommand(subcommand => subcommand
|
||||||
.setName('get')
|
.setName('get')
|
||||||
.setDescription('show all settings'));
|
.setDescription('show all settings'));
|
||||||
|
@ -171,6 +180,23 @@ export default class implements Command {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'set-default-queue-page-size': {
|
||||||
|
const value = interaction.options.getInteger('page-size')!;
|
||||||
|
|
||||||
|
await prisma.setting.update({
|
||||||
|
where: {
|
||||||
|
guildId: interaction.guild!.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
defaultQueuePageSize: value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.reply('👍 default queue page size updated');
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'get': {
|
case 'get': {
|
||||||
const embed = new EmbedBuilder().setTitle('Config');
|
const embed = new EmbedBuilder().setTitle('Config');
|
||||||
|
|
||||||
|
@ -185,6 +211,7 @@ export default class implements Command {
|
||||||
'Auto announce next song in queue': config.autoAnnounceNextSong ? 'yes' : 'no',
|
'Auto announce next song in queue': config.autoAnnounceNextSong ? 'yes' : 'no',
|
||||||
'Add to queue reponses show for requester only': config.autoAnnounceNextSong ? 'yes' : 'no',
|
'Add to queue reponses show for requester only': config.autoAnnounceNextSong ? 'yes' : 'no',
|
||||||
'Default Volume': config.defaultVolume,
|
'Default Volume': config.defaultVolume,
|
||||||
|
'Default queue page size': config.defaultQueuePageSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
let description = '';
|
let description = '';
|
||||||
|
|
|
@ -28,7 +28,10 @@ export default class implements Command {
|
||||||
.setDescription('shuffle the input if you\'re adding multiple tracks'))
|
.setDescription('shuffle the input if you\'re adding multiple tracks'))
|
||||||
.addBooleanOption(option => option
|
.addBooleanOption(option => option
|
||||||
.setName('split')
|
.setName('split')
|
||||||
.setDescription('if a track has chapters, split it')))
|
.setDescription('if a track has chapters, split it'))
|
||||||
|
.addBooleanOption(option => option
|
||||||
|
.setName('skip')
|
||||||
|
.setDescription('skip the currently playing track')))
|
||||||
.addSubcommand(subcommand => subcommand
|
.addSubcommand(subcommand => subcommand
|
||||||
.setName('list')
|
.setName('list')
|
||||||
.setDescription('list all favorites'))
|
.setDescription('list all favorites'))
|
||||||
|
@ -124,6 +127,7 @@ export default class implements Command {
|
||||||
shuffleAdditions: interaction.options.getBoolean('shuffle') ?? false,
|
shuffleAdditions: interaction.options.getBoolean('shuffle') ?? false,
|
||||||
addToFrontOfQueue: interaction.options.getBoolean('immediate') ?? false,
|
addToFrontOfQueue: interaction.options.getBoolean('immediate') ?? false,
|
||||||
shouldSplitChapters: interaction.options.getBoolean('split') ?? false,
|
shouldSplitChapters: interaction.options.getBoolean('split') ?? false,
|
||||||
|
skipCurrentTrack: interaction.options.getBoolean('skip') ?? false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,10 @@ export default class implements Command {
|
||||||
.setDescription('shuffle the input if you\'re adding multiple tracks'))
|
.setDescription('shuffle the input if you\'re adding multiple tracks'))
|
||||||
.addBooleanOption(option => option
|
.addBooleanOption(option => option
|
||||||
.setName('split')
|
.setName('split')
|
||||||
.setDescription('if a track has chapters, split it'));
|
.setDescription('if a track has chapters, split it'))
|
||||||
|
.addBooleanOption(option => option
|
||||||
|
.setName('skip')
|
||||||
|
.setDescription('skip the currently playing track'));
|
||||||
|
|
||||||
public requiresVC = true;
|
public requiresVC = true;
|
||||||
|
|
||||||
|
@ -52,6 +55,7 @@ export default class implements Command {
|
||||||
addToFrontOfQueue: interaction.options.getBoolean('immediate') ?? false,
|
addToFrontOfQueue: interaction.options.getBoolean('immediate') ?? false,
|
||||||
shuffleAdditions: interaction.options.getBoolean('shuffle') ?? false,
|
shuffleAdditions: interaction.options.getBoolean('shuffle') ?? false,
|
||||||
shouldSplitChapters: interaction.options.getBoolean('split') ?? false,
|
shouldSplitChapters: interaction.options.getBoolean('split') ?? false,
|
||||||
|
skipCurrentTrack: interaction.options.getBoolean('skip') ?? false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {TYPES} from '../types.js';
|
||||||
import PlayerManager from '../managers/player.js';
|
import PlayerManager from '../managers/player.js';
|
||||||
import Command from './index.js';
|
import Command from './index.js';
|
||||||
import {buildQueueEmbed} from '../utils/build-embed.js';
|
import {buildQueueEmbed} from '../utils/build-embed.js';
|
||||||
|
import {getGuildSettings} from '../utils/get-guild-settings.js';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export default class implements Command {
|
export default class implements Command {
|
||||||
|
@ -14,6 +15,12 @@ export default class implements Command {
|
||||||
.addIntegerOption(option => option
|
.addIntegerOption(option => option
|
||||||
.setName('page')
|
.setName('page')
|
||||||
.setDescription('page of queue to show [default: 1]')
|
.setDescription('page of queue to show [default: 1]')
|
||||||
|
.setRequired(false))
|
||||||
|
.addIntegerOption(option => option
|
||||||
|
.setName('page-size')
|
||||||
|
.setDescription('how many items to display per page [default: 10, max: 30]')
|
||||||
|
.setMinValue(1)
|
||||||
|
.setMaxValue(30)
|
||||||
.setRequired(false));
|
.setRequired(false));
|
||||||
|
|
||||||
private readonly playerManager: PlayerManager;
|
private readonly playerManager: PlayerManager;
|
||||||
|
@ -23,9 +30,17 @@ export default class implements Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async execute(interaction: ChatInputCommandInteraction) {
|
public async execute(interaction: ChatInputCommandInteraction) {
|
||||||
const player = this.playerManager.get(interaction.guild!.id);
|
const guildId = interaction.guild!.id;
|
||||||
|
const player = this.playerManager.get(guildId);
|
||||||
|
|
||||||
const embed = buildQueueEmbed(player, interaction.options.getInteger('page') ?? 1);
|
const pageSizeFromOptions = interaction.options.getInteger('page-size');
|
||||||
|
const pageSize = pageSizeFromOptions ?? (await getGuildSettings(guildId)).defaultQueuePageSize;
|
||||||
|
|
||||||
|
const embed = buildQueueEmbed(
|
||||||
|
player,
|
||||||
|
interaction.options.getInteger('page') ?? 1,
|
||||||
|
pageSize,
|
||||||
|
);
|
||||||
|
|
||||||
await interaction.reply({embeds: [embed]});
|
await interaction.reply({embeds: [embed]});
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,5 +40,5 @@ export default async (guild: Guild): Promise<void> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const owner = await guild.fetchOwner();
|
const owner = await guild.fetchOwner();
|
||||||
await owner.send('👋 Hi! Someone (probably you) just invited me to a server you own. By default, I\'m usable by all guild member in all guild channels. To change this, check out the wiki page on permissions: https://github.com/codetheweb/muse/wiki/Configuring-Bot-Permissions.');
|
await owner.send('👋 Hi! Someone (probably you) just invited me to a server you own. By default, I\'m usable by all guild member in all guild channels. To change this, check out the wiki page on permissions: https://github.com/museofficial/muse/wiki/Configuring-Bot-Permissions.');
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,12 +38,14 @@ export default class AddQueryToQueue {
|
||||||
addToFrontOfQueue,
|
addToFrontOfQueue,
|
||||||
shuffleAdditions,
|
shuffleAdditions,
|
||||||
shouldSplitChapters,
|
shouldSplitChapters,
|
||||||
|
skipCurrentTrack,
|
||||||
interaction,
|
interaction,
|
||||||
}: {
|
}: {
|
||||||
query: string;
|
query: string;
|
||||||
addToFrontOfQueue: boolean;
|
addToFrontOfQueue: boolean;
|
||||||
shuffleAdditions: boolean;
|
shuffleAdditions: boolean;
|
||||||
shouldSplitChapters: boolean;
|
shouldSplitChapters: boolean;
|
||||||
|
skipCurrentTrack: boolean;
|
||||||
interaction: ChatInputCommandInteraction;
|
interaction: ChatInputCommandInteraction;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const guildId = interaction.guild!.id;
|
const guildId = interaction.guild!.id;
|
||||||
|
@ -169,6 +171,14 @@ export default class AddQueryToQueue {
|
||||||
await player.play();
|
await player.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (skipCurrentTrack) {
|
||||||
|
try {
|
||||||
|
await player.forward(1);
|
||||||
|
} catch (_: unknown) {
|
||||||
|
throw new Error('no song to skip to');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build response message
|
// Build response message
|
||||||
if (statusMsg !== '') {
|
if (statusMsg !== '') {
|
||||||
if (extraMsg === '') {
|
if (extraMsg === '') {
|
||||||
|
@ -183,9 +193,9 @@ export default class AddQueryToQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newSongs.length === 1) {
|
if (newSongs.length === 1) {
|
||||||
await interaction.editReply(`u betcha, **${firstSong.title}** added to the${addToFrontOfQueue ? ' front of the' : ''} queue${extraMsg}`);
|
await interaction.editReply(`u betcha, **${firstSong.title}** added to the${addToFrontOfQueue ? ' front of the' : ''} queue${skipCurrentTrack ? 'and current track skipped' : ''}${extraMsg}`);
|
||||||
} else {
|
} else {
|
||||||
await interaction.editReply(`u betcha, **${firstSong.title}** and ${newSongs.length - 1} other songs were added to the queue${extraMsg}`);
|
await interaction.editReply(`u betcha, **${firstSong.title}** and ${newSongs.length - 1} other songs were added to the queue${skipCurrentTrack ? 'and current track skipped' : ''}${extraMsg}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {VoiceChannel, Snowflake} from 'discord.js';
|
import {VoiceChannel, Snowflake} from 'discord.js';
|
||||||
import {Readable} from 'stream';
|
import {Readable} from 'stream';
|
||||||
import hasha from 'hasha';
|
import hasha from 'hasha';
|
||||||
import ytdl, {videoFormat} from 'ytdl-core';
|
import ytdl, {videoFormat} from '@distube/ytdl-core';
|
||||||
import {WriteStream} from 'fs-capacitor';
|
import {WriteStream} from 'fs-capacitor';
|
||||||
import ffmpeg from 'fluent-ffmpeg';
|
import ffmpeg from 'fluent-ffmpeg';
|
||||||
import shuffle from 'array-shuffle';
|
import shuffle from 'array-shuffle';
|
||||||
|
@ -280,8 +280,8 @@ export default class {
|
||||||
if (this.getCurrent() && this.status !== STATUS.PAUSED) {
|
if (this.getCurrent() && this.status !== STATUS.PAUSED) {
|
||||||
await this.play();
|
await this.play();
|
||||||
} else {
|
} else {
|
||||||
this.audioPlayer?.stop(true);
|
|
||||||
this.status = STATUS.IDLE;
|
this.status = STATUS.IDLE;
|
||||||
|
this.audioPlayer?.stop(true);
|
||||||
|
|
||||||
const settings = await getGuildSettings(this.guildId);
|
const settings = await getGuildSettings(this.guildId);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {inject, injectable} from 'inversify';
|
import {inject, injectable} from 'inversify';
|
||||||
import {toSeconds, parse} from 'iso8601-duration';
|
import {toSeconds, parse} from 'iso8601-duration';
|
||||||
import got, {Got} from 'got';
|
import got, {Got} from 'got';
|
||||||
import ytsr, {Video} from 'ytsr';
|
import ytsr, {Video} from '@distube/ytsr';
|
||||||
import PQueue from 'p-queue';
|
import PQueue from 'p-queue';
|
||||||
import {SongMetadata, QueuedPlaylist, MediaSource} from './player.js';
|
import {SongMetadata, QueuedPlaylist, MediaSource} from './player.js';
|
||||||
import {TYPES} from '../types.js';
|
import {TYPES} from '../types.js';
|
||||||
|
|
|
@ -5,8 +5,6 @@ import getProgressBar from './get-progress-bar.js';
|
||||||
import {prettyTime} from './time.js';
|
import {prettyTime} from './time.js';
|
||||||
import {truncate} from './string.js';
|
import {truncate} from './string.js';
|
||||||
|
|
||||||
const PAGE_SIZE = 10;
|
|
||||||
|
|
||||||
const getMaxSongTitleLength = (title: string) => {
|
const getMaxSongTitleLength = (title: string) => {
|
||||||
// eslint-disable-next-line no-control-regex
|
// eslint-disable-next-line no-control-regex
|
||||||
const nonASCII = /[^\x00-\x7F]+/;
|
const nonASCII = /[^\x00-\x7F]+/;
|
||||||
|
@ -77,7 +75,7 @@ export const buildPlayingMessageEmbed = (player: Player): EmbedBuilder => {
|
||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildQueueEmbed = (player: Player, page: number): EmbedBuilder => {
|
export const buildQueueEmbed = (player: Player, page: number, pageSize: number): EmbedBuilder => {
|
||||||
const currentlyPlaying = player.getCurrent();
|
const currentlyPlaying = player.getCurrent();
|
||||||
|
|
||||||
if (!currentlyPlaying) {
|
if (!currentlyPlaying) {
|
||||||
|
@ -85,14 +83,14 @@ export const buildQueueEmbed = (player: Player, page: number): EmbedBuilder => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const queueSize = player.queueSize();
|
const queueSize = player.queueSize();
|
||||||
const maxQueuePage = Math.ceil((queueSize + 1) / PAGE_SIZE);
|
const maxQueuePage = Math.ceil((queueSize + 1) / pageSize);
|
||||||
|
|
||||||
if (page > maxQueuePage) {
|
if (page > maxQueuePage) {
|
||||||
throw new Error('the queue isn\'t that big');
|
throw new Error('the queue isn\'t that big');
|
||||||
}
|
}
|
||||||
|
|
||||||
const queuePageBegin = (page - 1) * PAGE_SIZE;
|
const queuePageBegin = (page - 1) * pageSize;
|
||||||
const queuePageEnd = queuePageBegin + PAGE_SIZE;
|
const queuePageEnd = queuePageBegin + pageSize;
|
||||||
const queuedSongs = player
|
const queuedSongs = player
|
||||||
.getQueue()
|
.getQueue()
|
||||||
.slice(queuePageBegin, queuePageEnd)
|
.slice(queuePageBegin, queuePageEnd)
|
||||||
|
|
130
yarn.lock
130
yarn.lock
|
@ -126,16 +126,16 @@
|
||||||
resolved "https://registry.npmjs.org/@discordjs/util/-/util-0.3.1.tgz"
|
resolved "https://registry.npmjs.org/@discordjs/util/-/util-0.3.1.tgz"
|
||||||
integrity sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA==
|
integrity sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA==
|
||||||
|
|
||||||
"@discordjs/voice@0.11.0":
|
"@discordjs/voice@^0.17.0":
|
||||||
version "0.11.0"
|
version "0.17.0"
|
||||||
resolved "https://registry.npmjs.org/@discordjs/voice/-/voice-0.11.0.tgz"
|
resolved "https://registry.npmjs.org/@discordjs/voice/-/voice-0.17.0.tgz"
|
||||||
integrity sha512-6+9cj1dxzBJm7WJ9qyG2XZZQ8rcLl6x2caW0C0OxuTtMLAaEDntpb6lqMTFiBg/rDc4Rd59g1w0gJmib33CuHw==
|
integrity sha512-hArn9FF5ZYi1IkxdJEVnJi+OxlwLV0NJYWpKXsmNOojtGtAZHxmsELA+MZlu2KW1F/K1/nt7lFOfcMXNYweq9w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/ws" "^8.5.3"
|
"@types/ws" "^8.5.10"
|
||||||
discord-api-types "^0.36.2"
|
discord-api-types "0.37.83"
|
||||||
prism-media "^1.3.4"
|
prism-media "^1.3.5"
|
||||||
tslib "^2.4.0"
|
tslib "^2.6.2"
|
||||||
ws "^8.8.1"
|
ws "^8.16.0"
|
||||||
|
|
||||||
"@discordjs/ws@^0.8.3":
|
"@discordjs/ws@^0.8.3":
|
||||||
version "0.8.3"
|
version "0.8.3"
|
||||||
|
@ -152,6 +152,25 @@
|
||||||
tslib "^2.5.0"
|
tslib "^2.5.0"
|
||||||
ws "^8.13.0"
|
ws "^8.13.0"
|
||||||
|
|
||||||
|
"@distube/ytdl-core@^4.14.4":
|
||||||
|
version "4.14.4"
|
||||||
|
resolved "https://registry.npmjs.org/@distube/ytdl-core/-/ytdl-core-4.14.4.tgz"
|
||||||
|
integrity sha512-dHb4GW3qATIjRsS6VIhm3Pop7FdUcDFhsnyQlsPeXW7UhTPuNS0BmraKiTpFbpp0Ky+rxBQjJBfPRFsM+dT1fg==
|
||||||
|
dependencies:
|
||||||
|
http-cookie-agent "^6.0.5"
|
||||||
|
m3u8stream "^0.8.6"
|
||||||
|
miniget "^4.2.3"
|
||||||
|
sax "^1.4.1"
|
||||||
|
tough-cookie "^4.1.4"
|
||||||
|
undici "^6.19.2"
|
||||||
|
|
||||||
|
"@distube/ytsr@^2.0.0":
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@distube/ytsr/-/ytsr-2.0.4.tgz#873b7ca767b5ff362bc0e136ae0d9ca5cbde8f7b"
|
||||||
|
integrity sha512-OiSWgARQ9LTj+dXt3jmMFzUH4l86VVCD4dVC4hEHNXdqp+DyU4QEzc+W6YY6//kWkvzTaUxOo7JUY7lBzwIF0A==
|
||||||
|
dependencies:
|
||||||
|
undici "^6.18.2"
|
||||||
|
|
||||||
"@esbuild-kit/cjs-loader@^2.3.3":
|
"@esbuild-kit/cjs-loader@^2.3.3":
|
||||||
version "2.3.3"
|
version "2.3.3"
|
||||||
resolved "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.3.3.tgz"
|
resolved "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.3.3.tgz"
|
||||||
|
@ -673,6 +692,13 @@ agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
|
agent-base@^7.1.1:
|
||||||
|
version "7.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317"
|
||||||
|
integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==
|
||||||
|
dependencies:
|
||||||
|
debug "^4.3.4"
|
||||||
|
|
||||||
ajv@^6.10.0, ajv@^6.12.4:
|
ajv@^6.10.0, ajv@^6.12.4:
|
||||||
version "6.12.6"
|
version "6.12.6"
|
||||||
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
|
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
|
||||||
|
@ -1290,6 +1316,13 @@ debug@^3.2.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
|
debug@^4.3.4:
|
||||||
|
version "4.3.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
|
||||||
|
integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
decode-uri-component@^0.2.0:
|
decode-uri-component@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz"
|
resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz"
|
||||||
|
@ -2584,6 +2617,13 @@ http-cache-semantics@^4.0.0:
|
||||||
resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz"
|
resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz"
|
||||||
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
|
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
|
||||||
|
|
||||||
|
http-cookie-agent@^6.0.5:
|
||||||
|
version "6.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-cookie-agent/-/http-cookie-agent-6.0.5.tgz#23b490439464424a689d80ea7f3a560a4a893ab8"
|
||||||
|
integrity sha512-sfZ8fDgDP3B1YB+teqSnAK1aPgBu8reUUGxSsndP2XnYN6cM29EURXWXZqQQiaRdor3B4QjpkUNfv21syaO4DA==
|
||||||
|
dependencies:
|
||||||
|
agent-base "^7.1.1"
|
||||||
|
|
||||||
http-errors@2.0.0:
|
http-errors@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz"
|
||||||
|
@ -3310,7 +3350,7 @@ lru-cache@^6.0.0:
|
||||||
|
|
||||||
m3u8stream@^0.8.6:
|
m3u8stream@^0.8.6:
|
||||||
version "0.8.6"
|
version "0.8.6"
|
||||||
resolved "https://registry.npmjs.org/m3u8stream/-/m3u8stream-0.8.6.tgz"
|
resolved "https://registry.yarnpkg.com/m3u8stream/-/m3u8stream-0.8.6.tgz#0d6de4ce8ee69731734e6b616e7b05dd9d9a55b1"
|
||||||
integrity sha512-LZj8kIVf9KCphiHmH7sbFQTVe4tOemb202fWwvJwR9W5ENW/1hxJN6ksAWGhQgSBSa3jyWhnjKU1Fw1GaOdbyA==
|
integrity sha512-LZj8kIVf9KCphiHmH7sbFQTVe4tOemb202fWwvJwR9W5ENW/1hxJN6ksAWGhQgSBSa3jyWhnjKU1Fw1GaOdbyA==
|
||||||
dependencies:
|
dependencies:
|
||||||
miniget "^4.2.2"
|
miniget "^4.2.2"
|
||||||
|
@ -3393,6 +3433,11 @@ miniget@^4.2.2:
|
||||||
resolved "https://registry.npmjs.org/miniget/-/miniget-4.2.2.tgz"
|
resolved "https://registry.npmjs.org/miniget/-/miniget-4.2.2.tgz"
|
||||||
integrity sha512-a7voNL1N5lDMxvTMExOkg+Fq89jM2vY8pAi9ZEWzZtfNmdfP6RXkvUtFnCAXoCv2T9k1v/fUJVaAEuepGcvLYA==
|
integrity sha512-a7voNL1N5lDMxvTMExOkg+Fq89jM2vY8pAi9ZEWzZtfNmdfP6RXkvUtFnCAXoCv2T9k1v/fUJVaAEuepGcvLYA==
|
||||||
|
|
||||||
|
miniget@^4.2.3:
|
||||||
|
version "4.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/miniget/-/miniget-4.2.3.tgz#3707a24c7c11c25d359473291638ab28aab349bd"
|
||||||
|
integrity sha512-SjbDPDICJ1zT+ZvQwK0hUcRY4wxlhhNpHL9nJOB2MEAXRGagTljsO8MEDzQMTFf0Q8g4QNi8P9lEm/g7e+qgzA==
|
||||||
|
|
||||||
minimatch@^3.0.4, minimatch@^3.1.2:
|
minimatch@^3.0.4, minimatch@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
||||||
|
@ -3996,6 +4041,11 @@ proxy-from-env@^1.0.0, proxy-from-env@^1.1.0:
|
||||||
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
|
||||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||||
|
|
||||||
|
psl@^1.1.33:
|
||||||
|
version "1.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
|
||||||
|
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
|
||||||
|
|
||||||
pump@^3.0.0:
|
pump@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz"
|
||||||
|
@ -4009,6 +4059,11 @@ punycode@^2.1.0:
|
||||||
resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz"
|
resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz"
|
||||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||||
|
|
||||||
|
punycode@^2.1.1:
|
||||||
|
version "2.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||||
|
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
||||||
|
|
||||||
pupa@^2.1.1:
|
pupa@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz"
|
resolved "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz"
|
||||||
|
@ -4033,6 +4088,11 @@ query-string@^6.13.8:
|
||||||
split-on-first "^1.0.0"
|
split-on-first "^1.0.0"
|
||||||
strict-uri-encode "^2.0.0"
|
strict-uri-encode "^2.0.0"
|
||||||
|
|
||||||
|
querystringify@^2.1.1:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
||||||
|
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
||||||
|
|
||||||
queue-microtask@^1.2.2:
|
queue-microtask@^1.2.2:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
||||||
|
@ -4182,6 +4242,11 @@ require-from-string@^2.0.2:
|
||||||
resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz"
|
resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz"
|
||||||
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
|
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
|
||||||
|
|
||||||
|
requires-port@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||||
|
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
|
||||||
|
|
||||||
resolve-alpn@^1.2.0:
|
resolve-alpn@^1.2.0:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz"
|
resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz"
|
||||||
|
@ -4317,10 +4382,10 @@ safe-regex-test@^1.0.3:
|
||||||
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
|
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
sax@^1.1.3, sax@^1.2.4:
|
sax@^1.2.4, sax@^1.4.1:
|
||||||
version "1.2.4"
|
version "1.4.1"
|
||||||
resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz"
|
resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f"
|
||||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==
|
||||||
|
|
||||||
semver-compare@^1.0.0:
|
semver-compare@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
|
@ -4780,6 +4845,16 @@ token-types@^5.0.0-alpha.2, token-types@^5.0.1:
|
||||||
"@tokenizer/token" "^0.3.0"
|
"@tokenizer/token" "^0.3.0"
|
||||||
ieee754 "^1.2.1"
|
ieee754 "^1.2.1"
|
||||||
|
|
||||||
|
tough-cookie@^4.1.4:
|
||||||
|
version "4.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36"
|
||||||
|
integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==
|
||||||
|
dependencies:
|
||||||
|
psl "^1.1.33"
|
||||||
|
punycode "^2.1.1"
|
||||||
|
universalify "^0.2.0"
|
||||||
|
url-parse "^1.5.3"
|
||||||
|
|
||||||
tr46@~0.0.3:
|
tr46@~0.0.3:
|
||||||
version "0.0.3"
|
version "0.0.3"
|
||||||
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
|
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
|
||||||
|
@ -4955,6 +5030,11 @@ undici@^5.8.0:
|
||||||
resolved "https://registry.npmjs.org/undici/-/undici-5.8.1.tgz"
|
resolved "https://registry.npmjs.org/undici/-/undici-5.8.1.tgz"
|
||||||
integrity sha512-iDRmWX4Zar/4A/t+1LrKQRm102zw2l9Wgat3LtTlTn8ykvMZmAmpq9tjyHEigx18FsY7IfATvyN3xSw9BDz0eA==
|
integrity sha512-iDRmWX4Zar/4A/t+1LrKQRm102zw2l9Wgat3LtTlTn8ykvMZmAmpq9tjyHEigx18FsY7IfATvyN3xSw9BDz0eA==
|
||||||
|
|
||||||
|
undici@^6.18.2, undici@^6.19.2:
|
||||||
|
version "6.19.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/undici/-/undici-6.19.2.tgz#231bc5de78d0dafb6260cf454b294576c2f3cd31"
|
||||||
|
integrity sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==
|
||||||
|
|
||||||
unique-string@^2.0.0:
|
unique-string@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz"
|
||||||
|
@ -4972,6 +5052,11 @@ universalify@^0.1.0:
|
||||||
resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz"
|
resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz"
|
||||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||||
|
|
||||||
|
universalify@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
|
||||||
|
integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
|
||||||
|
|
||||||
universalify@^2.0.0:
|
universalify@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
|
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
|
||||||
|
@ -5021,6 +5106,14 @@ url-parse-lax@^3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
prepend-http "^2.0.0"
|
prepend-http "^2.0.0"
|
||||||
|
|
||||||
|
url-parse@^1.5.3:
|
||||||
|
version "1.5.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
|
||||||
|
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
|
||||||
|
dependencies:
|
||||||
|
querystringify "^2.1.1"
|
||||||
|
requires-port "^1.0.0"
|
||||||
|
|
||||||
util-deprecate@^1.0.1:
|
util-deprecate@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
||||||
|
@ -5228,15 +5321,6 @@ yocto-queue@^1.0.0:
|
||||||
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz"
|
||||||
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
||||||
|
|
||||||
ytdl-core@^4.11.5:
|
|
||||||
version "4.11.5"
|
|
||||||
resolved "https://registry.npmjs.org/ytdl-core/-/ytdl-core-4.11.5.tgz"
|
|
||||||
integrity sha512-27LwsW4n4nyNviRCO1hmr8Wr5J1wLLMawHCQvH8Fk0hiRqrxuIu028WzbJetiYH28K8XDbeinYW4/wcHQD1EXA==
|
|
||||||
dependencies:
|
|
||||||
m3u8stream "^0.8.6"
|
|
||||||
miniget "^4.2.2"
|
|
||||||
sax "^1.1.3"
|
|
||||||
|
|
||||||
ytsr@^3.8.4:
|
ytsr@^3.8.4:
|
||||||
version "3.8.4"
|
version "3.8.4"
|
||||||
resolved "https://registry.npmjs.org/ytsr/-/ytsr-3.8.4.tgz"
|
resolved "https://registry.npmjs.org/ytsr/-/ytsr-3.8.4.tgz"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue