{"id":28317987,"url":"https://github.com/ry-ops/capture-cadence","last_synced_at":"2026-05-07T04:44:50.714Z","repository":{"id":292677173,"uuid":"981603017","full_name":"ry-ops/capture-cadence","owner":"ry-ops","description":"Automate snapshots and video clips from UniFi cameras — beautifully scheduled, locally stored, and easy to manage.","archived":false,"fork":false,"pushed_at":"2025-05-15T19:40:33.000Z","size":5488,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-24T15:43:17.633Z","etag":null,"topics":["docker","python","raspberrypi","unifi"],"latest_commit_sha":null,"homepage":"https://ry-ops.dev/capture-cadence","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ry-ops.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"ry-ops","patreon":"ry-ops","open_collective":null,"ko_fi":"ry-ops","tidelift":"ry-ops","community_bridge":"ry-ops","liberapay":"ry-ops","issuehunt":"ry-ops","lfx_crowdfunding":"ry-ops","polar":"ry-ops","buy_me_a_coffee":"ry-ops","thanks_dev":"ry-ops"}},"created_at":"2025-05-11T13:38:48.000Z","updated_at":"2025-05-15T19:40:36.000Z","dependencies_parsed_at":"2025-05-15T16:26:41.885Z","dependency_job_id":null,"html_url":"https://github.com/ry-ops/capture-cadence","commit_stats":null,"previous_names":["ry-ops/shotclock","ry-ops/capturecadence","ry-ops/capture-cadence"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ry-ops/capture-cadence","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ry-ops%2Fcapture-cadence","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ry-ops%2Fcapture-cadence/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ry-ops%2Fcapture-cadence/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ry-ops%2Fcapture-cadence/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ry-ops","download_url":"https://codeload.github.com/ry-ops/capture-cadence/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ry-ops%2Fcapture-cadence/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272214419,"owners_count":24893201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-26T02:00:07.904Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","python","raspberrypi","unifi"],"created_at":"2025-05-25T07:11:24.446Z","updated_at":"2026-05-07T04:44:50.709Z","avatar_url":"https://github.com/ry-ops.png","language":"Python","funding_links":["https://github.com/sponsors/ry-ops","https://patreon.com/ry-ops","https://ko-fi.com/ry-ops","https://tidelift.com/funding/github/ry-ops","https://funding.communitybridge.org/projects/ry-ops","https://liberapay.com/ry-ops","https://issuehunt.io/r/ry-ops","ry-ops","https://polar.sh/ry-ops","https://buymeacoffee.com/ry-ops","https://thanks.dev/ry-ops"],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"https://github.com/ry-ops/capture-cadence/blob/main/CaptureCadence.png\" width=\"100%\"\u003e\n\n# CaptureCadence\n\n**Scheduled full-page website screenshot automation.**\n\n[![Status](https://img.shields.io/badge/Status-Archived-lightgrey)](https://github.com/ry-ops/capture-cadence)\n[![Node.js](https://img.shields.io/badge/Node.js-18%2B-339933?logo=node.js\u0026logoColor=white)](https://nodejs.org)\n[![Docker](https://img.shields.io/badge/Docker-Ready-2496ED?logo=docker\u0026logoColor=white)](https://docker.com)\n[![License](https://img.shields.io/badge/License-MIT-yellow)](./LICENSE)\n\n\u003e **Note:** This project is archived and no longer in active development or production use. The codebase is preserved as-is for reference.\n\n---\n\n## What is CaptureCadence?\n\nCaptureCadence is a lightweight tool that takes full-page screenshots of websites on a configurable schedule. Add any URL, set an interval in minutes, and it automatically captures the page using Puppeteer's headless Chrome, saving the result as an efficient WebP image.\n\nIt was built for monitoring website changes, creating visual dashboards, and archiving periodic snapshots of web content.\n\n---\n\n## How It Works\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/architecture.svg\" alt=\"CaptureCadence Architecture\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n1. User adds a site via the web UI (URL, interval, save path, filename)\n2. Express server stores the config in `urls.json` and schedules a `setInterval`\n3. When the interval fires, Puppeteer launches headless Chrome\n4. Navigates to the URL, waits for `networkidle0` (all requests complete)\n5. Takes a full-page screenshot in WebP format\n6. Saves to the configured directory — on restart, all jobs reload automatically\n\n---\n\n## Components\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/components.svg\" alt=\"CaptureCadence Components\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n| Component | Description |\n|-----------|-------------|\n| **Express Server** | HTTP server on port 3000, serves UI, handles API requests, manages scheduler |\n| **Puppeteer Engine** | Headless Chrome automation — full-page capture with network idle detection |\n| **Web UI** | Minimal form-based interface for adding screenshot jobs |\n| **urls.json** | File-based persistence — jobs survive server restarts |\n| **Docker** | Containerized deployment with volume mount for screenshot output |\n\n---\n\n## Features\n\n- Full-page screenshots of any URL using headless Chrome\n- Per-site configurable intervals (in minutes)\n- Custom save paths and filenames\n- WebP format for efficient storage\n- Persistent job configuration (survives restarts)\n- Docker-ready with volume mount support\n- Overwrite mode (fixed filename) or accumulate mode (timestamped)\n\n---\n\n## Project Structure\n\n```\ncapture-cadence/\n├── server.js          # Express server, scheduler, API endpoints\n├── puppeteer.js       # Headless Chrome screenshot capture logic\n├── urls.json          # Persistent job configuration\n├── ui/\n│   └── index.html     # Web interface\n├── Dockerfile         # Docker build (Node.js 20)\n├── package.json       # Dependencies (express, puppeteer, body-parser)\n└── docs/              # Architecture diagrams\n```\n\n---\n\n## Tech Stack\n\n| Category | Technology |\n|----------|-----------|\n| **Runtime** | Node.js 18+ |\n| **Server** | Express.js |\n| **Browser** | Puppeteer (headless Chrome) |\n| **Image Format** | WebP |\n| **Persistence** | File-based (urls.json) |\n| **Deployment** | Docker (Node.js 20 base) |\n\n---\n\n## API\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/` | GET | Serves the web UI |\n| `/add-site` | POST | Add a screenshot job (`{url, interval, savePath, baseName}`) |\n| `/screenshots/*` | GET | Serves captured screenshot files |\n\n---\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fry-ops%2Fcapture-cadence","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fry-ops%2Fcapture-cadence","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fry-ops%2Fcapture-cadence/lists"}