Skip to content

Ju-Long/Edendale

Repository files navigation

Edendale app icon

Edendale

A free, open-source video player and personal watch-tracker for Apple platforms.

Play your own movies and shows, enrich them with TMDB, and keep your watch history in your own iCloud — never on someone else's server.

Platforms Built with SwiftUI Status


Edendale is a SwiftUI player that aims to be easy to use, maximally performant, and double as a centralised place to manage what you've watched and what you think of it — with all personal data stored in your own iCloud.

Features

  • Play local video — open a file directly, or import whole folders of pre-downloaded movies and shows. Playback runs on VLC (via SwiftVLC), so a wide range of formats work: mp4, mkv, avi, webm, ts, mpg, and many more (see LibraryController.supportedExtensions).
  • Build a library from your files — imported folders are scanned and each filename is classified as a movie (Title (2020).mkv) or a TV episode (Show.S01E02.mkv, Show.1x02.mkv) before any network call. The index is persisted immediately; metadata enrichment happens in the background.
  • Enrich with TMDB — posters, overviews, ratings, and episode details are fetched from TMDB and shown alongside your files. You can also browse TMDB for movie/show information independently of local files.
  • Track watch progress in your iCloud — which movies and shows you've watched, and how far into them you are, syncs across your devices via CloudKit. Nothing leaves your device except TMDB requests (for metadata) and your own private iCloud.
  • Planned — centralised review/critics management: your own ratings and reviews, stored the same privacy-first way.

Current status

Active development. The data layer, TMDB service, filename parser, library import, and iCloud watch-progress sync are in place, and the app now has a full UI shell:

  • Platform-adaptive TabView (RootView) — sidebar on iPad/macOS, tab bar on iOS, ornament on visionOS, top tab bar on tvOS — with Movies & Shows, Downloaded, Search, and Settings.
  • A dark "cinematic" theme built entirely from asset-catalog color sets, plus custom fonts (Bebas Neue / Inter) registered at runtime.
  • Movies & Shows — TMDB browse shelves (trending / popular / top-rated / genre collections) with a Continue-Watching hero.
  • Downloaded — the local library: folder import, per-folder rescan/remove, movie & show listings with watch state.
  • A shared media detail view (TMDB and local items) and VLC player presentation: in-place full-screen playback on iOS, visionOS, and tvOS, with a dedicated player window on macOS.

tvOS runs the same shared UI with platform-specific treatment (card-style poster focus, captions overlaid on artwork, trailers and local folder import compiled out — Apple TV has no user-browsable local storage; network sources are the planned path).

Still in progress: the player watch-progress loop (resume/seek + periodic writes), TMDB-only season/episode browsing, the Search tab, a Settings entry point on tvOS, and manual UI verification passes. See TASKS.md for the detailed phase tracker.

Project layout

Edendale/                    App target — thin per-target entry points only
  EdendaleApp.swift          @main; injects stores, registers fonts
  ContentView.swift          Renders RootView
  VideoPlayer.swift          SwiftUI wrapper around SwiftVLC's Player/VideoView
  Info.plist                 Exposes the TMDB key as "TMDBAPIKey"
  Edendale.entitlements      CloudKit container + app group

Shared/                      Code shared across all targets
  AppIdentifiers.swift       Single source of truth for bundle / iCloud / app-group ids
  Persistence.swift          TWO stores — see "Persistence" below
  Models/
    VideoFolder.swift        An imported folder (path + security-scoped bookmark)
    Movie.swift              Local file + optional TMDB metadata (SwiftData)
    TVShow.swift, Episode.swift
    WatchProgress.swift      Value type for watch state
    CDWatchProgress.swift    CoreData entity backing iCloud sync
    MediaRating.swift        Rating value type (TMDB score/votes)
  Controllers/
    LibraryController.swift  Folder import, file scanning, classify → persist → enrich
    MediaParser.swift        Filename → movie(title, year) or episode(show, S, E)
    TMDBService.swift        Thin async TMDB v3 API client (search + episode detail)
    TMDBBrowse.swift         Browse endpoints (trending/popular/discover) + unified DTOs
    TMDBRatingsProvider.swift / RatingsEnvironment.swift   Ratings source
    TMDBAccountStore.swift   TMDB account sign-in (v4 auth flow), token in synced keychain
    KeychainStore.swift      iCloud-synchronized keychain wrapper (Security.framework)
    WatchProgressStore.swift Watch state on CoreData + CloudKit, @Observable cache
  Views/                     All SwiftUI UI (shared across platforms)
    RootView.swift           Adaptive TabView (Movies & Shows / Downloaded / Search / Settings)
    Theme/                   Theme.swift (semantic colors + radii), Typography.swift (+ FontRegistrar)
    Components/              Reusable views (PosterCard, MediaShelf, GlassCard, ScoreTile, …)
    MoviesShows/, Downloaded/, Detail/, Player/, Search/, Settings/   Per-area screens
  Fonts/                     Bebas Neue + Inter (registered at runtime)
  Assets.xcassets/           Colors/, Icons/ (Font Awesome symbol sets), Images/

EdendaleTests/, EdendaleUITests/

Persistence — two deliberate stores

  1. SwiftData, local-only (Persistence.sharedModelContainer): the media library (VideoFolder, Movie, TVShow, Episode). It references device-specific file paths and security-scoped bookmarks, so CloudKit mirroring is explicitly disabled (cloudKitDatabase: .none). Don't re-enable it — the models would also violate CloudKit's "all attributes optional" rule.
  2. CoreData + CloudKit (Persistence.cloudPersistentContainer): watch progress only, keyed by TMDB id + media type, synced through the private iCloud container iCloud.com.BaBaSaMa.Edendale. WatchProgressStore keeps an in-memory @Observable cache and merges remote changes from NSPersistentStoreRemoteChange notifications.

Identifiers (bundle, iCloud container, app group) have a single source of truth in Shared/AppIdentifiers.swift, which must match Edendale.entitlements. Use AppIdentifiers.defaults (the app-group UserDefaults suite) rather than UserDefaults.standard.

Getting started

Requirements: Xcode with the iOS / macOS / visionOS / tvOS SDKs, and an Apple developer account signed in if you want CloudKit sync to work on device.

  1. Clone the repo and open Edendale.xcodeproj.
  2. Create your secrets file from the template:
    cp Shared/Example.xcconfig Shared/Secrets.xcconfig
  3. Sign up for free TMDB API access at https://www.themoviedb.org/settings/api, copy the API Read Access Token (the long eyJ… token), and paste it after TMDB_READ_ACCESS_TOKEN = in Shared/Secrets.xcconfig. (The short v3 "API Key" still works as a legacy fallback via TMDB_API_KEY =.)
  4. Build & run.

Secrets.xcconfig is gitignored — the token flows Secrets.xcconfig → Info.plist (TMDBReadAccessToken) → read from the bundle at runtime and sent as an Authorization: Bearerheader (seeTMDBService`). Builds work without a token; TMDB enrichment just returns nothing. Never commit a real token or key.

Dependencies (Swift Package Manager)

Package Purpose
SwiftVLC Video playback (libVLC wrapper)
Kingfisher Poster/backdrop image loading & caching
Alamofire Networking
Drops Lightweight in-app alerts

Principles

  • Free and open source — no accounts, no subscriptions, no telemetry.
  • Your data stays yours — watch history and reviews live in your personal iCloud.
  • Performance first — persist fast, enrich in the background, cache aggressively.

This product uses the TMDB API but is not endorsed or certified by TMDB.

About

iOS, iPadOS, tvOS, macOS, visionOS app for movie critics and multi-format video player

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages