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>
161 lines
4.2 KiB
TypeScript
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;
|
|
}
|