A Python tool that automatically downloads audio tracks from archive.org, creates videos with static background images, and uploads them to YouTube with proper metadata and playlists. Use it from the command line or the Web UI.
- Home page: hromp.com/archive-to-video
- Live Web UI: hromp.com/archive-to-video/app — try it in your browser
- Python 3.8 or higher
ffmpeginstalled and available in your PATH- Linux:
sudo apt-get install ffmpeg(Debian/Ubuntu) orsudo yum install ffmpeg(RHEL/CentOS) - macOS:
brew install ffmpeg - Windows: Download from ffmpeg.org and add to PATH
- Linux:
- YouTube API credentials (see setup instructions below)
- Clone this repository:
git clone <repository-url>
cd archive-to-video- Create a Python virtual environment:
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt- Go to the Google Cloud Console
- Create a new project (e.g., "Archive to YouTube")
- Go to "APIs & Services" > "Library"
- Search for "YouTube Data API v3" and enable it
- Go to "APIs & Services" > "Credentials"
- Configure the OAuth consent screen (if prompted):
- Choose "External"
- Fill in required fields (app name, email)
- Add yourself as a test user
- Create OAuth client ID:
- Choose "Desktop app"
- Download the JSON file
- Create the
configdirectory:
mkdir -p config- Move the downloaded JSON file to
config/client_secrets.json:
mv ~/Downloads/client_secret_*.json config/client_secrets.jsonWhen you run the tool for the first time, a browser window will open for authentication. Sign in with your Google account and grant permissions.
Run the tool with an archive.org URL:
python upload.py https://archive.org/details/lf2007-11-21.apython upload.py <URL> [--temp-dir DIR] [--credentials PATH]URL: Archive.org detail page URL (required)--temp-dir: Directory for temporary files (default:temp)--credentials: Path to YouTube API credentials (default:config/client_secrets.json)
- Preview: Shows track information, titles, durations, and playlist details
- Confirmation: Prompts for user confirmation before proceeding
- Check existing: Checks for existing videos on YouTube (prevents duplicates)
- Download: Downloads audio tracks and background image (skips if files exist)
- Create videos: Combines audio with background image using ffmpeg (skips if videos exist)
- Upload: Uploads videos to YouTube (skips if already uploaded)
- Create playlist: Creates or updates YouTube playlist with all tracks
- Review: Offers option to make videos and playlist public
A browser-based interface with the same workflow: sign in with YouTube, enter an archive.org URL, preview, optionally edit titles and descriptions and set privacy (private/unlisted/public), then process. Uploads run in the background with live progress; you can review the playlist and optionally make it public.
Try it: Live Web UI | Home page
The Web UI uses a Web application OAuth client (not the Desktop client used by the CLI). You need both YouTube Data API v3 and a Web OAuth client:
- Google Cloud Console → APIs & Services → Credentials
- Create (or edit) an OAuth 2.0 Client ID
- Application type: Web application
- Authorized redirect URIs — add the callback URL for where you will run the app:
- Local:
http://localhost:18765/api/auth/youtube/callback - Your domain:
https://your-domain.com/api/auth/youtube/callback - Path-based (e.g. hromp.com):
https://hromp.com/archive-to-video/app/api/auth/youtube/callback
- Local:
- Download the JSON and save it as
config/client_secrets.json
You can have both Desktop (CLI) and Web (Web UI) client IDs in the same Google Cloud project; the same client_secrets.json file can include both. The app uses the correct client based on how it’s run.
- Install dependencies (see Installation) and create
config/client_secrets.json(see above). - Set a session secret (required for cookies):
export SECRET_KEY="your-secret-key-for-sessions"
- Start the server:
python run_web.py
- Open http://localhost:18765 in your browser.
Default port is 18765; override with PORT=8080 python run_web.py if needed.
The Web UI runs in a container that includes Python, ffmpeg, and the app. You must provide credentials and a session secret via the host.
- Create
config/client_secrets.jsonon your machine (see Web UI setup above). - Set environment variables and start:
export SECRET_KEY="your-secret-key" docker compose up --build
- Open http://localhost:18765.
What the container does:
- Image: Built from
Dockerfile(Python 3.12, ffmpeg, app dependencies). - Port: 18765 (mapped to host).
- Volumes:
./config→/app/config(read-only) — soconfig/client_secrets.jsonis available inside the container.- Named volume
archive-to-video-temp→/app/temp— temporary downloads and videos (persist across restarts for resume).
- Environment: Set
SECRET_KEY(required). Optionally setPORT,BASE_URL(see below).
Example with custom port and base URL (e.g. behind a reverse proxy):
export SECRET_KEY="your-secret-key"
export BASE_URL="https://your-domain.com/archive-to-video"
docker compose up --buildFor path-based deployment (app served under a path like /archive-to-video/app/), set BASE_URL to the full public URL and add that callback to OAuth redirect URIs. See WEB_UI_SETUP.md for details.
| Variable | Default | Description |
|---|---|---|
PORT |
18765 | Port the server listens on |
HOST |
0.0.0.0 | Host to bind to |
SECRET_KEY |
(none) | Required. Secret for signing session cookies |
BASE_URL |
(none) | Public URL of the app (for OAuth redirects when behind a proxy or path) |
- Title/description overrides & privacy: Web UI lets you edit playlist title/description and set visibility (private/unlisted/public) before processing
- Multi-disc support: Automatically handles multi-disc recordings (d1t01, d2t01 patterns)
- Resume capability: Detects and reuses existing downloads and videos
- Duplicate detection: Checks for existing YouTube videos before uploading
- Preview mode: Shows what will be uploaded before any downloads
- Automatic cleanup: Temporary files deleted after successful upload
- High-quality encoding: 1080p H.264 video with AAC audio at 192kbps
The tool automatically detects existing files and resumes from where it left off:
- Audio files: Preserved until successful YouTube upload
- Video files: Preserved until successful YouTube upload
- Identifier-based naming: Files named with archive.org identifier for unique identification
- Skip redundant work: Reuses existing downloads and videos instead of recreating
You can safely interrupt the process and resume later without losing progress.
Install ffmpeg and ensure it's in your PATH (test with ffmpeg -version).
Place your OAuth2 credentials JSON file at config/client_secrets.json.
Verify the archive.org URL is correct and contains track listings.
- Check your YouTube API quota (daily limits apply)
- Verify OAuth consent screen setup is complete
- Ensure you have permission to upload to your YouTube channel
This is by design. Videos start as private. You can make them public through the interactive prompt after upload or in YouTube Studio.
archive-to-video/
├── README.md # This file
├── ARCHITECTURE.md # Technical documentation
├── WEB_UI_SETUP.md # Web UI OAuth, Docker, and deployment
├── requirements.txt # Python dependencies
├── upload.py # CLI entry point
├── run_web.py # Web UI server entry point
├── config/ # Configuration directory
│ ├── client_secrets.json # YouTube API credentials (you provide)
│ └── client_token.json # Saved OAuth token (CLI; auto-generated)
├── src/ # Core logic (shared by CLI and Web UI)
│ ├── main.py
│ ├── archive_scraper.py
│ ├── audio_downloader.py
│ ├── video_creator.py
│ ├── youtube_uploader.py
│ └── metadata_formatter.py
├── backend/ # Web UI API (FastAPI)
│ ├── main.py
│ └── api/ # auth, preview, process
├── frontend/ # Web UI (HTML, CSS, JS)
├── docs/ # Additional documentation (e.g. quota request)
├── Dockerfile # Web UI container
├── docker-compose.yml
└── temp/ # Temporary files (auto-cleaned)
For detailed technical information about the architecture and implementation, see ARCHITECTURE.md.
This project is licensed under the GNU General Public License v3.0 (GPL-3.0).
See LICENSE file for details.
