From e44228d14788f76635abfb77647411379de7f99b Mon Sep 17 00:00:00 2001 From: Sam Chau Date: Sat, 3 Jan 2026 02:01:38 +1030 Subject: [PATCH] feat(logging): implement change tracking and logging for media settings and naming updates --- .../pcd/queries/mediaManagement/update.ts | 136 ++++++++++++++++++ src/lib/server/pcd/writer.ts | 2 +- .../[databaseId]/radarr/+page.server.ts | 21 --- .../[databaseId]/sonarr/+page.server.ts | 23 --- 4 files changed, 137 insertions(+), 45 deletions(-) diff --git a/src/lib/server/pcd/queries/mediaManagement/update.ts b/src/lib/server/pcd/queries/mediaManagement/update.ts index fcc2348..36b3ba2 100644 --- a/src/lib/server/pcd/queries/mediaManagement/update.ts +++ b/src/lib/server/pcd/queries/mediaManagement/update.ts @@ -5,6 +5,7 @@ import type { PCDCache } from '../../cache.ts'; import { writeOperation, type OperationLayer } from '../../writer.ts'; +import { logger } from '$logger/logger.ts'; import type { PropersRepacks, ColonReplacementFormat, @@ -52,6 +53,20 @@ export async function updateRadarrMediaSettings(options: UpdateMediaSettingsOpti .where('enable_media_info', '=', current.enable_media_info ? 1 : 0) .compile(); + // Track changes + const changes: Record = {}; + if (current.propers_repacks !== input.propers_repacks) { + changes.propers_repacks = { from: current.propers_repacks, to: input.propers_repacks }; + } + if (current.enable_media_info !== input.enable_media_info) { + changes.enable_media_info = { from: current.enable_media_info, to: input.enable_media_info }; + } + + await logger.info('Save radarr media settings', { + source: 'MediaManagement', + meta: { id: current.id, changes } + }); + return await writeOperation({ databaseId, layer, @@ -84,6 +99,20 @@ export async function updateSonarrMediaSettings(options: UpdateMediaSettingsOpti .where('enable_media_info', '=', current.enable_media_info ? 1 : 0) .compile(); + // Track changes + const changes: Record = {}; + if (current.propers_repacks !== input.propers_repacks) { + changes.propers_repacks = { from: current.propers_repacks, to: input.propers_repacks }; + } + if (current.enable_media_info !== input.enable_media_info) { + changes.enable_media_info = { from: current.enable_media_info, to: input.enable_media_info }; + } + + await logger.info('Save sonarr media settings', { + source: 'MediaManagement', + meta: { id: current.id, changes } + }); + return await writeOperation({ databaseId, layer, @@ -149,6 +178,44 @@ export async function updateSonarrNaming(options: UpdateSonarrNamingOptions) { .where('replace_illegal_characters', '=', current.replace_illegal_characters ? 1 : 0) .compile(); + // Track changes + const changes: Record = {}; + if (current.rename !== input.rename) { + changes.rename = { from: current.rename, to: input.rename }; + } + if (current.replace_illegal_characters !== input.replace_illegal_characters) { + changes.replace_illegal_characters = { from: current.replace_illegal_characters, to: input.replace_illegal_characters }; + } + if (current.colon_replacement_format !== input.colon_replacement_format) { + changes.colon_replacement_format = { from: current.colon_replacement_format, to: input.colon_replacement_format }; + } + if (current.custom_colon_replacement_format !== input.custom_colon_replacement_format) { + changes.custom_colon_replacement_format = { from: current.custom_colon_replacement_format, to: input.custom_colon_replacement_format }; + } + if (current.standard_episode_format !== input.standard_episode_format) { + changes.standard_episode_format = { from: current.standard_episode_format, to: input.standard_episode_format }; + } + if (current.daily_episode_format !== input.daily_episode_format) { + changes.daily_episode_format = { from: current.daily_episode_format, to: input.daily_episode_format }; + } + if (current.anime_episode_format !== input.anime_episode_format) { + changes.anime_episode_format = { from: current.anime_episode_format, to: input.anime_episode_format }; + } + if (current.series_folder_format !== input.series_folder_format) { + changes.series_folder_format = { from: current.series_folder_format, to: input.series_folder_format }; + } + if (current.season_folder_format !== input.season_folder_format) { + changes.season_folder_format = { from: current.season_folder_format, to: input.season_folder_format }; + } + if (current.multi_episode_style !== input.multi_episode_style) { + changes.multi_episode_style = { from: current.multi_episode_style, to: input.multi_episode_style }; + } + + await logger.info('Save sonarr naming settings', { + source: 'MediaManagement', + meta: { id: current.id, changes } + }); + return await writeOperation({ databaseId, layer, @@ -204,6 +271,29 @@ export async function updateRadarrNaming(options: UpdateRadarrNamingOptions) { .where('replace_illegal_characters', '=', current.replace_illegal_characters ? 1 : 0) .compile(); + // Track changes + const changes: Record = {}; + if (current.rename !== input.rename) { + changes.rename = { from: current.rename, to: input.rename }; + } + if (current.replace_illegal_characters !== input.replace_illegal_characters) { + changes.replace_illegal_characters = { from: current.replace_illegal_characters, to: input.replace_illegal_characters }; + } + if (current.colon_replacement_format !== input.colon_replacement_format) { + changes.colon_replacement_format = { from: current.colon_replacement_format, to: input.colon_replacement_format }; + } + if (current.movie_format !== input.movie_format) { + changes.movie_format = { from: current.movie_format, to: input.movie_format }; + } + if (current.movie_folder_format !== input.movie_folder_format) { + changes.movie_folder_format = { from: current.movie_folder_format, to: input.movie_folder_format }; + } + + await logger.info('Save radarr naming settings', { + source: 'MediaManagement', + meta: { id: current.id, changes } + }); + return await writeOperation({ databaseId, layer, @@ -245,6 +335,9 @@ export async function updateRadarrQualityDefinitions(options: UpdateQualityDefin const { databaseId, cache, layer, current, input } = options; const db = cache.kb; + // Track changes per quality definition + const changes: Record = {}; + // Build queries for each changed definition const queries = input.map((def) => { const currentDef = current.find((c) => c.quality_id === def.quality_id); @@ -252,6 +345,21 @@ export async function updateRadarrQualityDefinitions(options: UpdateQualityDefin throw new Error(`Quality definition not found for quality_id: ${def.quality_id}`); } + // Track changes for this definition + const defChanges: Record = {}; + if (currentDef.min_size !== def.min_size) { + defChanges.min_size = { from: currentDef.min_size, to: def.min_size }; + } + if (currentDef.max_size !== def.max_size) { + defChanges.max_size = { from: currentDef.max_size, to: def.max_size }; + } + if (currentDef.preferred_size !== def.preferred_size) { + defChanges.preferred_size = { from: currentDef.preferred_size, to: def.preferred_size }; + } + if (Object.keys(defChanges).length > 0) { + changes[currentDef.quality_name] = defChanges as { from: unknown; to: unknown }; + } + return db .updateTable('radarr_quality_definitions') .set({ @@ -267,6 +375,11 @@ export async function updateRadarrQualityDefinitions(options: UpdateQualityDefin .compile(); }); + await logger.info('Save radarr quality definitions', { + source: 'MediaManagement', + meta: { changes } + }); + return await writeOperation({ databaseId, layer, @@ -287,6 +400,9 @@ export async function updateSonarrQualityDefinitions(options: UpdateQualityDefin const { databaseId, cache, layer, current, input } = options; const db = cache.kb; + // Track changes per quality definition + const changes: Record = {}; + // Build queries for each changed definition const queries = input.map((def) => { const currentDef = current.find((c) => c.quality_id === def.quality_id); @@ -294,6 +410,21 @@ export async function updateSonarrQualityDefinitions(options: UpdateQualityDefin throw new Error(`Quality definition not found for quality_id: ${def.quality_id}`); } + // Track changes for this definition + const defChanges: Record = {}; + if (currentDef.min_size !== def.min_size) { + defChanges.min_size = { from: currentDef.min_size, to: def.min_size }; + } + if (currentDef.max_size !== def.max_size) { + defChanges.max_size = { from: currentDef.max_size, to: def.max_size }; + } + if (currentDef.preferred_size !== def.preferred_size) { + defChanges.preferred_size = { from: currentDef.preferred_size, to: def.preferred_size }; + } + if (Object.keys(defChanges).length > 0) { + changes[currentDef.quality_name] = defChanges as { from: unknown; to: unknown }; + } + return db .updateTable('sonarr_quality_definitions') .set({ @@ -309,6 +440,11 @@ export async function updateSonarrQualityDefinitions(options: UpdateQualityDefin .compile(); }); + await logger.info('Save sonarr quality definitions', { + source: 'MediaManagement', + meta: { changes } + }); + return await writeOperation({ databaseId, layer, diff --git a/src/lib/server/pcd/writer.ts b/src/lib/server/pcd/writer.ts index f81f01f..2660242 100644 --- a/src/lib/server/pcd/writer.ts +++ b/src/lib/server/pcd/writer.ts @@ -331,7 +331,7 @@ export async function writeOperation(options: WriteOptions): Promise