- Move native shells (Zig/src, Tauri/src-tauri, app.zon, releases) to legacy/
- Add Bun + Hono server with yt-dlp proxy endpoints (search, channel, streams,
download), libsql (concurrent SQLite fork) for fingerprint-keyed playlist/
history sync, and static file serving for the frontend
- Add Dockerfile + docker-compose.yml (single container, volume-mounted DB)
- Add frontend/sw.js: app-shell cache-first, /api/* network-only,
thumbnails stale-while-revalidate, SW_UPDATE_AVAILABLE broadcast,
SKIP_WAITING message handler for seamless auto-update
- Add frontend/manifest.webmanifest: standalone PWA, vermilion theme,
search/history shortcuts
- Add frontend/icons/icon-{192,512}.png: generated PWA icons
- Add frontend/fingerprint.js: canvas+UA djb2 fingerprint, localStorage-cached,
exposes window.getFingerprint() for server-side playlist keying
- Add frontend/opfs.js: full OPFS video store (writeFromResponse streams
directly without full-file buffering), exposes window.OPFS
- Add scripts/make-pwa-icons.js: regenerate icons without external deps
- Patch frontend/app.js: WEB mode detection, webFetch + opfs* bridge wrappers,
API object routes to WEB helpers when no native bridge present,
Player.loadVideo handles OPFS blob URLs + revokes them on next load,
SW registration + update banner in boot()
- Patch frontend/index.html: manifest link, theme-color, Apple PWA meta,
CSP blob:/worker-src, fingerprint.js + opfs.js script tags
- Patch frontend/styles.css: .toast-update + .toast-reload-btn for update banner
- Native Tauri/Zig builds unchanged — all new code is additive via WEB flag
59 lines
2.2 KiB
Zig
Executable File
59 lines
2.2 KiB
Zig
Executable File
//! YT Player — zero-native app entry.
|
|
//!
|
|
//! This file shows how the App is wired to the runtime and how the bridge
|
|
//! handlers in `bridge.zig` are registered. When you scaffold with
|
|
//! `zero-native init`, a `src/main.zig` + `src/runner.zig` + `build.zig` are
|
|
//! generated for your installed version. Merge the `bridge()` registration and
|
|
//! the `handlers`/`policies` below into that generated App — the handler
|
|
//! implementations themselves live in `bridge.zig` and need no changes.
|
|
|
|
const std = @import("std");
|
|
const zero_native = @import("zero_native");
|
|
const handlers_impl = @import("bridge.zig");
|
|
|
|
const Handler = zero_native.bridge.Handler;
|
|
|
|
// Commands the UI is allowed to call, matched against window.zero.invoke names.
|
|
const policies = [_]zero_native.bridge.CommandPolicy{
|
|
.{ .command = "yt.search" },
|
|
.{ .command = "yt.channel" },
|
|
.{ .command = "yt.streams" },
|
|
.{ .command = "store.load" },
|
|
.{ .command = "store.save" },
|
|
};
|
|
|
|
pub const App = struct {
|
|
handlers: [5]Handler = undefined,
|
|
|
|
pub fn app(self: *App) zero_native.App {
|
|
return .{
|
|
.context = self,
|
|
.name = "YT Player",
|
|
// Serve the packaged static UI from the zero://app origin.
|
|
.source = zero_native.WebViewSource.packaged("frontend", "index.html"),
|
|
.bridge = bridge(self),
|
|
};
|
|
}
|
|
|
|
fn bridge(self: *App) zero_native.BridgeDispatcher {
|
|
self.handlers = .{
|
|
.{ .name = "yt.search", .context = self, .invoke_fn = handlers_impl.ytSearch },
|
|
.{ .name = "yt.channel", .context = self, .invoke_fn = handlers_impl.ytChannel },
|
|
.{ .name = "yt.streams", .context = self, .invoke_fn = handlers_impl.ytStreams },
|
|
.{ .name = "store.load", .context = self, .invoke_fn = handlers_impl.storeLoad },
|
|
.{ .name = "store.save", .context = self, .invoke_fn = handlers_impl.storeSave },
|
|
};
|
|
return .{
|
|
.policy = .{ .enabled = true, .commands = &policies },
|
|
.registry = .{ .handlers = &self.handlers },
|
|
};
|
|
}
|
|
};
|
|
|
|
pub fn main() !void {
|
|
var instance: App = .{};
|
|
var runtime = try zero_native.Runtime.init(std.heap.page_allocator);
|
|
defer runtime.deinit();
|
|
try runtime.run(instance.app());
|
|
}
|