Compare commits

..

167 Commits

Author SHA1 Message Date
a0af9c49bc update version to 20250823-d707074
Some checks failed
build + push containers to gitea (every) and docker (daily) / dockerhub (push) Failing after 26s
build + push containers to gitea (every) and docker (daily) / gitea (push) Failing after 14s
2025-08-23 11:58:31 -04:00
d707074052 update version to 20250823-68d5892 2025-08-23 11:58:31 -04:00
68d5892f6f update version to 20250823-72c4113 2025-08-23 11:58:31 -04:00
72c4113305 update version to 20250823-085ddb9 2025-08-23 11:58:31 -04:00
085ddb9b9a update version to 20250823-9586606 2025-08-23 11:58:31 -04:00
9586606b4c update version to 20250823-8cc52ee 2025-08-23 11:58:31 -04:00
8cc52eebaf update version to 20250823-371cbf9 2025-08-23 11:58:31 -04:00
371cbf90e9 update version to 20250823-178ff8c 2025-08-23 11:58:31 -04:00
178ff8c231 update version to 20250823-42ec673 2025-08-23 11:58:30 -04:00
42ec67374c update version to 20250823-732477b 2025-08-23 11:58:30 -04:00
732477be14 update version to 20250823-91509d0 2025-08-23 11:58:30 -04:00
91509d0947 update version to 20250823-a1d377b 2025-08-23 11:58:30 -04:00
a1d377b5e9 update version to 20250823-f720cc2 2025-08-23 11:58:30 -04:00
f720cc2863 update version to 20250823-7d230e5 2025-08-23 11:58:30 -04:00
7d230e57c4 update version to 20250823-9112e16 2025-08-23 11:58:30 -04:00
9112e16094 update version to 20250823-1568051 2025-08-23 11:58:30 -04:00
1568051ed2 update version to 20250823-4d4eb22 2025-08-23 11:58:30 -04:00
4d4eb22962 update version to 20250823-4a4e608 2025-08-23 11:58:29 -04:00
4a4e6086f3 update version to 20250823-2fe95f8 2025-08-23 11:58:29 -04:00
2fe95f8008 update version to 20250823-efcadce 2025-08-23 11:58:29 -04:00
efcadcecfe update version to 20250823-2112ccc 2025-08-23 11:58:29 -04:00
2112ccc392 update version to 20250823-898dc7f 2025-08-23 11:58:29 -04:00
898dc7f883 update version to 20250823-c94f109 2025-08-23 11:58:29 -04:00
c94f109693 update version to 20250823-071c99a 2025-08-23 11:58:29 -04:00
071c99a3f8 update version to 20250823-acbb9c7 2025-08-23 11:58:29 -04:00
acbb9c7661 update version to 20250823-9613b75 2025-08-23 11:58:29 -04:00
9613b75349 update version to 20250823-a2b93c3 2025-08-23 11:58:29 -04:00
a2b93c318e update version to 20250823-c81d581 2025-08-23 11:58:29 -04:00
c81d5816c5 update version to 20250823-6fe2621 2025-08-23 11:58:28 -04:00
6fe2621fb1 update version to 20250823-bbc5ccd 2025-08-23 11:58:28 -04:00
bbc5ccd729 update version to 20250823-8ffb14b 2025-08-23 11:58:28 -04:00
8ffb14be45 update version to 20250823-c1ce70e 2025-08-23 11:58:28 -04:00
c1ce70e7bd update version to 20250823-7f79955 2025-08-23 11:58:28 -04:00
7f79955edd update version to 20250823-e26c13a 2025-08-23 11:58:28 -04:00
e26c13aa5e update version to 20250823-0dea896 2025-08-23 11:58:28 -04:00
0dea8963e2 update version to 20250823-bdbe2d4 2025-08-23 11:58:28 -04:00
bdbe2d4550 update version to 20250823-cb8a565 2025-08-23 11:58:28 -04:00
cb8a565573 update version to 20250823-5091623 2025-08-23 11:58:28 -04:00
5091623447 update version to 20250823-4fcced5 2025-08-23 11:58:28 -04:00
4fcced5b9e update version to 20250823-1066323 2025-08-23 11:58:27 -04:00
1066323a94 update version to 20250823-9e00a69 2025-08-23 11:58:27 -04:00
9e00a69408 update version to 20250823-14bd0a0 2025-08-23 11:58:27 -04:00
14bd0a0ff0 update version to 20250823-27bf1eb 2025-08-23 11:58:27 -04:00
27bf1eba96 update version to 20250823-1fb8134 2025-08-23 11:58:27 -04:00
1fb813498f update version to 20250823-3cbe9f3 2025-08-23 11:58:27 -04:00
3cbe9f33f0 update version to 20250823-5ee9e69 2025-08-23 11:58:27 -04:00
5ee9e69bd7 update version to 20250823-8345534 2025-08-23 11:58:27 -04:00
834553450e update version to 20250823-c9d024e 2025-08-23 11:58:27 -04:00
c9d024e77a update version to 20250823-3d2a675 2025-08-23 11:58:27 -04:00
3d2a675f61 update version to 20250823-2eeb2c3 2025-08-23 11:58:27 -04:00
2eeb2c3bc5 update version to 20250823-b2eba15 2025-08-23 11:58:26 -04:00
b2eba155b3 update version to 20250823-9dde4fa 2025-08-23 11:58:26 -04:00
9dde4fa9a3 update version to 20250823-9174b4f 2025-08-23 11:58:26 -04:00
9174b4fcae update version to 20250823-a41aff6 2025-08-23 11:58:26 -04:00
a41aff6d97 update version to 20250823-af65828 2025-08-23 11:58:26 -04:00
af65828677 update version to 20250823-506c34f 2025-08-23 11:58:26 -04:00
506c34f302 update version to 20250823-b11307c 2025-08-23 11:58:26 -04:00
b11307c524 update version to 20250823-3c3e4f3 2025-08-23 11:58:26 -04:00
3c3e4f36d6 update version to 20250823-e9c3d97 2025-08-23 11:58:26 -04:00
e9c3d97a3c update version to 20250823-e0612e9 2025-08-23 11:58:26 -04:00
e0612e94b4 update version to 20250823-8126b97 2025-08-23 11:58:26 -04:00
8126b97ad0 update version to 20250823-908e6bc 2025-08-23 11:58:26 -04:00
908e6bc659 update version to 20250823-b6d85af 2025-08-23 11:58:25 -04:00
b6d85af756 update version to 20250823-e859589 2025-08-23 11:58:25 -04:00
e85958999e update version to 20250823-f50624f 2025-08-23 11:58:25 -04:00
f50624f6a1 update version to 20250823-1c8e33f 2025-08-23 11:58:25 -04:00
1c8e33f7a6 update version to 20250823-ee73b31 2025-08-23 11:58:25 -04:00
ee73b31743 update version to 20250823-ea2f22e 2025-08-23 11:58:25 -04:00
ea2f22eb13 update version to 20250823-94bb32e 2025-08-23 11:58:25 -04:00
94bb32e094 update version to 20250823-b9229c3 2025-08-23 11:58:25 -04:00
b9229c3d5f update version to 20250823-1c411b7 2025-08-23 11:58:25 -04:00
1c411b7c03 update version to 20250823-6bf5dc2 2025-08-23 11:58:25 -04:00
6bf5dc2606 update version to 20250823-cd1c8bf 2025-08-23 11:58:25 -04:00
cd1c8bf38b update version to 20250823-359316b 2025-08-23 11:58:24 -04:00
359316b0a8 update version to 20250823-fc3458e 2025-08-23 11:58:24 -04:00
fc3458e772 update version to 20250823-51fe2f2 2025-08-23 11:58:24 -04:00
51fe2f2ecf update version to 20250823-b0e4066 2025-08-23 11:58:24 -04:00
b0e4066d44 update version to 20250823-c2dec55 2025-08-23 11:58:24 -04:00
c2dec55fbf update version to 20250823-57eb974 2025-08-23 11:58:24 -04:00
57eb974ef7 update version to 20250823-e2064e3 2025-08-23 11:58:24 -04:00
e2064e3bad update version to 20250823-7c76668 2025-08-23 11:58:24 -04:00
7c76668a62 update version to 20250823-13a9bc8 2025-08-23 11:58:24 -04:00
13a9bc816a update version to 20250823-48775fd 2025-08-23 11:58:24 -04:00
48775fd221 update version to 20250823-ed71403 2025-08-23 11:58:24 -04:00
ed714037fa update version to 20250823-4d98ba1 2025-08-23 11:58:24 -04:00
4d98ba1b8b update version to 20250823-10d6f4b 2025-08-23 11:58:23 -04:00
10d6f4b3c1 update version to 20250823-167e8be 2025-08-23 11:58:23 -04:00
167e8beed3 update version to 20250823-116164f 2025-08-23 11:58:23 -04:00
116164fc6a update version to 20250823-f7db719 2025-08-23 11:58:23 -04:00
f7db719351 update version to 20250823-f3fe6ba 2025-08-23 11:58:23 -04:00
f3fe6baf54 update version to 20250823-823b449 2025-08-23 11:58:23 -04:00
823b44985f update version to 20250823-917fc11 2025-08-23 11:58:23 -04:00
917fc1131d update version to 20250823-1081ae9 2025-08-23 11:58:23 -04:00
1081ae9062 update version to 20250823-04a6e1e 2025-08-23 11:58:23 -04:00
04a6e1e9e6 update version to 20250823-f8c2786 2025-08-23 11:58:23 -04:00
f8c2786e41 update version to 20250823-be2abec 2025-08-23 11:58:23 -04:00
be2abec834 update version to 20250823-77deca0 2025-08-23 11:58:23 -04:00
77deca0e1c update version to 20250823-7df0ab2 2025-08-23 11:58:22 -04:00
7df0ab2c4b update version to 20250823-76295c1 2025-08-23 11:58:22 -04:00
76295c1522 update version to 20250823-d788770 2025-08-23 11:58:22 -04:00
d788770777 update version to 20250823-48fa969 2025-08-23 11:58:22 -04:00
48fa969dfd update version to 20250823-8abdb1f 2025-08-23 11:58:22 -04:00
8abdb1f86f update version to 20250823-d2124b8 2025-08-23 11:58:22 -04:00
d2124b8cd4 update version to 20250823-dccd25a 2025-08-23 11:58:22 -04:00
dccd25ab1b update version to 20250823-b20ce5a 2025-08-23 11:58:22 -04:00
b20ce5abc3 update version to 20250823-a9cafaf 2025-08-23 11:58:22 -04:00
a9cafafe81 update version to 20250823-7bbdd62 2025-08-23 11:58:22 -04:00
7bbdd621f7 update version to 20250823-e45123f 2025-08-23 11:58:22 -04:00
e45123fbb6 update version to 20250823-a96abee 2025-08-23 11:58:22 -04:00
a96abeeb96 update version to 20250823-9e544e5 2025-08-23 11:58:21 -04:00
9e544e555e update version to 20250823-81ccb0d 2025-08-23 11:58:21 -04:00
81ccb0db07 update version to 20250823-84d8ad9 2025-08-23 11:58:21 -04:00
84d8ad9075 update version to 20250823-b599d42 2025-08-23 11:58:21 -04:00
b599d4295b update version to 20250823-d510b45 2025-08-23 11:58:21 -04:00
d510b451f1 update version to 20250823-6d1f309 2025-08-23 11:58:21 -04:00
6d1f30928a update version to 20250823-81c0bcb 2025-08-23 11:58:21 -04:00
81c0bcb727 update version to 20250823-365e4e6 2025-08-23 11:58:21 -04:00
365e4e6425 update version to 20250823-ee6c83b 2025-08-23 11:58:21 -04:00
ee6c83bd87 update version to 20250823-5e89975 2025-08-23 11:58:21 -04:00
5e89975e71 update version to 20250823-0fa430b 2025-08-23 11:58:21 -04:00
0fa430b2c4 update version to 20250823-a61dbc0 2025-08-23 11:58:21 -04:00
a61dbc09fb update version to 20250823-0385fb9 2025-08-23 11:58:21 -04:00
0385fb9cb4 update version to 20250823-82f77a3 2025-08-23 11:58:20 -04:00
82f77a3ab8 update version to 20250823-a0487a1 2025-08-23 11:58:20 -04:00
a0487a1188 update version to 20250823-678a8c7 2025-08-23 11:58:20 -04:00
678a8c7f08 update version to 20250823-3146e62 2025-08-23 11:58:20 -04:00
3146e62f08 update version to 20250823-66c8ced 2025-08-23 11:58:20 -04:00
66c8ceddab update version to 20250823-ec42eb9 2025-08-23 11:58:20 -04:00
ec42eb9f12 update version to 20250823-9249ee5 2025-08-23 11:58:20 -04:00
9249ee5238 update version to 20250823-4467605 2025-08-23 11:58:20 -04:00
4467605fb1 update version to 20250823-dd6fe7c 2025-08-23 11:58:20 -04:00
dd6fe7cdc3 update version to 20250823-53146af 2025-08-23 11:58:20 -04:00
53146af51e update version to 20250823-66a33b6 2025-08-23 11:58:20 -04:00
66a33b6730 update version to 20250823-c2119ba 2025-08-23 11:58:20 -04:00
c2119ba4fd update version to 20250823-950cd89 2025-08-23 11:58:20 -04:00
950cd89676 update version to 20250823-0d640fe 2025-08-23 11:58:19 -04:00
0d640fef64 update version to 20250823-34b3f43 2025-08-23 11:58:19 -04:00
34b3f43975 update version to 20250823-a4fd8db 2025-08-23 11:58:19 -04:00
a4fd8dbcd6 update version to 20250823-896f4f1 2025-08-23 11:58:19 -04:00
896f4f1ef4 update version to 20250823-845e4ce 2025-08-23 11:58:19 -04:00
845e4ce2e2 update version to 20250823-7949dcc 2025-08-23 11:58:19 -04:00
7949dcc8d3 update version to 20250823-8fb6536 2025-08-23 11:58:19 -04:00
8fb653669a update version to 20250823-f529405 2025-08-23 11:58:19 -04:00
f5294059fc update version to 20250823-1dbb487 2025-08-23 11:58:19 -04:00
1dbb4876e5 update version to 20250823-324bc1b 2025-08-23 11:58:19 -04:00
324bc1b21e update version to 20250823-0920212 2025-08-23 11:58:19 -04:00
092021245f update version to 20250823-f8589b6 2025-08-23 11:58:19 -04:00
f8589b6d67 update version to 20250823-81b76d8 2025-08-23 11:58:19 -04:00
81b76d8d95 update version to 20250823-27d91fd 2025-08-23 11:58:19 -04:00
27d91fde33 update version to 20250823-daa87f6 2025-08-23 11:58:18 -04:00
daa87f668e update version to 20250823-8d85d9e 2025-08-23 11:58:18 -04:00
8d85d9e7c5 update version to 20250823-cc78fa1 2025-08-23 11:58:18 -04:00
cc78fa13cf update version to 20250823-068c38b 2025-08-23 11:58:18 -04:00
068c38b794 update version to 20250823-51d553f 2025-08-23 11:58:18 -04:00
51d553f838 update version to 20250823-b144c18 2025-08-23 11:58:18 -04:00
b144c18a52 update version to 20250823-ae3d7ee 2025-08-23 11:58:18 -04:00
ae3d7eec6e update version to 20250823-6ea02dd 2025-08-23 11:58:18 -04:00
6ea02dd299 update version to 20250823-6cb1fe9 2025-08-23 11:58:18 -04:00
6cb1fe9a8d update version to 20250823-1c2eb47 2025-08-23 11:58:18 -04:00
1c2eb47161 update version to 20250823-8720ed2 2025-08-23 11:58:18 -04:00
8720ed212c update version to 20250823-c18fabb 2025-08-23 11:58:18 -04:00
c18fabb81d update version to 20250823-b2f90bd 2025-08-23 11:58:18 -04:00
b2f90bd458 update version to 20250823-ab1487b 2025-08-23 11:58:18 -04:00
ab1487b031 update version to 20250823-e6898c9 2025-08-23 11:58:17 -04:00
e6898c9801 update version to 20250823-494cdaa 2025-08-23 11:58:17 -04:00
494cdaa48b update version to 20250823-7e8334d 2025-08-23 11:58:17 -04:00
7e8334dffc added version.txt 2025-08-23 11:58:10 -04:00
17 changed files with 396 additions and 383 deletions

1
.gitattributes vendored
View File

@@ -1 +0,0 @@
*.sh text eol=lf

View File

@@ -1,72 +1,70 @@
name: push new builds to gitea, dockerhub
on:
push:
branches: ["master"]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get youdis version
id: youdis
run: |
YOUDIS_VER=$(cat youdis-version.txt)
if [ -z "$YOUDIS_VER" ]; then
echo "youdis version empty" >&2
exit 1
fi
echo "youdis-version=$YOUDIS_VER" >> $GITHUB_OUTPUT
echo "shortsha=${GITHUB_SHA::5}" >> $GITHUB_OUTPUT
- name: Decide if build needed
id: check_build
run: |
SHOULD_BUILD=false
if [ "${{ steps.youdis.outputs.youdis-version }}" != "$(docker inspect --format='{{ index .Config.Labels "YOUDIS_VERSION" }}' eulaly/youdis:latest || echo none)" ]; then
SHOULD_BUILD=true
fi
echo "should_build=$SHOULD_BUILD" >> $GITHUB_OUTPUT
- name: Login to Dockerhub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push to Dockerhub
if: ${{ steps.check_build.outputs.should_build == 'true' }}
uses: docker/build-push-action@v5
with:
push: true
tags: |
eulaly/youdis:latest
eulaly/youdis:${{ steps.youdis.outputs.shortsha }}
labels: |
YOUDIS_VERSION=${{ steps.youdis.outputs.youdis-version }}
- name: Login to Gitea
uses: docker/login-action@v3
with:
registry: git.hgsky.me
username: ${{ secrets.USERNAME }}
password: ${{ secrets.YOUDIS_PASSWORD }}
- name: Build and push to Gitea
if: ${{ steps.check_build.outputs.should_build == 'true' }}
uses: docker/build-push-action@v5
with:
push: true
tags: |
git.hgsky.me/ben/youdis:latest
git.hgsky.me/ben/youdis:${{ steps.youdis.outputs.shortsha }}
labels: |
YOUDIS_VERSION=${{ steps.youdis.outputs.youdis-version }}
- name: link container in gitea
run: |
curl -X POST -H "Authorization: token ${{ secrets.SUPER }}" https://git.hgsky.me/api/v1/packages/ben/container/youdis/-/link/youdis
name: build + push containers to gitea (every) and docker (daily)
on:
push:
branches: [ "master" ]
schedule:
- cron: '0 0 * * 0' # weekly check
workflow_dispatch:
jobs:
dockerhub:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get current version
id: current
run: |
CURRENT=$(cat version.txt || echo "none")
echo "version=$CURRENT" >> $GITHUB_OUTPUT
- name: check latest yt-dlp release
id: latest
run: |
LATEST=$(curl -s https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest | jq -r .tag_name || echo "error")
if [ "$LATEST" == "error" ]; then
echo "failed to fetch latest version" >&2
exit 1
fi
echo "yt-dlp version=$LATEST" >> $GITHUB_OUTPUT
- name: Update version file if changed
run: echo ${{ steps.latest.outputs.version }} > version.txt
if: ${{ steps.current.outputs.version != steps.latest.outputs.version }}
- name: Login to Dockerhub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push to Dockerhub
uses: docker/build-push-action@v5
with:
push: true
tags: |
eulaly/youdis:latest
eulaly/youdis:${{ steps.latest.outputs.version }}
if: ${{ steps.current.outputs.version != steps.latest.outputs.version }}
gitea:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Gitea (local)
uses: docker/login-action@v2
with:
registry: git.hgsky.me
username: ${{ secrets.GITEA_USERNAME }}
password: ${{ secrets.GITEA_TOKEN }}
- name: Build and push to Gitea (all pushes)
uses: docker/build-push-action@v5
with:
push: true
tags: |
git.hgsky.me/eulaly/youdis:latest
git.hgsky.me/eulaly/youdis:dev-${{ github.sha }}

44
.github/workflows/docker-build.yaml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Check yt-dlp and rebuild if new
on:
schedule:
- cron: '0 0 * * 0'
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get current version
id: current
run: |
CURRENT=$(cat version.txt || echo "none")
echo "version=$CURRENT" >> $GITHUB_OUTPUT
- name: Check latest version
id: latest
run: |
LATEST=$(curl -s https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest | jq -r .tag_name || echo "error")
if [ "$LATEST" == "error" ]; then
echo "failed to fetch latest version" >&2
exit 1
fi
echo "version=$LATEST" >> $GITHUB_OUTPUT
- name: Update version file if changed
run: echo ${{ steps.latest.outputs.version }} > version.txt
if: ${{ steps.current.outputs.version != steps.latest.outputs.version }}
- name: Login to Dockerhub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push docker image
uses: docker/build-push-action@v5
with:
push: true
tags: eulaly/youdis:latest,eulaly/youdis:${{ steps.latest.outputs.version }}
if: ${{ steps.current.outputs.version != steps.latest.outputs.version }}

24
.gitignore vendored
View File

@@ -1,13 +1,13 @@
*.pyc
*.org
*.ps1
.env
#*
.#*
__pycache__/
venv/
archive/
config/
downloads/
data.json
*.pyc
*.org
*.ps1
.env
#*
.#*
__pycache__/
venv/
archive/
config/
downloads/
data.json

48
LICENSE
View File

@@ -1,24 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

View File

@@ -1,8 +1,8 @@
build and run the docker container
```
api_token = [discord bot token]
-v [downloads]:/downloads
-v [config]:/config
```
config contains data to persist across container updates, i.e., unraid appdata,
such as users.json (authorized users) and yt-dlp's archive.txt
build and run the docker container
```
api_token = [discord bot token]
-v [downloads]:/downloads
-v [config]:/config
```
config contains data to persist across container updates, i.e., unraid appdata,
such as users.json (authorized users) and yt-dlp's archive.txt

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

8
create-ytdlp-config.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
#copy yt-dlp.conf if missing
mkdir -p /config
if [ ! -f /config/yt-dlp.conf ]; then
cp /app/default-yt-dlp.conf /config/yt-dlp.conf
fi
exec "$@"

View File

@@ -1,11 +1,11 @@
# yt-dlp config file (yt-dlp.conf or .config/yt-dlp/config)
--simulate
-f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best"
--embed-chapters
--embed-info-json
--write-playlist-metafiles
#--paths "{"home":"./downloads"}"
--download-archive "/config/archive.txt"
--restrict-filenames
--output "/downloads/%(uploader)s/%(playlist_title)s/%(playlist_index)s%(playlist_index& - )s%(title)s.%(ext)s"
# yt-dlp config file (yt-dlp.conf or .config/yt-dlp/config)
--simulate
-f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best"
--embed-chapters
--embed-info-json
--write-playlist-metafiles
#--paths "{"home":"./downloads"}"
--download-archive "/config/archive.txt"
--restrict-filenames
--output "/downloads/%(uploader)s/%(playlist_title)s/%(playlist_index)s%(playlist_index& - )s%(title)s.%(ext)s"
# --split-chapters

View File

@@ -1,16 +1,14 @@
# syntax=docker/dockerfile:1
FROM python:3.12.1-alpine3.18
WORKDIR /app
RUN apk update && \
apk add --no-cache build-base ffmpeg && \
rm -rf /var/cache/apk/*
COPY requirements.txt .
RUN python3 -m pip install --no-cache-dir -r requirements.txt
COPY youdis.py default-yt-dlp.conf update-ytdlp.sh run-youdis.sh /app/
RUN chmod +x /app/update-ytdlp.sh /app/run-youdis.sh
COPY weekly-restart /etc/cron.d/
RUN chmod 0644 /etc/cron.d/weekly-restart
ENTRYPOINT ["/app/run-youdis.sh"]
# syntax=docker/dockerfile:1
FROM python:3.12.1-alpine3.18
WORKDIR /app
RUN apk update && \
apk add --no-cache build-base ffmpeg && \
rm -rf /var/cache/apk/*
COPY requirements.txt requirements.txt
RUN python3 -m pip install --no-cache-dir -r requirements.txt
COPY youdis.py youdis.py
COPY default-yt-dlp.conf /app/default-yt-dlp.conf
COPY create-ytdlp-config.sh /app/create-ytdlp-config.sh
RUN chmod 755 /app/create-ytdlp-config.sh
ENTRYPOINT ["/app/create-ytdlp-config.sh"]
CMD ["python3", "youdis.py"]

View File

@@ -1,21 +0,0 @@
#!/bin/sh
set -e
#start crond in bg
crond -l 2
echo "checking for /config/yt-dlp.conf"
#copy yt-dlp.conf if missing
mkdir -p /config
if [ ! -f /config/yt-dlp.conf ]; then
echo "yt-dlp.conf not found, setting default"
cp /app/default-yt-dlp.conf /config/yt-dlp.conf
echo "created yt-dlp.conf"
fi
python3 -m pip install --no-cache-dir --upgrade --pre "yt-dlp[default]"
VERSION=$(python3 -m pip show yt-dlp 2>/dev/null | awk '/Version:/ {print $2}')
echo "updated yt-dlp to $VERSION"
echo "starting youdis"
exec python3 /app/youdis.py

View File

@@ -1,18 +1,18 @@
<?xml version="1.0"?>
<Container version="2">
<Name>youdis</Name>
<Repository>eulaly/youdis</Repository>
<Registry>https://hub.docker.com/r/eulaly/youdis/</Registry>
<Network>bridge</Network>
<Shell>sh</Shell>
<Privileged>false</Privileged>
<Support>[Unraid Support Thread]</Support>
<Project>https://github.com/eulaly/youdis</Project>
<Overview>Discord bot-based wrapper for yt-dlp. Let your friends download videos to your server! Supports playlists, requires a configured Discord bot.</Overview>
<Category>Downloaders: Tools:</Category>
<TemplateURL>https://raw.githubusercontent.com/eulaly/unraid-templates/refs/heads/master/unraid-ca-template.xml</TemplateURL>
<Icon>https://github.com/eulaly/youdis/blob/c978a2326984efa9670678687ed1a1473478d753/yt_dlp.png</Icon>
<Config Name="api_token" Target="api_token" Default="" Mode="" Description="Discord bot token" Type="Variable" Display="always" Required="true" Mask="true"/>
<Config Name="Downloads" Target="/downloads" Default="" Mode="rw" Description="Video download location" Type="Path" Display="always" Required="false"/>
<Config Name="Config" Target="/config" Default="/mnt/user/appdata/youdis/config" Mode="rw" Description="Config location (archive.txt, users.json)" Type="Path" Display="always" Required="false"/>
</Container>
<?xml version="1.0"?>
<Container version="2">
<Name>youdis</Name>
<Repository>eulaly/youdis</Repository>
<Registry>https://hub.docker.com/r/eulaly/youdis/</Registry>
<Network>bridge</Network>
<Shell>sh</Shell>
<Privileged>false</Privileged>
<Support>[Unraid Support Thread]</Support>
<Project>https://github.com/eulaly/youdis</Project>
<Overview>Discord bot-based wrapper for yt-dlp. Let your friends download videos to your server! Supports playlists, requires a configured Discord bot.</Overview>
<Category>Downloaders: Tools:</Category>
<TemplateURL>https://raw.githubusercontent.com/eulaly/unraid-templates/refs/heads/master/unraid-ca-template.xml</TemplateURL>
<Icon>https://github.com/eulaly/youdis/blob/c978a2326984efa9670678687ed1a1473478d753/yt_dlp.png</Icon>
<Config Name="api_token" Target="api_token" Default="" Mode="" Description="Discord bot token" Type="Variable" Display="always" Required="true" Mask="true"/>
<Config Name="Downloads" Target="/downloads" Default="" Mode="rw" Description="Video download location" Type="Path" Display="always" Required="false"/>
<Config Name="Config" Target="/config" Default="/mnt/user/appdata/youdis/config" Mode="rw" Description="Config location (archive.txt, users.json)" Type="Path" Display="always" Required="false"/>
</Container>

View File

@@ -1,12 +0,0 @@
#!/bin/sh
set -e
echo "updating yt-dlp"
echo "killing youdis"
pkill -f youdis.py || true
python3 -m pip install --no-cache-dir --upgrade --pre "yt-dlp[default]"
VERSION=$(python3 -m pip show yt-dlp 2>/dev/null | awk '/Version:/ {print $2}')
echo "updated yt-dlp to $VERSION"
echo "restarting youdis"
python3 /app/youdis.py &

1
version.txt Normal file
View File

@@ -0,0 +1 @@
20250823-d707074

View File

@@ -1 +0,0 @@
0 3 * * 0 root /app/update-ytdlp.sh

View File

@@ -1 +0,0 @@
20250829-ec72c56

314
youdis.py
View File

@@ -1,157 +1,157 @@
#!/usr/bin/env python3
'''
youdis v1.1
bot for downloading youtube videos using yt-dlp
discord-py-interactions 5.11.0 has new option
requires python>=3.9
'''
# match_filter: info_dict -> Raise utils.DownloadCancelled(msg) ? interrupt
import interactions
from os import getenv
from pathlib import Path
import yt_dlp
import json
import asyncio
import threading
userFile = Path('/config/users.json')
userFile.touch(exist_ok=True)
bot = interactions.Client(intents=interactions.Intents.DEFAULT,default_scope=2147491904)
userFile.parent.mkdir(exist_ok=True, parents=True)
try:
with open(userFile, 'x') as f:
print(f'users.json not found; saving to {userFile}')
except FileExistsError:
with open(userFile, 'r') as f:
authorized_users = json.load(f).get('authorized_users')
print(f'authorized_users:{authorized_users}')
title = ''
async def send_message(ctx, message):
await ctx.author.send(message)
def download_video(url, options):
with yt_dlp.YoutubeDL(options) as ydl:
ydl.download(url)
def create_hook(ctx,loop):
def hook(d):
global title
status = d.get('status')
if status == 'error':
msg = f'error; video probably already exists, have you checked archive.txt'
asyncio.run_coroutine_threadsafe(send_message(ctx,msg),loop)
elif d.get('info_dict').get('title') != title:
title = d.get('info_dict').get('title')
playlist_index = d.get('info_dict').get('playlist_index')
playlist_count = d.get('info_dict').get('playlist_count')
filename = d.get('filename')
url = d.get('info_dict').get('webpage_url')
msg = f'{status} {playlist_index} of {playlist_count}: {filename} <{url}>'
asyncio.run_coroutine_threadsafe(send_message(ctx,msg),loop)
return hook
@interactions.slash_command(name="youtube",description="download video from youtube to server")
@interactions.slash_option(
name='url',
opt_type=interactions.OptionType.STRING,
required=True,
description='url target'
)
async def youtube(ctx: interactions.SlashContext, url:str):
print(f'{ctx.author.id} requested {url}')
loop = asyncio.get_running_loop()
hook = create_hook(ctx,loop)
msg = ''
# use api_to_cli and paste cli options to get the output you need
yoptions = {
'format':'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
'fragment_tries': 10,
'restrictfilenames':True,
'paths': {'home':'/downloads'},
'retries':10,
'writeinfojson':False,
'allow_playlist_files':True,
'noplaylist':True,
'download_archive':'/config/archive.txt',
'progress_hooks':[hook],
'outtmpl': '%(uploader)s/%(playlist_title)s/%(playlist_index)s%(playlist_index& - )s%(title)s.%(ext)s',
'outtmpl_na_placeholder':'',
}
# check that user is authorized
if ctx.author.id not in authorized_users:
if ctx.author.id == 127831327012683776:
await ctx.author.send('potato stop')
await ctx.author.send('you are not authorized to use this command. message my owner to be added.')
return
else:
await ctx.channel.send(f'Downloading from <{url}>. Status updates via DM.')
#await ctx.defer() #if you need up to 15m to respond
# 1/2 - download in separate thread, else progress_hook blocks downstream async ctx.send
download_thread = threading.Thread(target=download_video, args=(url,yoptions))
download_thread.start()
await asyncio.to_thread(download_thread.join)
# 2/2 - replace the above with this next try:
#try:
# await asyncio.to_thread(download_video, url, yoptions)
#except Exception as e:
# print(f"download failed: {e}")
# await ctx.author.send(f"download failed: {str(e)}")
@interactions.slash_command(name="interrupt",description="cancel current job")
@interactions.check(interactions.is_owner())
async def _interrupt(ctx):
# interrupt here
print('interrupting current job - not implemented')
await ctx.author.send('interrupting current job - not implemented')
@interactions.slash_command(name="adduser",description="authorize target user")
@interactions.slash_option(
name="user",
opt_type=interactions.OptionType.USER,
required=True,
description='enable this bot for target user',
)
@interactions.check(interactions.is_owner())
async def _adduser(ctx: interactions.SlashContext, user:interactions.OptionType.USER):
if str(user.id) not in authorized_users:
authorized_users.append(str(user.id))
with open(userFile,'w') as f: #overwrite file - fix later if other params come up
json.dump({'authorized_users':authorized_users})
print('react:checkmark')
await ctx.message.add_reaction('')
@interactions.slash_command(name="removeuser",description="deauthorize target user")
@interactions.slash_option(
name="user",
opt_type=interactions.OptionType.USER,
required=True,
description='disable this bot for target user',
)
@interactions.check(interactions.is_owner())
async def _removeuser(ctx: interactions.SlashContext, user:interactions.OptionType.USER):
if str(user.id) in authorized_users:
# ? ? ? fix pls
i = index(authorized_users(str(user.id)))
# update list, rewrite json
print('react:checkmark')
await ctx.message.add_reaction('')
async def dl_hook(d):
msg = f'{d["status"]} {d["filename"]}'
print(msg)
await ctx.author.send(msg)
api_token = getenv('api_token')
if not api_token:
raise ValueError('API token not set. Retrieve from your Discord bot.')
bot.start(api_token)
#!/usr/bin/env python3
'''
youdis v1.1
bot for downloading youtube videos using yt-dlp
discord-py-interactions 5.11.0 has new option
requires python>=3.9
'''
# match_filter: info_dict -> Raise utils.DownloadCancelled(msg) ? interrupt
import interactions
from os import getenv
from pathlib import Path
import yt_dlp
import json
import asyncio
import threading
userFile = Path('/config/users.json')
userFile.touch(exist_ok=True)
bot = interactions.Client(intents=interactions.Intents.DEFAULT,default_scope=2147491904)
userFile.parent.mkdir(exist_ok=True, parents=True)
try:
with open(userFile, 'x') as f:
print(f'users.json not found; saving to {userFile}')
except FileExistsError:
with open(userFile, 'r') as f:
authorized_users = json.load(f).get('authorized_users')
print(f'authorized_users:{authorized_users}')
title = ''
async def send_message(ctx, message):
await ctx.author.send(message)
def download_video(url, options):
with yt_dlp.YoutubeDL(options) as ydl:
ydl.download(url)
def create_hook(ctx,loop):
def hook(d):
global title
status = d.get('status')
if status == 'error':
msg = f'error; video probably already exists, have you checked archive.txt'
asyncio.run_coroutine_threadsafe(send_message(ctx,msg),loop)
elif d.get('info_dict').get('title') != title:
title = d.get('info_dict').get('title')
playlist_index = d.get('info_dict').get('playlist_index')
playlist_count = d.get('info_dict').get('playlist_count')
filename = d.get('filename')
url = d.get('info_dict').get('webpage_url')
msg = f'{status} {playlist_index} of {playlist_count}: {filename} <{url}>'
asyncio.run_coroutine_threadsafe(send_message(ctx,msg),loop)
return hook
@interactions.slash_command(name="youtube",description="download video from youtube to server")
@interactions.slash_option(
name='url',
opt_type=interactions.OptionType.STRING,
required=True,
description='url target'
)
async def youtube(ctx: interactions.SlashContext, url:str):
print(f'{ctx.author.id} requested {url}')
loop = asyncio.get_running_loop()
hook = create_hook(ctx,loop)
msg = ''
# use api_to_cli and paste cli options to get the output you need
yoptions = {
'format':'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
'fragment_tries': 10,
'restrictfilenames':True,
'paths': {'home':'/downloads'},
'retries':10,
'writeinfojson':False,
'allow_playlist_files':True,
'noplaylist':True,
'download_archive':'/config/archive.txt',
'progress_hooks':[hook],
'outtmpl': '%(uploader)s/%(playlist_title)s/%(playlist_index)s%(playlist_index& - )s%(title)s.%(ext)s',
'outtmpl_na_placeholder':'',
}
# check that user is authorized
if ctx.author.id not in authorized_users:
if ctx.author.id == 127831327012683776:
await ctx.author.send('potato stop')
await ctx.author.send('you are not authorized to use this command. message my owner to be added.')
return
else:
await ctx.channel.send(f'Downloading from <{url}>. Status updates via DM.')
#await ctx.defer() #if you need up to 15m to respond
# 1/2 - download in separate thread, else progress_hook blocks downstream async ctx.send
download_thread = threading.Thread(target=download_video, args=(url,yoptions))
download_thread.start()
await asyncio.to_thread(download_thread.join)
# 2/2 - replace the above with this next try:
#try:
# await asyncio.to_thread(download_video, url, yoptions)
#except Exception as e:
# print(f"download failed: {e}")
# await ctx.author.send(f"download failed: {str(e)}")
@interactions.slash_command(name="interrupt",description="cancel current job")
@interactions.check(interactions.is_owner())
async def _interrupt(ctx):
# interrupt here
print('interrupting current job - not implemented')
await ctx.author.send('interrupting current job - not implemented')
@interactions.slash_command(name="adduser",description="authorize target user")
@interactions.slash_option(
name="user",
opt_type=interactions.OptionType.USER,
required=True,
description='enable this bot for target user',
)
@interactions.check(interactions.is_owner())
async def _adduser(ctx: interactions.SlashContext, user:interactions.OptionType.USER):
if str(user.id) not in authorized_users:
authorized_users.append(str(user.id))
with open(userFile,'w') as f: #overwrite file - fix later if other params come up
json.dump({'authorized_users':authorized_users})
print('react:checkmark')
await ctx.message.add_reaction('')
@interactions.slash_command(name="removeuser",description="deauthorize target user")
@interactions.slash_option(
name="user",
opt_type=interactions.OptionType.USER,
required=True,
description='disable this bot for target user',
)
@interactions.check(interactions.is_owner())
async def _removeuser(ctx: interactions.SlashContext, user:interactions.OptionType.USER):
if str(user.id) in authorized_users:
# ? ? ? fix pls
i = index(authorized_users(str(user.id)))
# update list, rewrite json
print('react:checkmark')
await ctx.message.add_reaction('')
async def dl_hook(d):
msg = f'{d["status"]} {d["filename"]}'
print(msg)
await ctx.author.send(msg)
api_token = getenv('api_token')
if not api_token:
raise ValueError('API token not set. Retrieve from your Discord bot.')
bot.start(api_token)