diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index bb54181..d1ea37d 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -2,52 +2,14 @@ name: Build Release Packages on: push: - branches: - - main - #tags: - #- "v*" + #branches: + #- main + tags: + - "v*.*.*" jobs: - determine-next-tag: - runs-on: ubuntu-latest - outputs: - next_tag: ${{ steps.set_tag.outputs.next_tag }} - steps: - - name: Checkout code - uses: https://gitea.com/actions/checkout@v5 - - - name: Determine next version tag - id: set_tag - run: | - git fetch --tags - - LATEST_TAG=$(git tag -l "v*.*.*" | sort -V | tail -n1) - echo "Latest tag: $LATEST_TAG" - - if [ -z "$LATEST_TAG" ]; then - NEXT_TAG="v0.0.1" - else - PATCH=$(echo $LATEST_TAG | awk -F. '{print $3}') - MAJOR=$(echo $LATEST_TAG | awk -F. '{print $1}' | tr -d 'v') - MINOR=$(echo $LATEST_TAG | awk -F. '{print $2}') - PATCH=$((PATCH+1)) - NEXT_TAG="v${MAJOR}.${MINOR}.${PATCH}" - fi - - echo "Next tag: $NEXT_TAG" - echo "next_tag=$NEXT_TAG" >> $GITHUB_OUTPUT - - - name: Create new tag - run: | - git config user.name "Gitea Actions" - git config user.email "actions@gitea.local" - git tag ${{ steps.set_tag.outputs.next_tag }} - git push origin ${{ steps.set_tag.outputs.next_tag }} - - windows-package: runs-on: ubuntu-latest - needs: determine-next-tag steps: - name: Checkout code uses: https://gitea.com/actions/checkout@v5 @@ -63,46 +25,60 @@ jobs: sudo apt update sudo apt install -y unzip zip curl + - name: Extract tag name + run: | + REF="${GITEA_REF:-$GITHUB_REF}" + TAG="${REF#refs/tags/}" + echo "TAG=$TAG" >> $GITHUB_ENV + - name: Prepare Windows package run: | set -e - mkdir -p dist/windows - cp yt-playlist-main.py dist/windows/ - cp yt-playlist-config.json dist/windows/ + WORKSPACE_ROOT="${GITEA_WORKSPACE:-$PWD}" + mkdir -p "$WORKSPACE_ROOT/dist/windows" + cp "$WORKSPACE_ROOT/yt-playlist-main.py" "$WORKSPACE_ROOT/dist/windows/" + cp "$WORKSPACE_ROOT/yt-playlist-config.json" "$WORKSPACE_ROOT/dist/windows/" - mkdir -p dist/windows/bin + mkdir -p "$WORKSPACE_ROOT/dist/windows/bin" # yt-dlp - curl -L -o dist/windows/bin/yt-dlp.exe https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe + curl -L -o "$WORKSPACE_ROOT/dist/windows/bin/yt-dlp.exe" https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe # FFmpeg Windows static - curl -L -o dist/windows/ffmpeg.zip https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip - unzip -q dist/windows/ffmpeg.zip -d dist/windows/ffmpeg_temp - mv $(find dist/windows/ffmpeg_temp -name ffmpeg.exe | head -n 1) dist/windows/bin/ffmpeg.exe + curl -L -o "$WORKSPACE_ROOT/dist/windows/ffmpeg.zip" https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip + unzip -q "$WORKSPACE_ROOT/dist/windows/ffmpeg.zip" -d "$WORKSPACE_ROOT/dist/windows/ffmpeg_temp" + mv $(find "$WORKSPACE_ROOT/dist/windows/ffmpeg_temp" -name ffmpeg.exe | head -n 1) "$WORKSPACE_ROOT/dist/windows/bin/ffmpeg.exe" # aria2c Windows static - curl -L -o dist/windows/aria2c.zip https://github.com/aria2/aria2/releases/download/release-1.37.0/aria2-1.37.0-win-64bit-build1.zip - unzip dist/windows/aria2c.zip -d dist/windows/ - mv dist/windows/aria2-1.37.0-win-64bit-build1/aria2c.exe dist/windows/bin/aria2c.exe + curl -L -o "$WORKSPACE_ROOT/dist/windows/aria2c.zip" https://github.com/aria2/aria2/releases/download/release-1.37.0/aria2-1.37.0-win-64bit-build1.zip + unzip "$WORKSPACE_ROOT/dist/windows/aria2c.zip" -d "$WORKSPACE_ROOT/dist/windows/" + mv "$WORKSPACE_ROOT/dist/windows/aria2-1.37.0-win-64bit-build1/aria2c.exe" "$WORKSPACE_ROOT/dist/windows/bin/aria2c.exe" # Remove temp files before zipping - rm -rf dist/windows/ffmpeg_temp dist/windows/aria2-1.37.0-win-64bit-build1 dist/windows/ffmpeg.zip dist/windows/aria2c.zip dist/windows/aria2c.zip + rm -rf "$WORKSPACE_ROOT/dist/windows/ffmpeg_temp" "$WORKSPACE_ROOT/dist/windows/aria2-1.37.0-win-64bit-build1" "$WORKSPACE_ROOT/dist/windows/ffmpeg.zip" "$WORKSPACE_ROOT/dist/windows/aria2c.zip" - # Zip everything - cd dist/windows - zip -r ../../yt-playlist-windows-${{ needs.determine-next-tag.outputs.next_tag }}.zip * - cd ../.. + cd "$WORKSPACE_ROOT/dist/windows" + ZIP_NAME="yt-playlist-windows-${TAG}.zip" + zip -r "$WORKSPACE_ROOT/$ZIP_NAME" * + echo "ZIP_PATH=$WORKSPACE_ROOT/$ZIP_NAME" >> $GITHUB_ENV - name: Upload Windows release uses: https://gitea.com/actions/gitea-release-action@v1 with: - files: yt-playlist-windows-${{ needs.determine-next-tag.outputs.next_tag }}.zip - tag_name: ${{ needs.determine-next-tag.outputs.next_tag }} - name: ${{ needs.determine-next-tag.outputs.next_tag }} + files: ${{ env.ZIP_PATH }} + tag_name: ${{ env.TAG }} + name: ${{ env.TAG }} + + + + + + + + linux-package: runs-on: ubuntu-latest - needs: determine-next-tag steps: - name: Checkout code uses: https://gitea.com/actions/checkout@v5 @@ -116,10 +92,15 @@ jobs: run: | pip install --upgrade pip sudo apt update - sudo apt install -y \ - unzip zip curl wget build-essential pkg-config \ - autoconf automake libtool gettext autopoint \ - libssl-dev libxml2-dev zlib1g-dev libsqlite3-dev + sudo apt install -y unzip zip curl wget build-essential \ + pkg-config libssl-dev zlib1g-dev + + + - name: Extract tag name + run: | + REF="${GITEA_REF:-$GITHUB_REF}" + TAG="${REF#refs/tags/}" + echo "TAG=$TAG" >> $GITHUB_ENV - name: Build aria2c Linux static run: | @@ -139,39 +120,55 @@ jobs: - name: Prepare Linux package run: | set -e - mkdir -p dist/linux - cp yt-playlist-main.py dist/linux/ - cp yt-playlist-config.json dist/linux/ + WORKSPACE_ROOT="${GITEA_WORKSPACE:-$PWD}" + mkdir -p "$WORKSPACE_ROOT/dist/linux" + cp "$WORKSPACE_ROOT/yt-playlist-main.py" "$WORKSPACE_ROOT/dist/linux/" + cp "$WORKSPACE_ROOT/yt-playlist-config.json" "$WORKSPACE_ROOT/dist/linux/" - mkdir -p dist/linux/bin + mkdir -p "$WORKSPACE_ROOT/dist/linux/bin" # yt-dlp - curl -L -o dist/linux/bin/yt-dlp https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux - chmod +x dist/linux/bin/yt-dlp + curl -L -o "$WORKSPACE_ROOT/dist/linux/bin/yt-dlp" https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux + chmod +x "$WORKSPACE_ROOT/dist/linux/bin/yt-dlp" # FFmpeg Linux static - curl -L -o dist/linux/ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz - mkdir -p dist/linux/ffmpeg_temp - tar -xf dist/linux/ffmpeg.tar.xz -C dist/linux/ffmpeg_temp --strip-components=1 - mv dist/linux/ffmpeg_temp/ffmpeg dist/linux/bin/ffmpeg - chmod +x dist/linux/bin/ffmpeg + curl -L -o "$WORKSPACE_ROOT/dist/linux/ffmpeg.tar.xz" https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz + mkdir -p "$WORKSPACE_ROOT/dist/linux/ffmpeg_temp" + tar -xf "$WORKSPACE_ROOT/dist/linux/ffmpeg.tar.xz" -C "$WORKSPACE_ROOT/dist/linux/ffmpeg_temp" --strip-components=1 + mv "$WORKSPACE_ROOT/dist/linux/ffmpeg_temp/ffmpeg" "$WORKSPACE_ROOT/dist/linux/bin/ffmpeg" + chmod +x "$WORKSPACE_ROOT/dist/linux/bin/ffmpeg" - # Copy built aria2c binary if present - if [ -f dist/linux/bin/aria2c ]; then - chmod +x dist/linux/bin/aria2c - fi + # aria2c minimal fully static + mkdir -p "$WORKSPACE_ROOT/dist/linux/aria2c_build" + cd "$WORKSPACE_ROOT" + wget https://github.com/aria2/aria2/releases/download/release-1.37.0/aria2-1.37.0.tar.gz + tar -xzf aria2-1.37.0.tar.gz + cd aria2-1.37.0 + CFLAGS="-Os -s" LDFLAGS="-static" ./configure \ + --enable-static --disable-shared \ + --disable-libaria2 --without-ca-bundle \ + --without-libnettle --without-libgcrypt \ + --without-libssh2 --without-libexpat \ + --without-libxml2 --without-libsqlite3 \ + --with-openssl + make -j"$(nproc)" + strip src/aria2c + + cp src/aria2c "$WORKSPACE_ROOT/dist/linux/bin/aria2c" + chmod +x "$WORKSPACE_ROOT/dist/linux/bin/aria2c" - # Remove temp files and downloaded archives before zipping - rm -rf dist/linux/ffmpeg_temp dist/linux/aria2c_build dist/linux/ffmpeg.tar.xz dist/linux/aria2c_build/aria2-1.37.0.tar.gz + # Cleanup + rm -rf "$WORKSPACE_ROOT/dist/linux/ffmpeg_temp" "$WORKSPACE_ROOT/dist/linux/aria2c_build" "$WORKSPACE_ROOT/dist/linux/ffmpeg.tar.xz" "$WORKSPACE_ROOT/aria2-1.37.0" "$WORKSPACE_ROOT/aria2-1.37.0.tar.gz" - # Zip everything at top level - cd dist/linux - zip -r ../../yt-playlist-linux-${{ needs.determine-next-tag.outputs.next_tag }}.zip * - cd ../.. + # Zip everything + cd "$WORKSPACE_ROOT/dist/linux" + ZIP_NAME="yt-playlist-linux-${TAG}.zip" + zip -r "$WORKSPACE_ROOT/$ZIP_NAME" * + echo "ZIP_PATH=$WORKSPACE_ROOT/$ZIP_NAME" >> $GITHUB_ENV - name: Upload Linux release uses: https://gitea.com/actions/gitea-release-action@v1 with: - files: yt-playlist-linux-${{ needs.determine-next-tag.outputs.next_tag }}.zip - tag_name: ${{ needs.determine-next-tag.outputs.next_tag }} - name: ${{ needs.determine-next-tag.outputs.next_tag }} \ No newline at end of file + files: ${{ env.ZIP_PATH }} + tag_name: ${{ env.TAG }} + name: ${{ env.TAG }} \ No newline at end of file diff --git a/README.md b/README.md index 1fae113..2d8fd7d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,112 @@ -# YouTube-Playlist-Downloader +# YouTube Playlist Downloader -Little app to download music playlists from YouTube \ No newline at end of file +A cross-platform tool and workflow for downloading entire YouTube playlists as MP3 files, using [yt-dlp](https://github.com/yt-dlp/yt-dlp), [ffmpeg](https://ffmpeg.org/), and [aria2c](https://github.com/aria2/aria2). Includes Gitea CI/CD workflow for packaging and releasing Windows and Linux binaries. + +--- + +## Features + +- **Download full YouTube playlists** as high-quality MP3 files. +- **Parallel downloads** using aria2c for speed. +- **Automatic numbering:** numbers tracks same as the origin playlist. +- **Cleanup of tracks:** optioon to remove tracks not in playlist anymore. +- **Configurable output paths** and archive tracking. +- **Cross-platform:** Windows and Linux support. +- **Gitea CI/CD workflow** for automated packaging and release. + +--- + +## Requirements + +- Python 3.8+ + +--- + +## Installation + +### Quick Start + +1. **Download the latest release:** + - Go to the [Releases](https://git.darkzoul.org/dark_zoul/YouTube-Playlist-Downloader/releases) page. + - Download the appropriate archive for your platform (Windows or Linux). + +2. **Unzip the archive:** + - Extract the contents to a folder of your choice. + +3. **Edit configuration:** + - Open `yt-playlist-config.json` and adjust paths and playlist URLs as needed. + +4. **Run the downloader:** + - On Windows: + ```sh + python yt-playlist-main.py + ``` + - On Linux: + ```sh + python3 yt-playlist-main.py + ``` + +--- + +## Usage + +### Configuration + +Edit `yt-playlist-config.json` to specify playlists and paths: + +```json +{ + "playlists": [ + { + "url": "https://www.youtube.com/playlist?list=playlistidhere", + "save_path": "./music", + "archive": "archive.txt" + } + ], + "yt_dlp_path": "./bin/yt-dlp.exe", + "ffmpeg_path": "./bin/ffmpeg.exe", + "aria2c_path": "./bin/aria2c.exe", + "max_parallel_downloads": 10, + "aria2c_connections": 8 +} +``` + +- **playlists:** List of playlist objects. Each must have a `url`, `save_path`, and `archive`. +- **yt_dlp_path, ffmpeg_path, aria2c_path:** Paths to binaries (relative or absolute). +- **max_parallel_downloads:** Number of simultaneous downloads. +- **aria2c_connections:** Connections per download. + +### Running + +```sh +python yt-playlist-main.py +``` + +- The script will check for binaries, update yt-dlp, and download all new tracks in the playlist. +- Tracks are saved and numbered in the specified folder. +- Deleted/private videos are skipped. +- Archive file prevents re-downloading existing tracks. + +--- + + +## Troubleshooting + +- **No binaries found:** Ensure paths in `yt-playlist-config.json` are correct. +- **No tracks downloaded:** Check playlist URL and archive file. + +--- + +## License + +See [LICENSE](LICENSE). + +--- + +## Credits + +- [yt-dlp](https://github.com/yt-dlp/yt-dlp) +- [ffmpeg](https://ffmpeg.org/) +- [aria2c](https://github.com/aria2/aria2) + +--- \ No newline at end of file