Work / Case study

One track, five releases: Sleep Anchor’s AI and Python pipeline

How one person runs a sleep-music catalog: AI generates each track, and a Python pipeline checks, renders and publishes it to YouTube, podcasts and streaming.

In brief

Problem
One track has to ship as five releases on different platforms and schedules, and at the start the steps shared no schedule or state.
What I built
A pipeline where AI generates the music and artwork, and Python scripts check, render and publish each release from a SQLite calendar and a local dashboard.
What changed
An eight-hour render fell from about 4.8 hours to about 25 minutes, and one person now keeps two podcast feeds on a twice-weekly schedule.
Status
Running now
Tools
  • Python
  • Claude Code
  • FFmpeg
  • SQLite
  • Next.js
  • YouTube API
  • RSS.com API
  • Reaper
  1. Generate AI, Brent picks the take Check: Record and prompt exist
  2. Fit to length Python Check: Brent listens to the joins
  3. Master Python builds, Brent adjusts Check: Loudness and clippingverify_master.py
  4. Loop Python Check: Brent hears a test clip across the joinprepare_loop.py
  5. Artwork and animation AI, Brent directs Check: Brent approves the looped clip
  6. Render Python and FFmpeg Check: Black frames, truncated files
  7. Publish Python Check: Duplicate check against YouTube
How a track ships, in 7 stages. Revised . Each stage, who does it and its check, in full: The architecture.

The problem

Sleep Anchor is an ambient sleep-music catalog that I run alone, on AI and Python. AI models generate the music, the artwork and its animation. I do the mastering, the final pass that sets each track’s loudness and tone. Python does the rest. As of 10 October 2026 the catalog holds 60 tracks, 58 long-form videos and 129 free episodes across its two music feeds.

Making a track is not the hard part. One finished track has to go to five places: a two-hour YouTube video, a one-hour episode on one podcast feed, a 90-minute episode on a second, paid eight-hour and ten-hour versions for subscribers, and a streaming single. Each has its own format, length, artwork and release time, and the platforms’ APIs cover only part of the work.

At the start, each step was a separate script or browser session with no shared schedule or state between them, and it showed.

  • The first eight-hour videos repeated the track end to end with no blend between repeats, and on one of them the average viewer left at about the point where the track first repeated.
  • A large upload looked stalled, so it was stopped and restarted until five copies of the video existed.
  • A video went public the moment it was uploaded, because the schedule setting was left off.
  • Publishing a podcast episode needed a browser session each time, and the feed once went 24 days without a new one.

The architecture

Every track has one record, a JSON file that exists before the track is generated. It carries the prompts for the track and its artwork and the list of AI tools used, so a release can be traced back to its inputs. Scheduled videos, social posts and podcast episodes are rows in one SQLite database. A local dashboard sits on top: its buttons start the Python scripts as background jobs and track them in the same database.

AI does four jobs in this system.

  • Generation. An AI music model generates each track, and AI image and video models make the artwork and its animation.
  • Drafting. A language model in the dashboard suggests titles, descriptions and prompts, and I choose and edit them.
  • Engineering. An AI coding assistant, Claude Code, wrote much of the code to my design: about 55,000 lines of Python in 175 files, and a dashboard of about 78,000 lines of TypeScript.
  • Operations. Where a platform has no API for a step, an AI browser agent fills in the web form from a prompt the pipeline generates, and the dashboard tracks each one until I mark it done.

A track moves through seven stages.

Stage Done by Check before the next stage
Generate AI: a music model, from the prompt saved in the track’s record. I pick the take. Automated: no generation starts until the record and its prompt exist
Fit to length Python: extends or trims the take to seven or eight minutes Me: I listen to the joins
Master Python builds the session in Reaper, an audio editor, from a template. I adjust, render and listen. Automated: verify_master.py fails a master outside its loudness target or too close to clipping
Loop Python: prepare_loop.py finds loop points and blends the end into the start Me: a two-minute test clip across the join
Artwork and animation AI: image and video models, then scripted passes. I direct them and pick the takes. Me: I approve the looped clip
Render Python and FFmpeg cut every long version from the one loop file Automated: video is sampled for black frames, and a truncated podcast file is deleted
Publish Python: the YouTube and podcast host APIs, scheduled from the calendar Automated: a duplicate check against YouTube itself

Publishing is scheduled from the calendar. The upload script sends a video to YouTube as private with a publish time, sets YouTube’s label for altered or synthetic content on every upload, and adds the thumbnail and playlist. Podcast episodes go through the host’s API with a release time and its AI-content flag.

Nine scheduled jobs run on the same machine. Four of them work on the calendar: one posts the first comment when a video goes public, two reconcile calendar rows against what YouTube and the social scheduler report, and one flags videos still waiting for manual setup. The other five collect analytics, research, email and site reports.

Decisions and trade-offs

AI generates, a person signs off

Chose: AI generates every track, and I master each one in Reaper before anything is released.

The pipeline treats the model’s output as raw material, the way a data pipeline treats a source feed: nothing downstream uses it until it has passed a check. A script builds the mastering session from a template, I adjust it and listen, and an automated check has to pass before the track moves on. The cost is throughput. I listen to every master, so one person sets the pace. In July 2026 I moved the podcast schedule from three episodes a week to two, because a schedule kept every week matters more than a faster one.

One calendar, and the platform as the system of record

Chose: scheduled items are rows in one SQLite table, and scripts check that table against the platforms’ own APIs.

Before that, scheduling data was spread across at least six files in different formats, and neither I nor the AI assistant could say what had to happen today without cross-checking several of them. One table fixed that for both of us. But a local copy of a remote system goes stale, so the duplicate check asks YouTube whether a video exists, and reconciliation jobs mark a video or social post done only when the platform confirms it.

A check before each step that cannot be undone

Chose: a failed check stops the pipeline, and most of the checks came from a failure.

Duplicate uploads led to the duplicate check. The video that went public early led to uploading as private with a publish time. The videos people left at the first repeat led to the join test. A failed check ends its script with an error, so the step is not marked done. The last check is the publish screen, which sends nothing until the description, thumbnail and AI flag are set and the title matches the video’s length, checked again on the server.

A check also has to be able to fail. A two-minute render from the start of a seven-minute loop crosses no join and proves nothing, so the test now cuts its two minutes from either side of the join. The cost is false alarms: dark, sleep-graded video can trip the black-frame check, so the upload script has an explicit flag to skip it for that case.

Jobs that survive being interrupted

Chose: long jobs run apart from the dashboard and record their own state, and uploads resume.

In April 2026 a 28 GB upload was killed at 70 percent, most likely when the dashboard’s development server reloaded. After that, jobs moved out of the web server’s process and into a jobs table, with a sweep at start-up that marks dead ones. Uploads save their session after every chunk, so an interrupted one continues from where it stopped, and the resume logic has its own tests.

Two hours on YouTube, not eight

Chose: since the end of July 2026 new YouTube videos run just over two hours instead of eight.

Eight hours was chosen on the theory that a listener sleeps with the video running. The channel’s own analytics said otherwise: people watched a small part of an eight-hour video, so almost nobody reached the end and the next video never started. A video of just over two hours can reach its end, so the next one can start. The change cost little, because the all-night versions still exist for podcast subscribers, where that length is the product.

What the AI did, what I decided, what caught it

Each AI job in the pipeline has a person deciding and a check catching, and the incidents above show which was which.

  • An AI music model generates each track. I pick the take and master it in Reaper. verify_master.py fails a master outside its loudness target or too close to clipping.
  • Image and video models make the artwork and its animation. I direct them and pick the takes, and I approve the looped clip before anything is rendered.
  • A language model drafts titles, descriptions and prompts. I choose and edit them. The publish screen sends nothing until the title matches the video’s length, checked again on the server.
  • Claude Code wrote much of the code to my design. I decided the design: one table, a check before each irreversible step, jobs that record their own state. Tests catch the code: the resume logic has its own, and a join test that crossed no join was rewritten until it could fail.
  • An AI browser agent fills in web forms where a platform has no API. The dashboard tracks each one until I mark it done.

Where it breaks

  • A check with a false alarm. Dark, sleep-graded video can trip the black-frame check, so the upload script has a flag to skip it for that case, and that flag is a decision I make by hand.
  • One person sets the pace. I listen to every master, so throughput is mine, and in July 2026 the podcast schedule went from three episodes a week to two.
  • A local copy of a remote system goes stale. The calendar is checked against YouTube itself, and a video or social post is marked done only when the platform confirms it.
  • Long jobs die. A 28 GB upload was killed at 70 percent in April 2026, which is why jobs now live outside the web server and uploads resume from their last chunk.
  • The first renderer was the slow part. It encoded a still image frame by frame, and an eight-hour video took about 4.8 hours until the renderer repeated a short loop instead.

What changed

The numbers below come from the project’s own records.

  • An eight-hour video render went from about 4.8 hours with the first renderer, which encoded a still image frame by frame, to about 25 minutes once the renderer repeated a short loop.
  • A podcast episode went from a browser session each to a render and publish in under ten minutes, started from the dashboard. Both music feeds have released every Monday and Thursday since late July 2026.
  • Mastering went from about 50 minutes a track in the first session to a median of about 20 minutes on the 12 most recent tracks, measured from the fit-to-length step to my approval of the master.
  • Uploading a video went from a checklist of form fields to one command that checks for a duplicate, checks the frames, schedules the release and sets the labels.

Behind the catalog are 570 commits since 2 March 2026.

What I would carry into any data platform

  • Write state to disk at the moment it is created, not later in the pipeline.
  • Gate every step that cannot be undone, and make sure each check can fail.
  • Keep one source of truth, and reconcile it against the system of record, because a local copy of a remote system goes stale.
  • Make every call to an outside system idempotent: safe to repeat after a failure.
  • Monitor the monitoring. A job that fails silently costs more than one that fails loudly.