yt-dlp for page links in every Link mode; app icon and Linux launcher entry

yt-dlp: a managed, checksum-verified, daily-updated yt-dlp in the app and
the dedicated server (--[no-]yt-dlp, --yt-dlp-dir). Pasted links are
probed: direct files stay links, pages are resolved for title, uploader,
duration and thumbnail. Everyone fetches it: mpv's ytdl hook on the
managed copy. I pass it on / the server fetches it: the best single-file
format, re-resolved once when its URL expires. Relay names never split
the /r/ path; viewers of server-fetched pages wait for the relay to go
live. No wire change.

Icon: two stars orbiting a play button (assets/icon/icon.svg, PNGs, the
Windows .ico). GTK app id moe.senkochan.watch_together; release builds
install their .desktop file and icons into ~/.local/share so Wayland
shows the icon; the window icon is set for X11. README shows the icon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-09 18:21:22 +03:00
1 parent 9ddd85de90
commit 745717a4a0
40 files changed
+1444 -123

No files matched your search

+10
View File
@@ -19,6 +19,12 @@ Future<void> main(List<String> args) async {
..addOption('jellyfin',
help: 'YAML or JSON file with a Jellyfin login (baseUrl, token, client, device, deviceId, version[, serverId]) '
'to relay Jellyfin media to viewers without an account. The device fields must be the ones the token was issued with.')
..addFlag('yt-dlp',
defaultsTo: true,
help: 'Read web pages (YouTube, Twitter/X, ...) for "the server fetches it" with yt-dlp, '
'downloaded on first use.')
..addOption('yt-dlp-dir',
defaultsTo: '', help: 'Writable folder for that yt-dlp (default: yt-dlp next to this binary).')
..addOption('config', help: 'YAML or JSON config file (keys as in AppConfig); overrides the other flags.')
..addFlag('help', abbr: 'h', negatable: false, help: 'Show this help.');
final ArgResults parsed;
@@ -52,6 +58,8 @@ Future<void> main(List<String> args) async {
name: '${parsed['name']}',
mediaPath: '${parsed['media']}',
advertiseHost: '${parsed['advertise-host']}',
ytDlp: parsed['yt-dlp'] as bool,
ytDlpDir: '${parsed['yt-dlp-dir']}',
);
}
final jellyfinPath = parsed['jellyfin'] as String?;
@@ -66,6 +74,8 @@ Future<void> main(List<String> args) async {
advertiseHost: c.advertiseHost,
hostKey: c.hostKey,
jellyfin: await JellyfinCredential.loadFile(jellyfinPath),
ytDlp: c.ytDlp,
ytDlpDir: c.ytDlpDir,
);
}
} on ConfigException catch (e) {