Extract • Clean • Transform • Export
PlaylistForge is a modern desktop utility for extracting YouTube playlist metadata, cleaning video titles, previewing videos, and exporting structured data in formats that work well for documents, spreadsheets, applications, and databases.
It is built with Python 3.12+, PySide6, and yt-dlp. The project is intentionally modular: the GUI does not call yt-dlp directly, exporters are plugin-style classes, and the cleaning system is a typed rule pipeline.
Screenshots will be added as the visual polish pass matures: main window, cleaning panel, export panel, settings dialog, and large playlist preview.
PlaylistForge currently includes a working application foundation:
- PySide6 desktop shell.
- Background extraction service architecture.
- yt-dlp wrapper.
- Typed playlist/video/settings/export/cleaning models.
- Title cleaning rules.
- JSON, TXT, CSV, Markdown, HTML, Excel, and clipboard export helpers.
- Settings persistence.
- Rotating logs.
- Unit tests for core behavior.
- PyInstaller packaging scaffold.
Some advanced UI polish features are intentionally staged for later hardening, including thumbnail caching, richer column visibility controls, and polished cleaning preset editing.
- Paste one or many YouTube playlist URLs.
- Validate and normalize playlist URLs.
- Extract metadata without downloading videos.
- Run extraction in a background worker so the UI stays responsive.
- Cancel extraction.
- Preview playlist metadata:
- playlist title
- channel
- playlist ID
- video count
- total duration
- extraction time
- Preview videos in a sortable, searchable table.
- Copy selected table rows.
- Clean titles using ordered rules:
- remove literal phrases
- regex replacement support
- collapse repeated spaces
- trim whitespace
- preserve original titles
- undo/reset cleaning
- Export selected fields to:
- JSON
- TXT
- CSV
- Markdown
- HTML
- Excel
- clipboard text
- Remember:
- theme
- window size
- recent playlist URLs
- favorites
- last export directory
- export preferences
- cleaning rules
- Python 3.12 or newer
- pip
- Internet access for installing dependencies
- A desktop environment capable of running Qt applications
Runtime dependencies:
PySide6yt-dlpopenpyxl
Development dependencies:
pytestpytest-covruffmypypyinstaller
playlistforge/
app.py Application bootstrap
__main__.py python -m playlistforge entry point
core/ Dataclasses, enums, errors, shared results
extraction/ URL validation, yt-dlp client, parser, Qt worker
cleaning/ Cleaning rule engine, presets, undo stack
export/ Export plugin interface and exporters
settings/ JSON settings repository
history/ Recent/favorite playlist helpers
logging/ Rotating logging setup
ui/ PySide6 windows, widgets, table models, dialogs
tests/ Unit tests
packaging/
pyinstaller/ Platform-specific PyInstaller specs
linux/ .deb control files, .desktop entry, build scripts
windows/ Inno Setup installer script, build batch
macos/ DMG build script
icons/ SVG icon source
build-all.sh Cross-platform build helper
release.sh Version bump and tag helper
Clone or open the repository, then run these commands from the project root.
Linux and macOS:
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
python -m playlistforgeWindows PowerShell:
py -3.12 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
python -m playlistforgeYou can also run the installed GUI script:
playlistforge- Launch PlaylistForge.
- Paste one or more YouTube playlist URLs into the large input box.
- Click Extract.
- Watch extraction progress in the status panel.
- Search, sort, select, and copy rows in the video table.
- Use Clean Titles to remove unwanted words or normalize spacing.
- Choose an export format from the Export panel.
- Click Export file and choose a destination.
- Use Copy when you want clipboard-ready tabular output.
Example playlist URL formats:
https://www.youtube.com/playlist?list=PLxxxxxxxxxxxx
https://www.youtube.com/watch?v=VIDEO_ID&list=PLxxxxxxxxxxxx
The JSON exporter creates app/database-friendly camelCase keys:
[
{
"lecture": 1,
"title": "Introduction",
"videoId": "j8QdcI71-S4",
"watchUrl": "https://www.youtube.com/watch?v=j8QdcI71-S4",
"embedUrl": "https://www.youtube.com/embed/j8QdcI71-S4",
"thumbnail": "https://i.ytimg.com/vi/j8QdcI71-S4/hqdefault.jpg"
}
]The selected export fields are controlled by ExportOptions and can be exposed more deeply in the UI as the app matures.
Settings are stored as JSON in the user config directory:
- Linux:
~/.config/PlaylistForge/settings.json - macOS:
~/Library/Application Support/PlaylistForge/settings.json - Windows:
%APPDATA%\PlaylistForge\settings.json
Logs are stored in the user state/log directory:
- Linux:
~/.local/state/PlaylistForge/logs/ - macOS:
~/Library/Logs/PlaylistForge/logs/ - Windows:
%LOCALAPPDATA%\PlaylistForge\logs\
PlaylistForge writes:
playlistforge.logplaylistforge.error.log
Tracebacks are written to logs, not shown directly in user dialogs.
Run unit tests:
pytest -qRun syntax compilation:
python -m compileall -q playlistforge testsRun Ruff after installing development dependencies:
ruff check .Run mypy:
mypy playlistforgePlaylistForge can be packaged as native installers for Linux, Windows, and macOS. Each installer must be built on its target OS — PyInstaller does not reliably cross-compile Qt desktop apps.
bash packaging/build-all.shThis auto-detects your OS and runs the correct platform build script.
Two distribution methods are supported.
Method A — Native .deb package (Debian/Ubuntu)
Installs PlaylistForge as a system Python package with desktop integration:
bash packaging/linux/build-dpkg.shOutput: dist/playlistforge_0.1.0_all.deb
Install the .deb:
sudo dpkg -i dist/playlistforge_0.1.0_all.deb
sudo apt-get install -f # install runtime dependencies
playlistforgeMethod B — Standalone AppImage
Bundles everything into a single portable executable:
bash packaging/linux/build-appimage.shOutput: PlaylistForge-x86_64.AppImage
chmod +x PlaylistForge-x86_64.AppImage
./PlaylistForge-x86_64.AppImageRequires linuxdeploy or appimagetool on PATH. Install from https://github.com/linuxdeploy/linuxdeploy.
# From project root
pyinstaller packaging\pyinstaller\playlistforge.windows.spec --clean --noconfirm
# Build installer with Inno Setup (requires Inno Setup 6)
iscc packaging\windows\playlistforge.issOr use the batch script:
packaging\windows\build.batOutput:
dist\PlaylistForge\— portable directorydist\PlaylistForge-Setup-0.1.0.exe— installer
The installer is built with Inno Setup. Download and install it, then iscc will be available on PATH.
bash packaging/macos/build-dmg.shOutput:
dist/PlaylistForge.app— portable application bundledist/PlaylistForge-0.1.0.dmg— disk image for distribution
For production releases, set these environment variables before building:
export APPLE_SIGN_IDENTITY="Developer ID Application: Your Name"
export APPLE_ID="your@apple.id"
export APPLE_TEAM_ID="TEAM123456"
export APPLE_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"This enables codesigning and Apple notarization.
When you push a tag matching v*, the CI workflow in .github/workflows/ci.yml automatically:
- Runs linting and tests on all three OS runners.
- Builds the Linux AppImage, Windows installer, and macOS DMG.
- Creates a GitHub Release with all artifacts attached.
Create and push a release tag:
bash packaging/release.sh 0.2.0
git push origin main --follow-tagsThe release script updates version numbers across all spec files, build scripts, and the package metadata, then tags the commit.
# 1. Update version
bash packaging/release.sh 0.2.0
# 2. Verify everything
ruff check .
pytest -q
python -m compileall -q playlistforge tests
mypy playlistforge
# 3. Push tag — GitHub Actions builds it
git push origin main --follow-tags| Platform | Format | File |
|---|---|---|
| Linux | .deb | playlistforge_0.1.0_all.deb |
| Linux | AppImage | PlaylistForge-x86_64.AppImage |
| Windows | Portable | dist/PlaylistForge/ |
| Windows | Installer | PlaylistForge-Setup-0.1.0.exe |
| macOS | Bundle | PlaylistForge.app |
| macOS | DMG | PlaylistForge-0.1.0.dmg |
All artifacts go to dist/.
The main architecture rule is separation of concerns:
- UI code owns presentation and user interaction.
- Extraction code owns yt-dlp integration.
- Cleaning code owns title transformations.
- Export code owns output generation.
- Settings code owns persistence.
- Core models define the shared language of the application.
Important modules:
playlistforge/extraction/yt_dlp_client.py: the only module that importsyt_dlp.playlistforge/extraction/worker.py: background extraction worker.playlistforge/ui/main_window.py: application composition and signal wiring.playlistforge/export/base.py: exporter plugin contract.playlistforge/export/registry.py: exporter registration.playlistforge/cleaning/engine.py: title-cleaning pipeline.playlistforge/settings/repository.py: cross-platform settings persistence.
Install the project with runtime or development dependencies:
python -m pip install -e ".[dev]"YouTube may return HTTP 429 when too many requests are made. Wait and try again later.
Recent yt-dlp versions may warn that no supported JavaScript runtime is installed. PlaylistForge suppresses repeated copies of the same warning, but the first warning is still logged because some YouTube extraction paths may need a runtime.
Install one supported by yt-dlp, such as Deno, then restart PlaylistForge:
deno --versionIf deno is available on PATH, yt-dlp can use it automatically.
PlaylistForge cannot extract private playlists unless yt-dlp can access them. Public playlist URLs work best.
Install openpyxl:
python -m pip install openpyxlMake sure Qt platform dependencies are available for your desktop environment. On headless servers, the GUI cannot run without a display server.
No. It extracts metadata only.
No. yt-dlp is isolated inside the extraction service. PlaylistForge adds typed models, a GUI, cleaning, previews, settings, history, and exporter plugins.
Yes. Create a class implementing Exporter, then register it in default_exporter_registry().
Yes. The cleaning, exporting, settings, validation, and parsing layers are independent from PySide6.
- Keep changes focused.
- Add tests for non-GUI behavior.
- Keep GUI code out of extraction/export/cleaning modules.
- Run:
python -m compileall -q playlistforge tests
pytest -q
ruff check .- Document user-facing behavior in this README.
MIT