youtube-scheduler
Editing metadata against an API that wipes what you do not resend.
Python
- YouTube Data API
- OAuth 2.0
GitHub Actions
- 163
- Tests
- 3
- Dependencies
- 4
- Python versions in CI
The problem
YouTube’s update endpoint is destructive in a way that is easy to miss. It does not patch. For any part you name, every writable property in that part is replaced by what you sent, and anything you omitted is cleared. Send a new description without the tags, and the tags are gone.
That makes a bulk edit across a channel a genuinely dangerous operation, and the danger is silent: the API returns success.
What I built
A command-line tool that edits titles, descriptions and tags and schedules publish times across many videos at once, built so that a mistake is hard to make and recoverable when it happens.
Every video is fetched before anything is planned, the whole batch is planned before anything is written, and the confirmation screen shows the exact quota cost and every skip before the first write goes out.
Decisions I'd defend
Bodies are built from the fetched resource, never from scratch, and read-only fields are removed by a denylist rather than selected by an allowlist. The difference matters on a moving API: a writable field the platform adds next year round-trips safely under a denylist, and gets silently wiped under an allowlist.
The real safety mechanism is sending the minimum set of parts. If nothing is being scheduled, the request names only the snippet, so every property in the status part is structurally unreachable rather than carefully preserved. Licence, embeddable and the made-for-kids declaration cannot be touched because the request never mentions the part they live in.
The made-for-kids declaration gets its own prompt that a blanket yes flag cannot bypass. The value the API returns is the effective one and may be inherited from the channel, while the writable field is a per-video declaration, so echoing back what you read would invent a claim the creator never made.
A time with no UTC offset is rejected rather than assumed local. That is the single most common bug in this class of tool: the same input schedules a different moment depending on which machine runs it.
Every run writes a log holding the full before state, the exact request body and the response, and a restore command replays the before state through the same merge logic. The tests are the part I would point at: the stub mutates an in-memory store the same way the real API does, so a wipe in a test looks exactly like a wipe in production.
The honest gaps
The rollback is not total. Only writes recorded as succeeded are candidates, so a write that landed server-side but failed in transit is unrecoverable. One field is never returned by the read endpoint, so it is not in the before state and cannot be restored. And restore is itself a destructive update, which is worth saying out loud.
The run log is also less durable than its own comment claims: it is written at the end rather than in a finally block, so an exception raised outside the per-video handlers exits without writing it. For a tool whose pitch is that everything is recorded and undoable, that is the defect I would fix first, and it is a one-line change.
One display bug: the confirmation screen labels every scheduled time with a fixed West Africa offset. The value sent is correct; the reassurance printed next to it is wrong for anyone outside that zone.
Where it is now
Public and MIT licensed, with 163 tests running against four Python versions in CI, and dependencies pinned with a test that fails if the two dependency files ever disagree.