{"id":50575856,"url":"https://github.com/kevm/20four7","last_synced_at":"2026-06-04T22:01:24.969Z","repository":{"id":361466003,"uuid":"1254569516","full_name":"KevM/20four7","owner":"KevM","description":"Ambient YouTube livestream channel surfer for iOS/iPadOS — fireplaces, rain, lofi, aquariums, and more. SwiftUI.","archived":false,"fork":false,"pushed_at":"2026-05-30T19:52:22.000Z","size":659,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T20:13:39.314Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","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/KevM.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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-30T18:25:56.000Z","updated_at":"2026-05-30T19:52:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/KevM/20four7","commit_stats":null,"previous_names":["kevm/20four7"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/KevM/20four7","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevM%2F20four7","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevM%2F20four7/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevM%2F20four7/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevM%2F20four7/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KevM","download_url":"https://codeload.github.com/KevM/20four7/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KevM%2F20four7/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33921352,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"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":[],"created_at":"2026-06-04T22:01:22.514Z","updated_at":"2026-06-04T22:01:24.963Z","avatar_url":"https://github.com/KevM.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 20Four7\n\n[![CI](https://github.com/KevM/20four7/actions/workflows/ci.yml/badge.svg)](https://github.com/KevM/20four7/actions/workflows/ci.yml)\n\nA lean-back, TV-like iOS/iPadOS app for watching always-on **ambient YouTube\nlivestreams** — fireplaces, rain, lofi radio, aquariums, nature, cityscapes,\nspace. Content is a **curated, remotely-updatable catalog** plus your own\n**user-added channels**. The experience blends browsing a **Channel Guide** with\n**channel-surfing** in fullscreen.\n\n## Features\n\n- **Ambient playback** of YouTube livestreams via YouTube's official IFrame\n  Player API (the only ToS-compliant embedding path).\n- **Hybrid navigation** — browse a tag-filtered Guide, then surf ▲/▼ while watching.\n- **Ambient extras** — sleep timer, clock/dim overlay, auto-resume of the last\n  channel, best-effort audio-only.\n- **Curated catalog** updatable without an App Store release, with a bundled\n  fallback so the app is never empty (live → cached → bundled resilience ladder).\n- **Background liveness scanning** — channels are quietly probed for offline/VOD\n  status so the Guide hides dead feeds, with cooldown and cellular-aware throttling.\n- **User channels** — paste a URL or `@handle`, validated at add-time, tagged,\n  and stored locally.\n- **Tag-native data model** — editorial tags (from the catalog) and private user\n  tags.\n\n## Architecture\n\nNative **SwiftUI**, built around independently-testable units that communicate\nthrough well-defined interfaces:\n\n- **`PlayerService`** (protocol) — the swappable playback abstraction and the\n  most important boundary in the app. The iOS implementation\n  (`WebViewPlayerService`) wraps the YouTube IFrame player in a `WKWebView`;\n  everything else talks to the protocol, never the web view. This quarantines\n  YouTube's fragility behind one interface and makes the whole app testable\n  against a `MockPlayerService`.\n- **`ChannelStore`** — merges the remote curated catalog with user-added\n  channels; owns dedupe, ordering, and dynamic tag sorting; the source of truth\n  for the filterable lineup.\n- **`PlaybackController`** — app-level \"what's playing now\" state: surf\n  next/previous, sleep timer, auto-surf, audio-only, auto-resume. Drives a\n  `PlayerService` and depends on an injectable `Clock` so all timer behavior is\n  tested deterministically (no real waiting).\n- **`RemoteConfig`** — fetches/caches the versioned curated catalog with a\n  bundled fallback.\n- **`BackgroundLineupScanner`** — off-screen `WKWebView` that probes channels\n  for offline/VOD status, with a cooldown and cellular-aware throttle.\n- **`LocalStore`** — the single owner of the SwiftData `ModelContext`; a thin\n  CRUD facade so the rest of the app never touches SwiftData directly.\n- **`AppEnvironment`** — the composition root that wires concrete dependencies\n  together once, at launch.\n- **UI** — `GuideView`, `PlayerView` (fullscreen + overlays), `AddChannelView`,\n  `SettingsView`.\n\nPersistence uses **SwiftData** (local-only today; structured so CloudKit sync\nand a tvOS target can be added later without a rewrite).\n\nSee [`docs/superpowers/specs/`](docs/superpowers/specs/) for the full design.\n\n## Requirements\n\n- Xcode 16+ (Swift 6)\n- iOS / iPadOS 17.0+\n- [XcodeGen](https://github.com/yonaskolb/XcodeGen) (`brew install xcodegen`)\n\n## Getting started\n\nThe Xcode project is generated from [`project.yml`](project.yml) and is not\nchecked in. Generate it with [`generate.sh`](generate.sh) (a thin wrapper around\n`xcodegen generate` that also loads `.env`), then open and run:\n\n```sh\n./generate.sh\nopen 20Four7.xcodeproj\n```\n\nTo build to a device, set your signing team — either in Xcode (Signing \u0026\nCapabilities) or by adding `DEVELOPMENT_TEAM=XXXXXXXXXX` to a `.env` file (which\nis git-ignored) and re-running `./generate.sh`.\n\n### Catalog configuration\n\nThe curated catalog base URL is a build-time constant in\n[`Sources/App/Config.swift`](Sources/App/Config.swift) and defaults to a\nplaceholder (`cdn.example.com`). Point it at your own static host that serves:\n\n```\n\u003cbase\u003e/channels-manifest.json   ← stable entry point, fetched first\n\u003cbase\u003e/catalog-v{N}.json         ← versioned catalog payload\n```\n\nA bundled [`catalog-fallback.json`](Sources/Resources/catalog-fallback.json)\nships with the app so it works offline on first launch.\n\n## Tests\n\n```sh\nxcodebuild test -scheme 20Four7 -destination 'platform=iOS Simulator,name=iPhone 16'\n```\n\nData, config, and parsing logic are covered by unit tests against a mock player\n(no network or YouTube required). The web view is exercised by manual smoke\ntests, not unit tests.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkevm%2F20four7","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkevm%2F20four7","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkevm%2F20four7/lists"}