feat: expand subtitle and media tracking workflows

- Add reference-guided subtitle timing, frame picking, live-action Jimaku search, and YouTube library kinds
- Harden Jellyfin media identity handling and live settings feedback
- Refresh user-facing documentation and changelog fragments
This commit is contained in:
2026-09-20 20:10:03 -07:00
139 changed files with 4511 additions and 389 deletions
+22 -1
View File
@@ -21,6 +21,8 @@ export interface MediaTimingReviewRequest {
noteId?: number;
audioPadding: number;
maxMediaDuration: number;
/** Still screenshots only; animated images continue to follow the audio range. */
screenshotEnabled?: boolean;
}
/** A subtitle line adjacent to the mined one that the review can pull onto the card. */
@@ -32,7 +34,13 @@ export interface MediaTimingReviewContextLine {
export type MediaTimingReviewDecision =
/** `text` is set when the review combined adjacent lines into the card sentence. */
| { action: 'confirm'; startTime: number; endTime: number; text?: string }
| {
action: 'confirm';
startTime: number;
endTime: number;
text?: string;
screenshotTime?: number;
}
| { action: 'use-original' }
| { action: 'skip-media' }
| { action: 'discard' };
@@ -53,6 +61,19 @@ export interface MediaTimingReviewOpenPayload {
timelineEndTime: number;
mediaDuration?: number;
maxMediaDuration: number;
screenshotEnabled?: boolean;
}
export interface MediaTimingReviewFrameRequest {
reviewId: string;
timestamp: number;
/** Step to the adjacent decoded frame instead of seeking to a time. */
direction?: -1 | 1;
}
export interface MediaTimingReviewFrameResult extends MediaTimingReviewActionResult {
dataUrl?: string;
timestamp?: number;
}
export interface MediaTimingReviewPreviewRequest {
+4
View File
@@ -224,8 +224,12 @@ export interface JimakuMediaInfo {
rawTitle: string;
}
export type JimakuSearchCategory = 'anime' | 'liveAction';
export interface JimakuSearchQuery {
query: string;
// Which Jimaku catalogue to search; defaults to anime when omitted.
category?: JimakuSearchCategory;
}
export interface JimakuEntryFlags {
+5
View File
@@ -8,6 +8,8 @@ import type {
MediaTimingReviewOpenPayload,
MediaTimingReviewPreviewRequest,
MediaTimingReviewResolveRequest,
MediaTimingReviewFrameRequest,
MediaTimingReviewFrameResult,
MediaTimingReviewWaveformRequest,
MediaTimingReviewWaveformResult,
} from './anki';
@@ -553,6 +555,9 @@ export interface ElectronAPI {
previewMediaTimingReview: (
request: MediaTimingReviewPreviewRequest,
) => Promise<MediaTimingReviewActionResult>;
getMediaTimingReviewFrame: (
request: MediaTimingReviewFrameRequest,
) => Promise<MediaTimingReviewFrameResult>;
getMediaTimingReviewWaveform: (
request: MediaTimingReviewWaveformRequest,
) => Promise<MediaTimingReviewWaveformResult>;
+3
View File
@@ -1,3 +1,4 @@
import type { MediaKind } from '../shared/media-kind';
export interface SessionSummary {
sessionId: number;
canonicalTitle: string | null;
@@ -240,6 +241,7 @@ export const EventType = {
export type EventType = (typeof EventType)[keyof typeof EventType];
export interface AnimeLibraryItem {
mediaKind: MediaKind;
animeId: number;
canonicalTitle: string;
anilistId: number | null;
@@ -261,6 +263,7 @@ export interface AnilistEntry {
export interface AnimeDetailData {
detail: {
mediaKind: MediaKind;
animeId: number;
canonicalTitle: string;
anilistId: number | null;
+2
View File
@@ -246,6 +246,8 @@ export interface SubtitleMiningContext {
capturedAtMs?: number;
/** Explicit generator padding. Confirmed timing-review ranges set this to zero. */
mediaPaddingSeconds?: number;
/** Independent still screenshot selected during media timing review. */
screenshotTime?: number;
}
export interface SubtitleHoverTokenPayload {