feat: add app-owned YouTube subtitle flow with absPlayer-style parsing (#31)

* fix: harden preload argv parsing for popup windows

* fix: align youtube playback with shared overlay startup

* fix: unwrap mpv youtube streams for anki media mining

* docs: update docs for youtube subtitle and mining flow

* refactor: unify cli and runtime wiring for startup and youtube flow

* feat: update subtitle sidebar overlay behavior

* chore: add shared log-file source for diagnostics

* fix(ci): add changelog fragment for immersion changes

* fix: address CodeRabbit review feedback

* fix: persist canonical title from youtube metadata

* style: format stats library tab

* fix: address latest review feedback

* style: format stats library files

* test: stub launcher youtube deps in CI

* test: isolate launcher youtube flow deps

* test: stub launcher youtube deps in failing case

* test: force x11 backend in launcher ci harness

* test: address latest review feedback

* fix(launcher): preserve user YouTube ytdl raw options

* docs(backlog): update task tracking notes

* fix(immersion): special-case youtube media paths in runtime and tracking

* feat(stats): improve YouTube media metadata and picker key handling

* fix(ci): format stats media library hook

* fix: address latest CodeRabbit review items

* docs: update youtube release notes and docs

* feat: auto-load youtube subtitles before manual picker

* fix: restore app-owned youtube subtitle flow

* docs: update youtube playback docs and config copy

* refactor: remove legacy youtube launcher mode plumbing

* fix: refine youtube subtitle startup binding

* docs: clarify youtube subtitle startup behavior

* fix: address PR #31 latest review follow-ups

* fix: address PR #31 follow-up review comments

* test: harden youtube picker test harness

* udpate backlog

* fix: add timeout to youtube metadata probe

* docs: refresh youtube and stats docs

* update backlog

* update backlog

* chore: release v0.9.0
This commit is contained in:
2026-03-24 00:01:24 -07:00
committed by GitHub
parent c17f0a4080
commit 5feed360ca
219 changed files with 12778 additions and 1052 deletions

View File

@@ -35,6 +35,9 @@ export class AnkiConnectProxyServer {
private pendingNoteIdSet = new Set<number>();
private inFlightNoteIds = new Set<number>();
private processingQueue = false;
private readyPromise: Promise<void> | null = null;
private resolveReady: (() => void) | null = null;
private rejectReady: ((error: Error) => void) | null = null;
constructor(private readonly deps: AnkiConnectProxyServerDeps) {
this.client = axios.create({
@@ -48,6 +51,13 @@ export class AnkiConnectProxyServer {
return this.server !== null;
}
waitUntilReady(): Promise<void> {
if (!this.server || this.server.listening) {
return Promise.resolve();
}
return this.readyPromise ?? Promise.resolve();
}
start(options: StartProxyOptions): void {
this.stop();
@@ -58,15 +68,26 @@ export class AnkiConnectProxyServer {
return;
}
this.readyPromise = new Promise<void>((resolve, reject) => {
this.resolveReady = resolve;
this.rejectReady = reject;
});
this.server = http.createServer((req, res) => {
void this.handleRequest(req, res, options.upstreamUrl);
});
this.server.on('error', (error) => {
this.rejectReady?.(error as Error);
this.resolveReady = null;
this.rejectReady = null;
this.deps.logError('[anki-proxy] Server error:', (error as Error).message);
});
this.server.listen(options.port, options.host, () => {
this.resolveReady?.();
this.resolveReady = null;
this.rejectReady = null;
this.deps.logInfo(
`[anki-proxy] Listening on http://${options.host}:${options.port} -> ${options.upstreamUrl}`,
);
@@ -79,6 +100,10 @@ export class AnkiConnectProxyServer {
this.server = null;
this.deps.logInfo('[anki-proxy] Stopped');
}
this.rejectReady?.(new Error('AnkiConnect proxy stopped before becoming ready'));
this.readyPromise = null;
this.resolveReady = null;
this.rejectReady = null;
this.pendingNoteIds = [];
this.pendingNoteIdSet.clear();
this.inFlightNoteIds.clear();