The webcam snapshot service had a lot going on this week. Here's a quick summary of the latest updates.
Better GIFs
GIFs now look better than before. They last 3 seconds instead of just 1, and the width is now 480px instead of 320px. I also added a palette optimization step, so colors look much more accurate within the GIF's 256-color limit. All settings are now in one place, so both webcam and YouTube snapshots use the same configuration.
I fixed an issue where the palette filter could freeze on live HLS streams. Now, the video segment is saved to a temporary file first, and then JPG and GIF extraction run at the same time from disk.
YouTube Snapshots Actually Snapshot
Before, YouTube snapshots just showed cached CDN thumbnails instead of the live video. Now, yt-dlp downloads a real video segment, so snapshots show what's actually on screen and animated GIFs capture real movement. Thumbnails are only used if the video download doesn't work.
I also fixed another issue where using --live-from-start would download the whole stream history.
Homepage Gallery
The old plain-text landing page is gone. Now, there's an HTML gallery that displays the 24 most recent snapshots. Images show up as JPGs by default, but if you hover over them, they switch to animated GIFs.
CORS Support
Now, cross-origin requests work from yayproject.com and all its subdomains. The server checks the Origin header, handles OPTIONS preflight requests, and adds the correct Access-Control-Allow-* headers to every response. This means pages on tools.yayproject.com can now use the service.
Test Suite and CI
I added a full test suite with 21 unit tests that check CORS logic, YouTube URL parsing, GIF settings, and HTTP route validation. All tests run automatically in GitHub Actions for every push and pull request.
I also added integration tests. These start the real server, connect to live HLS and YouTube streams, and check that the images returned have the right magic bytes and are a reasonable size. These tests run manually with workflow_dispatch since they rely on external streams and take longer to complete.