Files
netfelix-audio-fix/src/types.ts
Felix Förtsch ea536ce533 initial implementation: jellyfin audio/subtitle cleanup service
bun + hono + htmx service with sqlite, jellyfin/radarr/sonarr api
clients, stream analyzer, ffmpeg command builder, ssh remote execution,
setup wizard, scan with sse progress, review ui with inline edits,
execute queue, remote node management, docker deployment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 22:29:33 +01:00

161 lines
4.2 KiB
TypeScript

// ─── Database row types ───────────────────────────────────────────────────────
export interface MediaItem {
id: number;
jellyfin_id: string;
type: 'Movie' | 'Episode';
name: string;
series_name: string | null;
series_jellyfin_id: string | null;
season_number: number | null;
episode_number: number | null;
year: number | null;
file_path: string;
file_size: number | null;
container: string | null;
original_language: string | null;
orig_lang_source: 'jellyfin' | 'radarr' | 'sonarr' | 'manual' | null;
needs_review: number;
imdb_id: string | null;
tmdb_id: string | null;
tvdb_id: string | null;
scan_status: 'pending' | 'scanned' | 'error';
scan_error: string | null;
last_scanned_at: string | null;
created_at: string;
}
export interface MediaStream {
id: number;
item_id: number;
stream_index: number;
type: 'Video' | 'Audio' | 'Subtitle' | 'Data' | 'EmbeddedImage';
codec: string | null;
language: string | null;
language_display: string | null;
title: string | null;
is_default: number;
is_forced: number;
is_hearing_impaired: number;
channels: number | null;
channel_layout: string | null;
bit_rate: number | null;
sample_rate: number | null;
}
export interface ReviewPlan {
id: number;
item_id: number;
status: 'pending' | 'approved' | 'skipped' | 'done' | 'error';
is_noop: number;
notes: string | null;
reviewed_at: string | null;
created_at: string;
}
export interface StreamDecision {
id: number;
plan_id: number;
stream_id: number;
action: 'keep' | 'remove';
target_index: number | null;
}
export interface Job {
id: number;
item_id: number;
command: string;
node_id: number | null;
status: 'pending' | 'running' | 'done' | 'error';
output: string | null;
exit_code: number | null;
created_at: string;
started_at: string | null;
completed_at: string | null;
}
export interface Node {
id: number;
name: string;
host: string;
port: number;
username: string;
private_key: string;
ffmpeg_path: string;
work_dir: string;
status: 'unknown' | 'ok' | 'error';
last_checked_at: string | null;
created_at: string;
}
// ─── Analyzer types ───────────────────────────────────────────────────────────
export interface StreamWithDecision extends MediaStream {
action: 'keep' | 'remove';
target_index: number | null;
}
export interface PlanResult {
is_noop: boolean;
decisions: Array<{ stream_id: number; action: 'keep' | 'remove'; target_index: number | null }>;
notes: string | null;
}
// ─── Jellyfin API types ───────────────────────────────────────────────────────
export interface JellyfinMediaStream {
Type: string;
Index: number;
Codec?: string;
Language?: string;
DisplayLanguage?: string;
Title?: string;
IsDefault?: boolean;
IsForced?: boolean;
IsHearingImpaired?: boolean;
Channels?: number;
ChannelLayout?: string;
BitRate?: number;
SampleRate?: number;
}
export interface JellyfinItem {
Id: string;
Type: string;
Name: string;
SeriesName?: string;
SeriesId?: string;
ParentIndexNumber?: number;
IndexNumber?: number;
ProductionYear?: number;
Path?: string;
Size?: number;
Container?: string;
MediaStreams?: JellyfinMediaStream[];
ProviderIds?: Record<string, string>;
}
export interface JellyfinUser {
Id: string;
Name: string;
}
// ─── Scan state ───────────────────────────────────────────────────────────────
export interface ScanProgress {
total: number;
scanned: number;
current_item: string;
errors: number;
running: boolean;
}
// ─── SSE event helpers ────────────────────────────────────────────────────────
export type SseEventType = 'progress' | 'log' | 'complete' | 'error';
export interface SseEvent {
type: SseEventType;
data: unknown;
}