Live · Self-hosted · Personal project

Maxtube

A private music platform that harvests YouTube playlists, fingerprints every track via Shazam, and serves a curated library through live genre radio channels backed by Backblaze B2.

Role
Sole developer
Stack
Django · Python · PostgreSQL
Storage
Backblaze B2
Hosted
VPS · Ubuntu

Overview

Maxtube began as a simple playlist archiver and grew into a full media pipeline. It monitors YouTube playlists for new videos, downloads the audio, and runs each track through Shazam fingerprinting to identify artist, album, genre, and release year — building a structured music catalogue entirely from raw downloads.

The finished library is served through a custom streaming interface with persistent cross-tab playback, genre-based radio channels, and signed per-request URLs from B2. No public CDN, no third-party player — everything is built from scratch on Django.

The project is in active daily use on a self-hosted VPS and has processed over 1,400 tracks. PostgreSQL is the planned database backend as the catalogue grows.

1,400+
Tracks identified and catalogued
9
Management pipeline commands
B2
Backblaze object storage · signed URLs
∞
Genre radio · live streaming

Processing pipeline

From YouTube to library in five stages

1
Harvest
Playlists are polled for new videos. Each new video is recorded and queued for download with its YouTube ID as the canonical slug.
fetch_playlistsfetch_videos
2
Download & Extract
Videos are downloaded via a bundled pytube fork with exponential backoff. The audio stream is extracted as MP3 and stored locally, then uploaded to Backblaze B2.
download_videoscreate_audio
3
Fingerprint
A short clip is cut from each audio file and submitted to Shazam via shazamio. The raw recognition JSON is stored on the Clip model alongside parsed metadata fields. Proxy rotation prevents rate-limiting.
create_clipsfetch_recognitionprocess_recognition
4
Tag & Catalogue
Identified tracks are linked to normalised Artist, Album, Genre, and Label records. MP3 tags are written using eyed3. Duplicates are flagged; the Music record is verified and becomes visible in the library.
tag_audiocreate_music
5
Stream
Tracks are streamed directly from B2 via signed expiring URLs. Genre radio channels use a virtual clock to synchronise playback position across listeners, yielding MP3 byte ranges through a Django StreamingHttpResponse.
channels.pyb2.py

Tech stack

Everything that runs it

Django 4.x
Web framework, ORM, management commands, streaming HTTP
Python 3.11
Async pipeline jobs, management commands, generator-based streaming
PostgreSQL
Primary database — migration from SQLite in progress
Backblaze B2
Object storage for audio files; accessed via S3-compatible API with signed URLs
shazamio
Async Python client for Shazam audio fingerprinting and track identification
boto3
AWS SDK used for Backblaze B2 object operations and presigned URL generation
moviepy
Audio duration extraction and video processing utilities
mutagen / eyed3
MP3 metadata reading and ID3 tag writing after Shazam identification
pytube (fork)
Bundled local fork of pytube for YouTube video and audio stream downloading

Design decisions

A few things that were harder than expected

Continuous radio without a media server
Genre radio channels stream directly from Django using a Python generator and StreamingHttpResponse. A virtual clock tracks the byte-position of the current track across all listeners, so every browser joining mid-stream lands at the correct offset — no Icecast, no Node.js, no websockets.
ID3 headers causing audio pops at track boundaries
When concatenating MP3 streams, ID3v2 headers at the start of each file caused the browser decoder to resync, producing an audible click. The fix was a byte-scan for the first valid MP3 frame sync word (0xFF 0xEx) and stripping everything before it at yield time.
Eliminating per-track B2 HEAD requests
The streaming generator needed each track's byte length to compute the range end before downloading. Initially this meant one B2 HEAD request per track transition, adding 50–150ms of silence. The solution was a cached b2_size field on the Audio model, populated once and read from the database instead.
Shazam throughput and proxy rotation
Shazam has no published rate limit, but submitting clips concurrently at high volume triggers silent failures. The recognition pipeline runs with conservative concurrency, exponential backoff, and a rotating proxy pool to spread requests across IP addresses — treating availability as a moving target rather than a fixed constraint.