remove jellyfin, mqtt, webhook services, fix tests, add schema migrations

- delete server/services/jellyfin.ts, webhook.ts, mqtt.ts and their tests
- strip jellyfin/mqtt imports and startup calls from index.tsx and settings.ts
- remove /jellyfin, /mqtt, /mqtt/status, /mqtt/test, /jellyfin/webhook-plugin endpoints from settings router
- clean ENV_MAP and isEnvConfigured of jellyfin/mqtt keys
- add db/index.ts migrations for series_key, duration_seconds, scan_status, scan_error, last_scanned_at (new columns absent on older dev DBs)
- move idx_media_items_series_key out of SCHEMA into migrate() so it runs after the column is added
- fix all test fixtures: drop jellyfin_id/series_jellyfin_id column refs, update MediaItem/MediaStream object literals to match current types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-20 19:33:29 +02:00
parent 6b01de5f30
commit 686434f5c3
17 changed files with 37 additions and 951 deletions
+2 -2
View File
@@ -15,9 +15,9 @@ function makeDb(): Database {
function seedQueuedItem(db: Database, id: number, autoClass: "auto" | "auto_heuristic" | "manual") {
db
.prepare(
"INSERT INTO media_items (id, jellyfin_id, type, name, file_path, container) VALUES (?, ?, 'Movie', ?, ?, 'mkv')",
"INSERT INTO media_items (id, type, name, file_path, container) VALUES (?, 'Movie', ?, ?, 'mkv')",
)
.run(id, `jf-${id}`, `Item ${id}`, `/x/${id}.mkv`);
.run(id, `Item ${id}`, `/x/${id}.mkv`);
db
.prepare(
"INSERT INTO review_plans (item_id, status, is_noop, auto_class, sorted, apple_compat, job_type) VALUES (?, 'approved', 0, ?, 1, 'direct_play', 'copy')",
@@ -15,9 +15,9 @@ function makeDb(): Database {
function seedSortedPlan(db: Database, id: number, autoClass: "auto_heuristic" | "manual") {
db
.prepare(
"INSERT INTO media_items (id, jellyfin_id, type, name, file_path, container) VALUES (?, ?, 'Movie', ?, ?, 'mkv')",
"INSERT INTO media_items (id, type, name, file_path, container) VALUES (?, 'Movie', ?, ?, 'mkv')",
)
.run(id, `jf-${id}`, `Item ${id}`, `/x/${id}.mkv`);
.run(id, `Item ${id}`, `/x/${id}.mkv`);
db
.prepare(
"INSERT INTO media_streams (item_id, stream_index, type, codec, language) VALUES (?, 0, 'Audio', 'eac3', 'eng')",
+10 -10
View File
@@ -17,7 +17,7 @@ interface SeedOpts {
type: "Movie" | "Episode";
name?: string;
seriesName?: string | null;
seriesJellyfinId?: string | null;
seriesKey?: string | null;
seasonNumber?: number | null;
episodeNumber?: number | null;
autoClass?: "auto" | "auto_heuristic" | "manual" | null;
@@ -30,7 +30,7 @@ function seed(db: Database, opts: SeedOpts) {
type,
name = `Item ${id}`,
seriesName = null,
seriesJellyfinId = null,
seriesKey = null,
seasonNumber = null,
episodeNumber = null,
autoClass = "manual",
@@ -38,9 +38,9 @@ function seed(db: Database, opts: SeedOpts) {
} = opts;
db
.prepare(
"INSERT INTO media_items (id, jellyfin_id, type, name, series_name, series_jellyfin_id, season_number, episode_number, file_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
"INSERT INTO media_items (id, type, name, series_name, series_key, season_number, episode_number, file_path) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
)
.run(id, `jf-${id}`, type, name, seriesName, seriesJellyfinId, seasonNumber, episodeNumber, `/x/${id}.mkv`);
.run(id, type, name, seriesName, seriesKey, seasonNumber, episodeNumber, `/x/${id}.mkv`);
db
.prepare(
"INSERT INTO review_plans (item_id, status, is_noop, auto_class, sorted, apple_compat, job_type, notes) VALUES (?, 'pending', 0, ?, ?, 'direct_play', 'copy', NULL)",
@@ -56,7 +56,7 @@ describe("buildReviewGroups", () => {
id: i,
type: "Episode",
seriesName: "Breaking Bad",
seriesJellyfinId: "bb",
seriesKey: "bb",
seasonNumber: 1,
episodeNumber: i,
});
@@ -81,7 +81,7 @@ describe("buildReviewGroups", () => {
id: ep,
type: "Episode",
seriesName: "Lost",
seriesJellyfinId: "lost",
seriesKey: "lost",
seasonNumber: 1,
episodeNumber: ep,
});
@@ -91,12 +91,12 @@ describe("buildReviewGroups", () => {
id: 10 + ep,
type: "Episode",
seriesName: "Lost",
seriesJellyfinId: "lost",
seriesKey: "lost",
seasonNumber: 2,
episodeNumber: ep,
});
}
seed(db, { id: 99, type: "Episode", seriesName: "Lost", seriesJellyfinId: "lost", seasonNumber: null });
seed(db, { id: 99, type: "Episode", seriesName: "Lost", seriesKey: "lost", seasonNumber: null });
const { groups } = buildReviewGroups(db, { bucket: "review" });
expect(groups).toHaveLength(1);
@@ -125,7 +125,7 @@ describe("buildReviewGroups", () => {
id: 1,
type: "Episode",
seriesName: "Show",
seriesJellyfinId: "s",
seriesKey: "s",
seasonNumber: 1,
episodeNumber: 1,
autoClass: "auto_heuristic",
@@ -134,7 +134,7 @@ describe("buildReviewGroups", () => {
id: 2,
type: "Episode",
seriesName: "Show",
seriesJellyfinId: "s",
seriesKey: "s",
seasonNumber: 1,
episodeNumber: 2,
autoClass: "manual",
@@ -21,7 +21,7 @@ interface AudioSeed {
interface SeedOpts {
id: number;
origLang: string | null;
origLangSource: "radarr" | "sonarr" | "manual" | "jellyfin" | null;
origLangSource: "probe" | "radarr" | "sonarr" | "manual" | null;
needsReview?: number;
audio: AudioSeed[];
}
@@ -29,11 +29,10 @@ interface SeedOpts {
function seedItem(db: Database, opts: SeedOpts): void {
db
.prepare(
"INSERT INTO media_items (id, jellyfin_id, type, name, file_path, container, original_language, orig_lang_source, needs_review) VALUES (?, ?, 'Movie', ?, ?, 'mkv', ?, ?, ?)",
"INSERT INTO media_items (id, type, name, file_path, container, original_language, orig_lang_source, needs_review) VALUES (?, 'Movie', ?, ?, 'mkv', ?, ?, ?)",
)
.run(
opts.id,
`jf-${opts.id}`,
`Item ${opts.id}`,
`/x/${opts.id}.mkv`,
opts.origLang,
@@ -16,9 +16,9 @@ function seedPlan(db: Database, id: number, opts: { sorted?: 0 | 1; status?: str
const { sorted = 1, status = "pending", isNoop = 0 } = opts;
db
.prepare(
"INSERT INTO media_items (id, jellyfin_id, type, name, file_path, container) VALUES (?, ?, 'Movie', ?, ?, 'mkv')",
"INSERT INTO media_items (id, type, name, file_path, container) VALUES (?, 'Movie', ?, ?, 'mkv')",
)
.run(id, `jf-${id}`, `Item ${id}`, `/x/${id}.mkv`);
.run(id, `Item ${id}`, `/x/${id}.mkv`);
db
.prepare(
"INSERT INTO review_plans (item_id, status, is_noop, auto_class, sorted, apple_compat, job_type) VALUES (?, ?, ?, 'auto_heuristic', ?, 'direct_play', 'copy')",
+2 -2
View File
@@ -10,8 +10,8 @@ function makeDb(): Database {
if (trimmed) db.run(trimmed);
}
db
.prepare("INSERT INTO media_items (id, jellyfin_id, type, name, file_path) VALUES (?, ?, 'Movie', 'T', '/x.mkv')")
.run(1, "jf-1");
.prepare("INSERT INTO media_items (id, type, name, file_path) VALUES (?, 'Movie', 'T', '/x.mkv')")
.run(1);
return db;
}