About Projects Experience Contact
Back to Projects

Desktop systems case study / shipped product / Rust & Tauri

Deskemy

An offline player for the video courses you already own. Point it at a folder and it becomes a browsable library with native playback, resume, subtitle search, and study tools — no accounts, no telemetry, and no network calls for your content.

52 GitHub stars from real users
261 installer and portable downloads
6 releases shipped in the first 6 days
0 accounts, telemetry, or content network calls
Deskemy library home screen showing Continue Watching and the course grid
The library: Continue Watching picks up the exact lecture you left off on, with the course grid and aggregate progress underneath.

Why This Project Exists

I had a folder of downloaded video courses and no good way to actually work through them. A generic media player forgets where you were, knows nothing about sections or lectures, and cannot tell you whether you have finished a course. The web players these courses come from need an account and a connection. What I wanted was something in between: a player that understands course structure, remembers everything, and works on a plane.

So Deskemy is a desktop app that treats a folder of videos as a first-class course. It reads the folder structure into sections and ordered lectures, cleans the numeric prefixes out of the titles, attaches subtitles and resource files to the lecture they belong to, and then gets out of the way. Your files are referenced where they sit — never copied, moved, or uploaded — and everything the app records lives in one local SQLite database.

Outcome: It found an audience I did not plan for. 52 stars, 261 installer and portable downloads across six releases, and a community-maintained Simplified Chinese translation someone forked and now keeps up on their own.

My Role

Solo project, end to end: the Rust core, the Svelte front end, the playback integration, the SQLite schema and search, the Windows packaging and installer, the auto-updater feed, and the release process. The interesting engineering was almost all at the boundaries — between a web UI and a native video surface, and between a media library and the filesystem underneath it.

Architecture

Deskemy is a Tauri v2 app: a SvelteKit SPA in a WebView2 window, talking over IPC to a Rust core that owns the database, the importer, and the player. The one thing the web layer never touches is the video itself. That is handled natively, and composited underneath the UI.

flowchart TB
  subgraph win[Deskemy window - Tauri v2]
    ui[SvelteKit UI - transparent WebView2]
    subgraph core[Rust core]
      cmd[Commands + events]
      imp[Two-phase importer: scan, probe, persist]
      ply[Player control via FFI]
    end
    dcomp[DirectComposition video surface]
  end

  mpv[libmpv-2.dll bundled]
  db[(SQLite + FTS5 via rusqlite)]
  thumb[(Content-addressed thumbnail cache)]
  files[/Your course folders, referenced in place/]

  ui -->|IPC| cmd
  cmd -->|events| ui
  cmd --> imp
  cmd --> ply
  cmd --> db
  cmd --> thumb
  imp -->|probe| mpv
  imp --> files
  imp --> db
  ply --> mpv
  ply --> db
  mpv -->|renders| dcomp
  dcomp -. shows through .-> ui
                    

Engineering Highlights

1. Native playback: libmpv over FFI, not an HTML5 video tag

Problem: downloaded course videos are whatever the uploader exported — arbitrary containers, codecs, and subtitle formats. A WebView's <video> element supports a narrow slice of that, so a browser-based player would either refuse to play files or force users to transcode their library.

Implementation: playback goes through libmpv, mpv's media library, loaded at runtime through FFI with libloading rather than linked at build time. That means codec coverage is mpv's, not the WebView's. libmpv-2.dll ships inside both the installer and the portable zip so there is nothing extra to install, and the loader falls back to a libmpv-2.dll on PATH or at DESKEMY_LIBMPV for anyone who would rather supply their own build.

2. Compositing native video underneath a web UI

Problem: once video is rendered by a native library instead of the WebView, the two have to share a window. The naive approach — a native child window sitting on top — puts the video above the entire web layer, so no HTML control can ever draw over it. Overlays, the seek bar, and the contents panel would all be hidden behind the picture.

Implementation: mpv renders into a DirectComposition surface placed behind a transparent WebView2. The video shows through the parts of the UI that are see-through, while the player chrome, panels, and overlays stay real DOM elements layered on top. The UI keeps the flexibility of HTML and CSS, and the video path stays fully native.

Deskemy player with the course-contents panel open over the video
The payoff: the course-contents panel and player chrome are ordinary DOM, layered over natively rendered video.
3. Two-phase import so a big course never freezes the UI

Problem: importing a course means probing every video for duration and track metadata. Probing is slow and I/O-bound. Doing it while holding the database connection would serialize the whole import behind media inspection and leave the interface unresponsive on a large folder.

Implementation: the importer runs in two phases — probe, then persist. Media probing happens off the database connection entirely, and only the resulting metadata is written in the persist phase. Before anything is committed, the user gets a preview of exactly what will be imported: sections, lectures, resources, subtitles, and total runtime, with live progress during the scan.

4. Rename-safe progress: identity from content, not path

Problem: if a library row is keyed by file path, then reorganizing your folders throws away your history. Rename a directory and every lecture underneath it looks brand new — progress gone, bookmarks orphaned. For an app whose whole value is remembering things, that is the worst possible failure.

Implementation: lectures are matched by content rather than path, so a moved or renamed file keeps its watch progress and bookmarks. The thumbnail cache is content-addressed on the same principle. An optional folder auto-rescan picks up changes on its own, and a storage panel makes the cache's disk use visible and reclaimable rather than mysterious.

5. Searching the words that were actually spoken

Problem: title search only finds a lecture if you remember roughly what it was called. Often you remember a phrase the instructor said and have no idea which of two hundred lectures it was in.

Implementation: SQLite FTS5 — bundled through rusqlite, so there is no external database — indexes course, section, and lecture titles for full-text search. On top of that, optional subtitle search indexes the spoken text from your subtitle files and jumps straight to the matching timestamp. You search for a sentence and land on the second it was said.

Deskemy search results across courses, sections, lectures, and subtitle text
Search spans courses, sections, and lectures, and optionally the subtitle text underneath them.
6. Two install modes, two data-residency guarantees

Problem: "portable" is a promise about more than the executable. If an app claims to run from a USB stick but quietly writes settings to %APPDATA% and a key into the registry, it has left traces on a machine that was never supposed to keep any.

Implementation: the installed build is a per-user install needing no admin rights, keeping its library under %APPDATA%\com.spooksy.deskemy and deliberately leaving that folder behind on uninstall so a reinstall resumes where you left off. The portable build detects a .portable marker beside the executable and redirects all state — library, settings, thumbnails — into a data/ folder next to it. Nothing touches %APPDATA% or the registry; deleting the folder removes the app completely.

Study Tools

Beyond playback, the app tracks the things that make a course feel finishable. Continue Watching resumes the exact lecture. Bookmarks are timestamped, and there are tags, favorites, and a watch history. Career Tracks group courses into an ordered path with aggregate completion. Playback preferences — speed, subtitle, and audio track — are remembered per course, and there is a full set of YouTube-style keyboard shortcuts so the mouse is optional.

Deskemy stats dashboard with watch-time streaks and an activity heatmap
Watch-time stats: an activity heatmap, streaks, and a daily goal.
Deskemy course view with sections and ordered lectures
Course view: sections and ordered lectures with per-lecture progress.
Deskemy fullscreen player
Fullscreen playback with the chrome dismissed.
Deskemy watch history
Watch history across every course in the library.
Deskemy keyboard shortcuts cheat sheet
The shortcut cheat sheet, one ? away at any time.

Privacy By Construction

There are no accounts, no telemetry, and no network requests for your content. The library, progress, bookmarks, and stats exist only in a local database, and the app works entirely offline. This is not a policy page — it is a consequence of the architecture. There is no server to send anything to.

Tradeoffs And Honest Limits

Deskemy is Windows-only. The DirectComposition compositing that makes the player work is a Windows API, so macOS and Linux would each need their own equivalent path rather than a recompile. Bundling libmpv-2.dll also makes the download noticeably larger than a pure web-stack app would be — a deliberate trade for playing files that would otherwise not play at all.

Subtitle search only knows what is in your subtitle files; there is no transcription step, so a course shipped without subtitles is not searchable by speech. The community translations are maintained as separate projects, which means they can trail a release — a real cost of not having built localization into the app from the start.

What This Demonstrates

This is the project where I had to be most careful at the seams between a managed runtime and the operating system: loading a C library through FFI at runtime, getting a native video surface to composite correctly with a browser engine, and deciding what identity means for a file that can move. It is also the one with actual users, which changed how I worked — six releases in the first six days, because bug reports arrived from people who were not me.