LS / MTLLuke Sidney
Lighting the room0%
10th floor · Quartier Latin · golden hour
Montreal, QC·lukesidney.me·Fresh 2 · DenoNo cookies · No forms
Plate01

Front page.What it is

Building

The ad-free video library

All my long-form video in one place, on storage I own, with no ads.

Running on a temporary address while the domain waits.

The video library's home page, playing the owner's own gameplay recording.
Plate02

Set in type.The stack

What it runs on

A static front on Cloudflare, a small box for accounts, and the files in my own bucket.

Front

  • Astro7.3a prerendered catalogue
  • Preact10.29the player island
  • Cloudflare Workershosting

Box

  • Bun + Hono4.13the API
  • Better Auth1.7accounts
  • Polarmembership
  • Postgres17with Drizzle 0.45, on Railway

Media

  • Cloudflare R2the files, no egress fees
  • Trigger.dev4.5renditions and previews with ffmpeg
  • Payloadone row per video
  • <video>no third-party player, no DRM
Plate03

On the press.How it works

5 steps, in order

From one upload to an honest view count.

  1. Upload once

    The master goes up through CCC, which writes the video's CMS row.

  2. Transcode in the background

    A background job cuts the renditions and a hover preview with ffmpeg, straight into R2.

  3. Rebuild the catalogue

    A push rebuilds every catalogue page as static HTML in about 40 seconds.

    The video library's catalogue, with filters by kind and tag.
  4. Play from a signed link

    The player asks for a short-lived signed URL. Member videos check the membership first.

    A watch page in the video library: the player, the title and the view count.
  5. Count honestly

    A view counts after 3 seconds of real playback, once per viewer per day.

Plate04

Pulled proof.The code

What counts as a view

src/lib/views.ts · lines 1–15real source
// A VIEW, as the watch page reports it: the viewer pressed play and has watched at least// VIEW_SEC seconds of the video. The box counts it once per viewer per 24 hours and keeps the// rules (box/src/viewing.ts); this file only measures.export const VIEW_SEC = 3/** * Seconds actually played on this page, from `player.played` — the stretches of the video the * viewer watched. A seek adds nothing and a stretch replayed counts once, so no timer and no * per-second event is needed to know it: it is read at the moments the page already saves. */export function playedSeconds(ranges: Pick<TimeRanges, 'length' | 'start' | 'end'>): number { let sum = 0 for (let i = 0; i < ranges.length; i++) sum += ranges.end(i) - ranges.start(i) return sum}

A seek adds nothing and a replay counts once, so there is no timer and no per-second write.

Plate05

The reel.The walkthrough

Watch it run

Software demo coming soonAbout 2 minutes: upload in CCC, the transcode job, the catalogue rebuild, a hover preview becoming the player.
Plate06

Corrections.What it fixes

The problems it solves

  • The platform keeps the files and the audience.

    The files sit in my own bucket, and the list is mine.

  • Ads on every video, and less reach for linking my own work.

    No ads. Linking my own work costs nothing.

  • Bandwidth bills that grow with every view.

    R2 charges no egress, and the catalogue is static.

  • View counts nobody can check.

    A view is 3 seconds, once a day, and the rule is tested.

Plate07

Editor's letter.Why I built it

Why it exists

Own the data. Every re-hosting provider was out from the start.

I want to promote my own work without a platform punishing me for it. So each video is published here first, and the copies elsewhere point back to the ad-free version.

Riso illustration: film reels stored in a vault that belongs to one person.
Plate08

Against the rack.Better than average

Against the usual tools

Against Vimeo OTT, Uscreen and Patreon video. Their side was read from each vendor's own pages on 2026-09-27.

WhatThe market averageThis app
Where files liveOn the vendor's servers.In my own R2 bucket.vendor's page
Cost as the audience growsA fee per paying subscriber, or a cut of each sale.No fee per subscriber and no cut; R2 charges no egress.vendor's page
What counts as a viewAny start of playback (Vimeo).3 seconds of real playback, once per viewer per day, tested.vendor's page
PrivacyPlayer analytics cookies on by default (Vimeo).Signed-out viewers' IP addresses are held in memory only.vendor's page
Plate09

The imprint.How long it took

64 commits in 11 days

commits on the site
64
days with a commit
10
days, first commit to latest
11
2026-09-09commits per day · busiest: 142026-09-19
  1. First commit
  2. Latest site commit
  3. Plus 25 commits on the ingest and transcode side

Measured: git log of the site's repo, 2026-09-27.

Plate10

Colophon.Where it sits in the ledger

entry
08 / 09
status
building

Still building. Nothing shipped is claimed for it.

← Back to the ledger
Remote▲ ▼ section · ◀ ▶ project · OK opens