mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-02-01 07:10:47 +01:00
refactor(pcd): mediaManagement now uses generated types, removed a fair bit of dead code
This commit is contained in:
@@ -11,13 +11,29 @@
|
||||
*/
|
||||
|
||||
import { Database } from '@jsr/db__sqlite';
|
||||
import { columnTypeOverrides } from './pcd-type-overrides.ts';
|
||||
|
||||
// ============================================================================
|
||||
// CONFIGURATION
|
||||
// ============================================================================
|
||||
|
||||
const SCHEMA_REPO = 'Dictionarry-Hub/schema';
|
||||
|
||||
/**
|
||||
* Manual type overrides for columns that store integers in the DB
|
||||
* but need semantic string types for the UI/API layer.
|
||||
*
|
||||
* Columns with CHECK constraints don't need overrides - the generator
|
||||
* parses those automatically. These are for Sonarr's integer enums.
|
||||
*
|
||||
* Runtime conversion functions are in: src/lib/shared/pcd/conversions.ts
|
||||
*/
|
||||
const COLUMN_TYPE_OVERRIDES: Record<string, string> = {
|
||||
// Sonarr stores these as integers (0-5) but we want semantic strings in TS
|
||||
'sonarr_naming.colon_replacement_format':
|
||||
"'delete' | 'dash' | 'spaceDash' | 'spaceDashSpace' | 'smart' | 'custom'",
|
||||
'sonarr_naming.multi_episode_style':
|
||||
"'extend' | 'duplicate' | 'repeat' | 'scene' | 'range' | 'prefixedRange'"
|
||||
};
|
||||
const DEFAULT_VERSION = '1.0.0'; // Schema versions are branch names (e.g., 1.0.0, 1.1.0)
|
||||
const SCHEMA_PATH = 'ops/0.schema.sql';
|
||||
const OUTPUT_DIR = './src/lib/shared/pcd';
|
||||
@@ -268,7 +284,7 @@ function getSemanticType(
|
||||
): string {
|
||||
// 1. Check for manual type override (for columns that store numbers but need string types)
|
||||
const overrideKey = `${tableName}.${column.name}`;
|
||||
const override = columnTypeOverrides[overrideKey];
|
||||
const override = COLUMN_TYPE_OVERRIDES[overrideKey];
|
||||
if (override) {
|
||||
return nullable ? `(${override}) | null` : override;
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* PCD Type Overrides
|
||||
*
|
||||
* Manual type overrides for columns that store numeric values in the DB
|
||||
* but need semantic string types (because that's what the API expects).
|
||||
*
|
||||
* Format: 'table_name.column_name': 'TypeScript type'
|
||||
*
|
||||
* Note: Columns with CHECK constraints in the schema don't need overrides -
|
||||
* the generator parses those automatically.
|
||||
*/
|
||||
|
||||
export const columnTypeOverrides: Record<string, string> = {
|
||||
// Sonarr stores these as integers (0-5) but API expects semantic strings
|
||||
'sonarr_naming.colon_replacement_format':
|
||||
"'delete' | 'dash' | 'spaceDash' | 'spaceDashSpace' | 'smart' | 'custom'",
|
||||
'sonarr_naming.multi_episode_style':
|
||||
"'extend' | 'duplicate' | 'repeat' | 'scene' | 'range' | 'prefixedRange'"
|
||||
};
|
||||
|
||||
/**
|
||||
* DB value mappings for converting between numeric DB values and semantic strings.
|
||||
* Used by query read/write functions.
|
||||
*/
|
||||
export const colonReplacementFromDb: Record<number, string> = {
|
||||
0: 'delete',
|
||||
1: 'dash',
|
||||
2: 'spaceDash',
|
||||
3: 'spaceDashSpace',
|
||||
4: 'smart',
|
||||
5: 'custom'
|
||||
};
|
||||
|
||||
export const colonReplacementToDb: Record<string, number> = {
|
||||
delete: 0,
|
||||
dash: 1,
|
||||
spaceDash: 2,
|
||||
spaceDashSpace: 3,
|
||||
smart: 4,
|
||||
custom: 5
|
||||
};
|
||||
|
||||
export const multiEpisodeStyleFromDb: Record<number, string> = {
|
||||
0: 'extend',
|
||||
1: 'duplicate',
|
||||
2: 'repeat',
|
||||
3: 'scene',
|
||||
4: 'range',
|
||||
5: 'prefixedRange'
|
||||
};
|
||||
|
||||
export const multiEpisodeStyleToDb: Record<string, number> = {
|
||||
extend: 0,
|
||||
duplicate: 1,
|
||||
repeat: 2,
|
||||
scene: 3,
|
||||
range: 4,
|
||||
prefixedRange: 5
|
||||
};
|
||||
@@ -1,118 +0,0 @@
|
||||
/**
|
||||
* Combined getters for media management configs
|
||||
* Provides backward compatibility for the syncer which expects combined objects
|
||||
*
|
||||
* NOTE: This returns the first available config for each type.
|
||||
* The syncer will need to be updated later to support selecting specific named configs.
|
||||
*/
|
||||
|
||||
import type { PCDCache } from '../../cache.ts';
|
||||
import type { MediaSettings, RadarrNaming, SonarrNaming } from '$lib/shared/mediaManagement.ts';
|
||||
import { getRadarrByName as getRadarrMediaSettings, getSonarrByName as getSonarrMediaSettings } from './media-settings/read.ts';
|
||||
import { getRadarrByName as getRadarrNaming, getSonarrByName as getSonarrNaming } from './naming/read.ts';
|
||||
import { getRadarrByName as getRadarrQualityDefs, getSonarrByName as getSonarrQualityDefs } from './quality-definitions/read.ts';
|
||||
import { list as listMediaSettings } from './media-settings/read.ts';
|
||||
import { list as listNaming } from './naming/read.ts';
|
||||
import { list as listQualityDefs } from './quality-definitions/read.ts';
|
||||
import type { QualityDefinitionEntry } from './quality-definitions/types.ts';
|
||||
|
||||
export interface QualityDefinition {
|
||||
quality_name: string;
|
||||
min_size: number;
|
||||
max_size: number | null;
|
||||
preferred_size: number | null;
|
||||
}
|
||||
|
||||
export interface RadarrCombined {
|
||||
mediaSettings: MediaSettings | null;
|
||||
naming: RadarrNaming | null;
|
||||
qualityDefinitions: QualityDefinition[];
|
||||
}
|
||||
|
||||
export interface SonarrCombined {
|
||||
mediaSettings: MediaSettings | null;
|
||||
naming: SonarrNaming | null;
|
||||
qualityDefinitions: QualityDefinition[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Radarr media management configs (returns first available of each type)
|
||||
*/
|
||||
export async function getRadarr(cache: PCDCache): Promise<RadarrCombined> {
|
||||
// Get the first available media settings config
|
||||
const mediaSettingsList = await listMediaSettings(cache);
|
||||
const radarrMediaSettings = mediaSettingsList.find(c => c.arr_type === 'radarr');
|
||||
const mediaSettings = radarrMediaSettings
|
||||
? await getRadarrMediaSettings(cache, radarrMediaSettings.name)
|
||||
: null;
|
||||
|
||||
// Get the first available naming config
|
||||
const namingList = await listNaming(cache);
|
||||
const radarrNaming = namingList.find(c => c.arr_type === 'radarr');
|
||||
const naming = radarrNaming
|
||||
? await getRadarrNaming(cache, radarrNaming.name)
|
||||
: null;
|
||||
|
||||
// Get quality definitions (not yet refactored to multi-config)
|
||||
const qualityDefinitions = await getQualityDefinitions(cache, 'radarr');
|
||||
|
||||
return {
|
||||
mediaSettings,
|
||||
naming,
|
||||
qualityDefinitions
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all Sonarr media management configs (returns first available of each type)
|
||||
*/
|
||||
export async function getSonarr(cache: PCDCache): Promise<SonarrCombined> {
|
||||
// Get the first available media settings config
|
||||
const mediaSettingsList = await listMediaSettings(cache);
|
||||
const sonarrMediaSettings = mediaSettingsList.find(c => c.arr_type === 'sonarr');
|
||||
const mediaSettings = sonarrMediaSettings
|
||||
? await getSonarrMediaSettings(cache, sonarrMediaSettings.name)
|
||||
: null;
|
||||
|
||||
// Get the first available naming config
|
||||
const namingList = await listNaming(cache);
|
||||
const sonarrNaming = namingList.find(c => c.arr_type === 'sonarr');
|
||||
const naming = sonarrNaming
|
||||
? await getSonarrNaming(cache, sonarrNaming.name)
|
||||
: null;
|
||||
|
||||
// Get quality definitions (not yet refactored to multi-config)
|
||||
const qualityDefinitions = await getQualityDefinitions(cache, 'sonarr');
|
||||
|
||||
return {
|
||||
mediaSettings,
|
||||
naming,
|
||||
qualityDefinitions
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get quality definitions for an arr type (returns first available config)
|
||||
*/
|
||||
async function getQualityDefinitions(cache: PCDCache, arrType: 'radarr' | 'sonarr'): Promise<QualityDefinition[]> {
|
||||
const qualityDefsList = await listQualityDefs(cache);
|
||||
const config = qualityDefsList.find(c => c.arr_type === arrType);
|
||||
|
||||
if (!config) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const getByName = arrType === 'radarr' ? getRadarrQualityDefs : getSonarrQualityDefs;
|
||||
const fullConfig = await getByName(cache, config.name);
|
||||
|
||||
if (!fullConfig) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return fullConfig.entries.map((entry: QualityDefinitionEntry) => ({
|
||||
quality_name: entry.quality_name,
|
||||
min_size: entry.min_size,
|
||||
max_size: entry.max_size,
|
||||
preferred_size: entry.preferred_size
|
||||
}));
|
||||
}
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import { writeOperation, type OperationLayer } from '../../../writer.ts';
|
||||
import type { PropersRepacks } from '$lib/shared/mediaManagement.ts';
|
||||
import type { RadarrMediaSettingsRow } from '$shared/pcd/display.ts';
|
||||
|
||||
export interface CreateMediaSettingsInput {
|
||||
name: string;
|
||||
propersRepacks: PropersRepacks;
|
||||
propersRepacks: RadarrMediaSettingsRow['propers_repacks'];
|
||||
enableMediaInfo: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
* Media settings queries index
|
||||
*/
|
||||
|
||||
export * from './types.ts';
|
||||
export * from './read.ts';
|
||||
export * from './create.ts';
|
||||
export * from './update.ts';
|
||||
export * from './remove.ts';
|
||||
// Read
|
||||
export { list, getRadarrByName, getSonarrByName } from './read.ts';
|
||||
|
||||
// Create
|
||||
export { createRadarrMediaSettings, createSonarrMediaSettings } from './create.ts';
|
||||
|
||||
// Update
|
||||
export { updateRadarrMediaSettings, updateSonarrMediaSettings } from './update.ts';
|
||||
|
||||
// Delete
|
||||
export { removeRadarrMediaSettings, removeSonarrMediaSettings } from './delete.ts';
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
*/
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import type { MediaSettings, PropersRepacks } from '$lib/shared/mediaManagement.ts';
|
||||
import type { MediaSettingsListItem } from './types.ts';
|
||||
import type { RadarrMediaSettingsRow, SonarrMediaSettingsRow, MediaSettingsListItem } from '$shared/pcd/display.ts';
|
||||
|
||||
export async function list(cache: PCDCache): Promise<MediaSettingsListItem[]> {
|
||||
const db = cache.kb;
|
||||
@@ -42,12 +41,12 @@ export async function list(cache: PCDCache): Promise<MediaSettingsListItem[]> {
|
||||
export async function getRadarrByName(
|
||||
cache: PCDCache,
|
||||
name: string
|
||||
): Promise<MediaSettings | null> {
|
||||
): Promise<RadarrMediaSettingsRow | null> {
|
||||
const db = cache.kb;
|
||||
|
||||
const row = await db
|
||||
.selectFrom('radarr_media_settings')
|
||||
.select(['name', 'propers_repacks', 'enable_media_info'])
|
||||
.selectAll()
|
||||
.where('name', '=', name)
|
||||
.executeTakeFirst();
|
||||
|
||||
@@ -55,20 +54,22 @@ export async function getRadarrByName(
|
||||
|
||||
return {
|
||||
name: row.name!,
|
||||
propers_repacks: row.propers_repacks as PropersRepacks,
|
||||
enable_media_info: row.enable_media_info === 1
|
||||
propers_repacks: row.propers_repacks as RadarrMediaSettingsRow['propers_repacks'],
|
||||
enable_media_info: row.enable_media_info === 1,
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSonarrByName(
|
||||
cache: PCDCache,
|
||||
name: string
|
||||
): Promise<MediaSettings | null> {
|
||||
): Promise<SonarrMediaSettingsRow | null> {
|
||||
const db = cache.kb;
|
||||
|
||||
const row = await db
|
||||
.selectFrom('sonarr_media_settings')
|
||||
.select(['name', 'propers_repacks', 'enable_media_info'])
|
||||
.selectAll()
|
||||
.where('name', '=', name)
|
||||
.executeTakeFirst();
|
||||
|
||||
@@ -76,7 +77,9 @@ export async function getSonarrByName(
|
||||
|
||||
return {
|
||||
name: row.name!,
|
||||
propers_repacks: row.propers_repacks as PropersRepacks,
|
||||
enable_media_info: row.enable_media_info === 1
|
||||
propers_repacks: row.propers_repacks as SonarrMediaSettingsRow['propers_repacks'],
|
||||
enable_media_info: row.enable_media_info === 1,
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Media settings query-specific types
|
||||
*/
|
||||
|
||||
export type ArrType = 'radarr' | 'sonarr';
|
||||
|
||||
export interface MediaSettingsListItem {
|
||||
name: string;
|
||||
arr_type: ArrType;
|
||||
propers_repacks: string;
|
||||
enable_media_info: boolean;
|
||||
updated_at: string;
|
||||
}
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import { writeOperation, type OperationLayer } from '../../../writer.ts';
|
||||
import type { PropersRepacks } from '$lib/shared/mediaManagement.ts';
|
||||
import type { RadarrMediaSettingsRow } from '$shared/pcd/display.ts';
|
||||
|
||||
export interface UpdateMediaSettingsInput {
|
||||
name: string;
|
||||
propersRepacks: PropersRepacks;
|
||||
propersRepacks: RadarrMediaSettingsRow['propers_repacks'];
|
||||
enableMediaInfo: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import { writeOperation, type OperationLayer } from '../../../writer.ts';
|
||||
import type { RadarrColonReplacementFormat, ColonReplacementFormat, MultiEpisodeStyle } from '$lib/shared/mediaManagement.ts';
|
||||
import { colonReplacementToDb, multiEpisodeStyleToDb } from '$lib/shared/mediaManagement.ts';
|
||||
import type { RadarrNamingRow, SonarrNamingRow } from '$shared/pcd/display.ts';
|
||||
import { colonReplacementToDb, multiEpisodeStyleToDb } from '$shared/pcd/conversions.ts';
|
||||
|
||||
export interface CreateRadarrNamingInput {
|
||||
name: string;
|
||||
@@ -13,7 +13,7 @@ export interface CreateRadarrNamingInput {
|
||||
movieFormat: string;
|
||||
movieFolderFormat: string;
|
||||
replaceIllegalCharacters: boolean;
|
||||
colonReplacementFormat: RadarrColonReplacementFormat;
|
||||
colonReplacementFormat: RadarrNamingRow['colon_replacement_format'];
|
||||
}
|
||||
|
||||
export interface CreateRadarrNamingOptions {
|
||||
@@ -72,9 +72,9 @@ export interface CreateSonarrNamingInput {
|
||||
seriesFolderFormat: string;
|
||||
seasonFolderFormat: string;
|
||||
replaceIllegalCharacters: boolean;
|
||||
colonReplacementFormat: ColonReplacementFormat;
|
||||
colonReplacementFormat: SonarrNamingRow['colon_replacement_format'];
|
||||
customColonReplacementFormat: string | null;
|
||||
multiEpisodeStyle: MultiEpisodeStyle;
|
||||
multiEpisodeStyle: SonarrNamingRow['multi_episode_style'];
|
||||
}
|
||||
|
||||
export interface CreateSonarrNamingOptions {
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
* Naming queries index
|
||||
*/
|
||||
|
||||
export * from './types.ts';
|
||||
export * from './read.ts';
|
||||
export * from './create.ts';
|
||||
export * from './update.ts';
|
||||
export * from './remove.ts';
|
||||
// Read
|
||||
export { list, getRadarrByName, getSonarrByName } from './read.ts';
|
||||
|
||||
// Create
|
||||
export { createRadarrNaming, createSonarrNaming } from './create.ts';
|
||||
|
||||
// Update
|
||||
export { updateRadarrNaming, updateSonarrNaming } from './update.ts';
|
||||
|
||||
// Delete
|
||||
export { removeRadarrNaming, removeSonarrNaming } from './delete.ts';
|
||||
|
||||
@@ -3,13 +3,8 @@
|
||||
*/
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import type {
|
||||
RadarrNaming,
|
||||
SonarrNaming,
|
||||
RadarrColonReplacementFormat
|
||||
} from '$lib/shared/mediaManagement.ts';
|
||||
import { colonReplacementFromDb, multiEpisodeStyleFromDb } from '$lib/shared/mediaManagement.ts';
|
||||
import type { NamingListItem } from './types.ts';
|
||||
import type { RadarrNamingRow, SonarrNamingRow, NamingListItem } from '$shared/pcd/display.ts';
|
||||
import { colonReplacementFromDb, multiEpisodeStyleFromDb } from '$shared/pcd/conversions.ts';
|
||||
|
||||
// Note: name is PRIMARY KEY so never null, but Kysely types it as nullable
|
||||
// because the generator doesn't detect non-INTEGER primary keys
|
||||
@@ -48,19 +43,12 @@ export async function list(cache: PCDCache): Promise<NamingListItem[]> {
|
||||
export async function getRadarrByName(
|
||||
cache: PCDCache,
|
||||
name: string
|
||||
): Promise<RadarrNaming | null> {
|
||||
): Promise<RadarrNamingRow | null> {
|
||||
const db = cache.kb;
|
||||
|
||||
const row = await db
|
||||
.selectFrom('radarr_naming')
|
||||
.select([
|
||||
'name',
|
||||
'rename',
|
||||
'movie_format',
|
||||
'movie_folder_format',
|
||||
'replace_illegal_characters',
|
||||
'colon_replacement_format'
|
||||
])
|
||||
.selectAll()
|
||||
.where('name', '=', name)
|
||||
.executeTakeFirst();
|
||||
|
||||
@@ -72,31 +60,21 @@ export async function getRadarrByName(
|
||||
movie_format: row.movie_format,
|
||||
movie_folder_format: row.movie_folder_format,
|
||||
replace_illegal_characters: row.replace_illegal_characters === 1,
|
||||
colon_replacement_format: row.colon_replacement_format as RadarrColonReplacementFormat
|
||||
colon_replacement_format: row.colon_replacement_format as RadarrNamingRow['colon_replacement_format'],
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSonarrByName(
|
||||
cache: PCDCache,
|
||||
name: string
|
||||
): Promise<SonarrNaming | null> {
|
||||
): Promise<SonarrNamingRow | null> {
|
||||
const db = cache.kb;
|
||||
|
||||
const row = await db
|
||||
.selectFrom('sonarr_naming')
|
||||
.select([
|
||||
'name',
|
||||
'rename',
|
||||
'standard_episode_format',
|
||||
'daily_episode_format',
|
||||
'anime_episode_format',
|
||||
'series_folder_format',
|
||||
'season_folder_format',
|
||||
'replace_illegal_characters',
|
||||
'colon_replacement_format',
|
||||
'custom_colon_replacement_format',
|
||||
'multi_episode_style'
|
||||
])
|
||||
.selectAll()
|
||||
.where('name', '=', name)
|
||||
.executeTakeFirst();
|
||||
|
||||
@@ -113,6 +91,8 @@ export async function getSonarrByName(
|
||||
replace_illegal_characters: row.replace_illegal_characters === 1,
|
||||
colon_replacement_format: colonReplacementFromDb(row.colon_replacement_format),
|
||||
custom_colon_replacement_format: row.custom_colon_replacement_format,
|
||||
multi_episode_style: multiEpisodeStyleFromDb(row.multi_episode_style)
|
||||
multi_episode_style: multiEpisodeStyleFromDb(row.multi_episode_style),
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Naming query-specific types
|
||||
*/
|
||||
|
||||
export type ArrType = 'radarr' | 'sonarr';
|
||||
|
||||
export interface NamingListItem {
|
||||
name: string;
|
||||
arr_type: ArrType;
|
||||
rename: boolean;
|
||||
updated_at: string;
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import { writeOperation, type OperationLayer } from '../../../writer.ts';
|
||||
import type { ColonReplacementFormat, MultiEpisodeStyle, RadarrColonReplacementFormat } from '$lib/shared/mediaManagement.ts';
|
||||
import { colonReplacementToDb, multiEpisodeStyleToDb } from '$lib/shared/mediaManagement.ts';
|
||||
import type { RadarrNamingRow, SonarrNamingRow } from '$shared/pcd/display.ts';
|
||||
import { colonReplacementToDb, multiEpisodeStyleToDb } from '$shared/pcd/conversions.ts';
|
||||
|
||||
export interface UpdateRadarrNamingInput {
|
||||
name: string;
|
||||
@@ -13,7 +13,7 @@ export interface UpdateRadarrNamingInput {
|
||||
movieFormat: string;
|
||||
movieFolderFormat: string;
|
||||
replaceIllegalCharacters: boolean;
|
||||
colonReplacementFormat: RadarrColonReplacementFormat;
|
||||
colonReplacementFormat: RadarrNamingRow['colon_replacement_format'];
|
||||
}
|
||||
|
||||
export interface UpdateRadarrNamingOptions {
|
||||
@@ -76,9 +76,9 @@ export interface UpdateSonarrNamingInput {
|
||||
seriesFolderFormat: string;
|
||||
seasonFolderFormat: string;
|
||||
replaceIllegalCharacters: boolean;
|
||||
colonReplacementFormat: ColonReplacementFormat;
|
||||
colonReplacementFormat: SonarrNamingRow['colon_replacement_format'];
|
||||
customColonReplacementFormat: string | null;
|
||||
multiEpisodeStyle: MultiEpisodeStyle;
|
||||
multiEpisodeStyle: SonarrNamingRow['multi_episode_style'];
|
||||
}
|
||||
|
||||
export interface UpdateSonarrNamingOptions {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import { writeOperation, type OperationLayer } from '../../../writer.ts';
|
||||
import type { QualityDefinitionEntry } from './types.ts';
|
||||
import type { QualityDefinitionEntry } from '$shared/pcd/display.ts';
|
||||
|
||||
export interface CreateQualityDefinitionsInput {
|
||||
name: string;
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
* Quality definitions queries
|
||||
*/
|
||||
|
||||
export * from './types.ts';
|
||||
export * from './read.ts';
|
||||
export * from './create.ts';
|
||||
export * from './update.ts';
|
||||
export * from './remove.ts';
|
||||
// Read
|
||||
export { list, getRadarrByName, getSonarrByName, getAvailableQualities } from './read.ts';
|
||||
|
||||
// Create
|
||||
export { createRadarrQualityDefinitions, createSonarrQualityDefinitions } from './create.ts';
|
||||
|
||||
// Update
|
||||
export { updateRadarrQualityDefinitions, updateSonarrQualityDefinitions } from './update.ts';
|
||||
|
||||
// Delete
|
||||
export { removeRadarrQualityDefinitions, removeSonarrQualityDefinitions } from './delete.ts';
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
*/
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import type { QualityDefinitionListItem, QualityDefinitionsConfig, QualityDefinitionEntry, ArrType } from './types.ts';
|
||||
import type { ArrType } from '$shared/pcd/types.ts';
|
||||
import type {
|
||||
QualityDefinitionListItem,
|
||||
QualityDefinitionsConfig,
|
||||
QualityDefinitionEntry
|
||||
} from '$shared/pcd/display.ts';
|
||||
|
||||
/**
|
||||
* Get available qualities for an arr type from quality_api_mappings
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Quality definitions types
|
||||
*/
|
||||
|
||||
export type ArrType = 'radarr' | 'sonarr';
|
||||
|
||||
export interface QualityDefinitionListItem {
|
||||
name: string;
|
||||
arr_type: ArrType;
|
||||
quality_count: number;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface QualityDefinitionEntry {
|
||||
quality_name: string;
|
||||
min_size: number;
|
||||
max_size: number;
|
||||
preferred_size: number;
|
||||
}
|
||||
|
||||
export interface QualityDefinitionsConfig {
|
||||
name: string;
|
||||
entries: QualityDefinitionEntry[];
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import type { PCDCache } from '../../../cache.ts';
|
||||
import { writeOperation, type OperationLayer } from '../../../writer.ts';
|
||||
import type { QualityDefinitionEntry } from './types.ts';
|
||||
import type { QualityDefinitionEntry } from '$shared/pcd/display.ts';
|
||||
|
||||
export interface UpdateQualityDefinitionsInput {
|
||||
name: string;
|
||||
|
||||
@@ -39,4 +39,4 @@ export { updateGeneral } from './updateGeneral.ts';
|
||||
export { updateScoring } from './updateScoring.ts';
|
||||
export { updateQualities } from './updateQualities.ts';
|
||||
export { updateLanguages } from './updateLanguages.ts';
|
||||
export { remove } from './remove.ts';
|
||||
export { remove } from './delete.ts';
|
||||
|
||||
@@ -17,12 +17,11 @@
|
||||
import { BaseSyncer, type SyncResult } from '../base.ts';
|
||||
import { arrSyncQueries } from '$db/queries/arrSync.ts';
|
||||
import { getCache, type PCDCache } from '$pcd/cache.ts';
|
||||
import type { QualityDefinition } from '$pcd/queries/mediaManagement/combined.ts';
|
||||
import { getRadarrByName as getRadarrMediaSettings, getSonarrByName as getSonarrMediaSettings } from '$pcd/queries/mediaManagement/media-settings/read.ts';
|
||||
import { getRadarrByName as getRadarrNaming, getSonarrByName as getSonarrNaming } from '$pcd/queries/mediaManagement/naming/read.ts';
|
||||
import { getRadarrByName as getRadarrQualityDefs, getSonarrByName as getSonarrQualityDefs } from '$pcd/queries/mediaManagement/quality-definitions/read.ts';
|
||||
import type { MediaSettings, RadarrNaming, SonarrNaming } from '$lib/shared/mediaManagement.ts';
|
||||
import { colonReplacementToDb, multiEpisodeStyleToDb } from '$lib/shared/mediaManagement.ts';
|
||||
import type { RadarrMediaSettingsRow, SonarrMediaSettingsRow, RadarrNamingRow, SonarrNamingRow } from '$shared/pcd/display.ts';
|
||||
import { colonReplacementToDb, multiEpisodeStyleToDb } from '$shared/pcd/conversions.ts';
|
||||
import type {
|
||||
ArrType,
|
||||
ArrPropersAndRepacks,
|
||||
@@ -150,7 +149,7 @@ export class MediaManagementSyncer extends BaseSyncer {
|
||||
}
|
||||
|
||||
// Fetch from PCD by config name
|
||||
let mediaSettings: MediaSettings | null = null;
|
||||
let mediaSettings: RadarrMediaSettingsRow | SonarrMediaSettingsRow | null = null;
|
||||
if (this.instanceType === 'radarr') {
|
||||
mediaSettings = await getRadarrMediaSettings(cache, configName);
|
||||
} else if (this.instanceType === 'sonarr') {
|
||||
@@ -337,14 +336,7 @@ export class MediaManagementSyncer extends BaseSyncer {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pcdDefinitions: QualityDefinition[] = qualityDefsConfig.entries.map(entry => ({
|
||||
quality_name: entry.quality_name,
|
||||
min_size: entry.min_size,
|
||||
max_size: entry.max_size,
|
||||
preferred_size: entry.preferred_size
|
||||
}));
|
||||
|
||||
if (pcdDefinitions.length === 0) {
|
||||
if (qualityDefsConfig.entries.length === 0) {
|
||||
await logger.debug(`Quality definitions config "${configName}" has no entries`, {
|
||||
source: 'Sync:QualityDefinitions',
|
||||
meta: { instanceId: this.instanceId, configName }
|
||||
@@ -368,13 +360,13 @@ export class MediaManagementSyncer extends BaseSyncer {
|
||||
|
||||
// Update ARR definitions with PCD values
|
||||
let updatedCount = 0;
|
||||
for (const pcdDef of pcdDefinitions) {
|
||||
for (const entry of qualityDefsConfig.entries) {
|
||||
// Get the API name for this quality
|
||||
const apiName = apiMappings.get(pcdDef.quality_name.toLowerCase());
|
||||
const apiName = apiMappings.get(entry.quality_name.toLowerCase());
|
||||
if (!apiName) {
|
||||
await logger.debug(`No API mapping found for quality "${pcdDef.quality_name}"`, {
|
||||
await logger.debug(`No API mapping found for quality "${entry.quality_name}"`, {
|
||||
source: 'Sync:QualityDefinitions',
|
||||
meta: { instanceId: this.instanceId, qualityName: pcdDef.quality_name }
|
||||
meta: { instanceId: this.instanceId, qualityName: entry.quality_name }
|
||||
});
|
||||
continue;
|
||||
}
|
||||
@@ -390,10 +382,10 @@ export class MediaManagementSyncer extends BaseSyncer {
|
||||
}
|
||||
|
||||
// Update the definition
|
||||
// PCD uses 0 for "unlimited", Radarr API uses null
|
||||
arrDef.minSize = pcdDef.min_size;
|
||||
arrDef.maxSize = pcdDef.max_size === 0 ? null : pcdDef.max_size;
|
||||
arrDef.preferredSize = pcdDef.preferred_size === 0 ? null : pcdDef.preferred_size;
|
||||
// PCD stores 0 for "unlimited", arr API expects null
|
||||
arrDef.minSize = entry.min_size;
|
||||
arrDef.maxSize = entry.max_size === 0 ? null : entry.max_size;
|
||||
arrDef.preferredSize = entry.preferred_size === 0 ? null : entry.preferred_size;
|
||||
updatedCount++;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,289 +0,0 @@
|
||||
/**
|
||||
* Shared media management types and options
|
||||
* Used by both UI and sync engine
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// PROPERS AND REPACKS
|
||||
// ============================================================================
|
||||
|
||||
export type PropersRepacks = 'doNotPrefer' | 'preferAndUpgrade' | 'doNotUpgradeAutomatically';
|
||||
|
||||
// ============================================================================
|
||||
// MEDIA SETTINGS
|
||||
// ============================================================================
|
||||
|
||||
export interface MediaSettings {
|
||||
name: string;
|
||||
propers_repacks: PropersRepacks;
|
||||
enable_media_info: boolean;
|
||||
}
|
||||
|
||||
export const PROPERS_REPACKS_OPTIONS: {
|
||||
value: PropersRepacks;
|
||||
label: string;
|
||||
description: string;
|
||||
}[] = [
|
||||
{
|
||||
value: 'doNotPrefer',
|
||||
label: 'Do Not Prefer',
|
||||
description: 'Propers and repacks are not preferred over existing files'
|
||||
},
|
||||
{
|
||||
value: 'preferAndUpgrade',
|
||||
label: 'Prefer and Upgrade',
|
||||
description: 'Automatically upgrade to propers and repacks when available'
|
||||
},
|
||||
{
|
||||
value: 'doNotUpgradeAutomatically',
|
||||
label: 'Do Not Upgrade Automatically',
|
||||
description: 'Prefer propers/repacks but do not automatically upgrade'
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the display label for a propers_repacks value
|
||||
*/
|
||||
export function getPropersRepacksLabel(value: PropersRepacks): string {
|
||||
const option = PROPERS_REPACKS_OPTIONS.find((o) => o.value === value);
|
||||
return option?.label ?? value;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SONARR NAMING
|
||||
// ============================================================================
|
||||
|
||||
export type ColonReplacementFormat =
|
||||
| 'delete'
|
||||
| 'dash'
|
||||
| 'spaceDash'
|
||||
| 'spaceDashSpace'
|
||||
| 'smart'
|
||||
| 'custom';
|
||||
|
||||
export const COLON_REPLACEMENT_OPTIONS: {
|
||||
value: ColonReplacementFormat;
|
||||
label: string;
|
||||
}[] = [
|
||||
{ value: 'delete', label: 'Delete' },
|
||||
{ value: 'dash', label: 'Replace with Dash' },
|
||||
{ value: 'spaceDash', label: 'Replace with Space Dash' },
|
||||
{ value: 'spaceDashSpace', label: 'Replace with Space Dash Space' },
|
||||
{ value: 'smart', label: 'Smart Replace' },
|
||||
{ value: 'custom', label: 'Custom' }
|
||||
];
|
||||
|
||||
export function getColonReplacementLabel(value: ColonReplacementFormat): string {
|
||||
const option = COLON_REPLACEMENT_OPTIONS.find((o) => o.value === value);
|
||||
return option?.label ?? value;
|
||||
}
|
||||
|
||||
// Database stores as numbers: 0=delete, 1=dash, 2=spaceDash, 3=spaceDashSpace, 4=smart, 5=custom
|
||||
const COLON_REPLACEMENT_NUM_MAP: Record<number, ColonReplacementFormat> = {
|
||||
0: 'delete',
|
||||
1: 'dash',
|
||||
2: 'spaceDash',
|
||||
3: 'spaceDashSpace',
|
||||
4: 'smart',
|
||||
5: 'custom'
|
||||
};
|
||||
|
||||
const COLON_REPLACEMENT_STR_MAP: Record<ColonReplacementFormat, number> = {
|
||||
delete: 0,
|
||||
dash: 1,
|
||||
spaceDash: 2,
|
||||
spaceDashSpace: 3,
|
||||
smart: 4,
|
||||
custom: 5
|
||||
};
|
||||
|
||||
export function colonReplacementFromDb(value: number): ColonReplacementFormat {
|
||||
return COLON_REPLACEMENT_NUM_MAP[value] ?? 'delete';
|
||||
}
|
||||
|
||||
export function colonReplacementToDb(value: ColonReplacementFormat): number {
|
||||
return COLON_REPLACEMENT_STR_MAP[value] ?? 0;
|
||||
}
|
||||
|
||||
export type MultiEpisodeStyle =
|
||||
| 'extend'
|
||||
| 'duplicate'
|
||||
| 'repeat'
|
||||
| 'scene'
|
||||
| 'range'
|
||||
| 'prefixedRange';
|
||||
|
||||
export const MULTI_EPISODE_STYLE_OPTIONS: {
|
||||
value: MultiEpisodeStyle;
|
||||
label: string;
|
||||
}[] = [
|
||||
{ value: 'extend', label: 'Extend' },
|
||||
{ value: 'duplicate', label: 'Duplicate' },
|
||||
{ value: 'repeat', label: 'Repeat' },
|
||||
{ value: 'scene', label: 'Scene' },
|
||||
{ value: 'range', label: 'Range' },
|
||||
{ value: 'prefixedRange', label: 'Prefixed Range' }
|
||||
];
|
||||
|
||||
export function getMultiEpisodeStyleLabel(value: MultiEpisodeStyle): string {
|
||||
const option = MULTI_EPISODE_STYLE_OPTIONS.find((o) => o.value === value);
|
||||
return option?.label ?? value;
|
||||
}
|
||||
|
||||
// Database stores as numbers: 0=extend, 1=duplicate, 2=repeat, 3=scene, 4=range, 5=prefixedRange
|
||||
const MULTI_EPISODE_NUM_MAP: Record<number, MultiEpisodeStyle> = {
|
||||
0: 'extend',
|
||||
1: 'duplicate',
|
||||
2: 'repeat',
|
||||
3: 'scene',
|
||||
4: 'range',
|
||||
5: 'prefixedRange'
|
||||
};
|
||||
|
||||
const MULTI_EPISODE_STR_MAP: Record<MultiEpisodeStyle, number> = {
|
||||
extend: 0,
|
||||
duplicate: 1,
|
||||
repeat: 2,
|
||||
scene: 3,
|
||||
range: 4,
|
||||
prefixedRange: 5
|
||||
};
|
||||
|
||||
export function multiEpisodeStyleFromDb(value: number): MultiEpisodeStyle {
|
||||
return MULTI_EPISODE_NUM_MAP[value] ?? 'extend';
|
||||
}
|
||||
|
||||
export function multiEpisodeStyleToDb(value: MultiEpisodeStyle): number {
|
||||
return MULTI_EPISODE_STR_MAP[value] ?? 0;
|
||||
}
|
||||
|
||||
// Radarr colon replacement (no custom option)
|
||||
export type RadarrColonReplacementFormat =
|
||||
| 'delete'
|
||||
| 'dash'
|
||||
| 'spaceDash'
|
||||
| 'spaceDashSpace'
|
||||
| 'smart';
|
||||
|
||||
export const RADARR_COLON_REPLACEMENT_OPTIONS: {
|
||||
value: RadarrColonReplacementFormat;
|
||||
label: string;
|
||||
}[] = [
|
||||
{ value: 'delete', label: 'Delete' },
|
||||
{ value: 'dash', label: 'Replace with Dash' },
|
||||
{ value: 'spaceDash', label: 'Replace with Space Dash' },
|
||||
{ value: 'spaceDashSpace', label: 'Replace with Space Dash Space' },
|
||||
{ value: 'smart', label: 'Smart Replace' }
|
||||
];
|
||||
|
||||
export function getRadarrColonReplacementLabel(value: RadarrColonReplacementFormat): string {
|
||||
const option = RADARR_COLON_REPLACEMENT_OPTIONS.find((o) => o.value === value);
|
||||
return option?.label ?? value;
|
||||
}
|
||||
|
||||
export function radarrColonReplacementFromDb(value: number): RadarrColonReplacementFormat {
|
||||
const map: Record<number, RadarrColonReplacementFormat> = {
|
||||
0: 'delete',
|
||||
1: 'dash',
|
||||
2: 'spaceDash',
|
||||
3: 'spaceDashSpace',
|
||||
4: 'smart'
|
||||
};
|
||||
return map[value] ?? 'delete';
|
||||
}
|
||||
|
||||
export function radarrColonReplacementToDb(value: RadarrColonReplacementFormat): number {
|
||||
const map: Record<RadarrColonReplacementFormat, number> = {
|
||||
delete: 0,
|
||||
dash: 1,
|
||||
spaceDash: 2,
|
||||
spaceDashSpace: 3,
|
||||
smart: 4
|
||||
};
|
||||
return map[value] ?? 0;
|
||||
}
|
||||
|
||||
export interface RadarrNaming {
|
||||
name: string;
|
||||
rename: boolean;
|
||||
movie_format: string;
|
||||
movie_folder_format: string;
|
||||
replace_illegal_characters: boolean;
|
||||
colon_replacement_format: RadarrColonReplacementFormat;
|
||||
}
|
||||
|
||||
export interface SonarrNaming {
|
||||
name: string;
|
||||
rename: boolean;
|
||||
replace_illegal_characters: boolean;
|
||||
colon_replacement_format: ColonReplacementFormat;
|
||||
custom_colon_replacement_format: string | null;
|
||||
standard_episode_format: string;
|
||||
daily_episode_format: string;
|
||||
anime_episode_format: string;
|
||||
series_folder_format: string;
|
||||
season_folder_format: string;
|
||||
multi_episode_style: MultiEpisodeStyle;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// QUALITY DEFINITION RESOLUTION GROUPS
|
||||
// ============================================================================
|
||||
|
||||
export type ResolutionGroup = 'SD' | '720p' | '1080p' | '2160p' | 'Prereleases' | 'Other';
|
||||
|
||||
export const RESOLUTION_GROUP_ORDER: ResolutionGroup[] = [
|
||||
'2160p',
|
||||
'1080p',
|
||||
'720p',
|
||||
'SD',
|
||||
'Prereleases',
|
||||
'Other'
|
||||
];
|
||||
|
||||
export const RESOLUTION_GROUP_LABELS: Record<ResolutionGroup, string> = {
|
||||
'2160p': '4K Ultra HD (2160p)',
|
||||
'1080p': 'Full HD (1080p)',
|
||||
'720p': 'HD (720p)',
|
||||
SD: 'Standard Definition (SD)',
|
||||
Prereleases: 'Prereleases',
|
||||
Other: 'Other'
|
||||
};
|
||||
|
||||
// Qualities that belong to Prereleases group
|
||||
const PRERELEASE_QUALITIES = ['cam', 'dvdscr', 'regional', 'telecine', 'telesync', 'workprint'];
|
||||
|
||||
// Qualities that belong to Other group
|
||||
const OTHER_QUALITIES = ['raw-hd', 'unknown'];
|
||||
|
||||
/**
|
||||
* Determine the resolution group from a quality name
|
||||
* Parses names like "Bluray-1080p", "WEBDL-720p", "HDTV-2160p", etc.
|
||||
*/
|
||||
export function getResolutionGroup(qualityName: string): ResolutionGroup {
|
||||
const name = qualityName.toLowerCase();
|
||||
|
||||
// Check for prereleases first
|
||||
if (PRERELEASE_QUALITIES.some((q) => name === q || name.includes(q))) {
|
||||
return 'Prereleases';
|
||||
}
|
||||
|
||||
// Check for other/unknown
|
||||
if (OTHER_QUALITIES.some((q) => name === q || name.includes(q))) {
|
||||
return 'Other';
|
||||
}
|
||||
|
||||
// Check by resolution
|
||||
if (name.includes('2160') || name.includes('4k') || name.includes('uhd')) {
|
||||
return '2160p';
|
||||
}
|
||||
if (name.includes('1080')) {
|
||||
return '1080p';
|
||||
}
|
||||
if (name.includes('720')) {
|
||||
return '720p';
|
||||
}
|
||||
|
||||
// Everything else is SD (480p, SDTV, DVD, etc.)
|
||||
return 'SD';
|
||||
}
|
||||
187
src/lib/shared/pcd/conversions.ts
Normal file
187
src/lib/shared/pcd/conversions.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* PCD Value Conversions
|
||||
*
|
||||
* Runtime conversion functions for columns that store integers in the DB
|
||||
* but need semantic string values for the API/UI.
|
||||
*
|
||||
* Note: These are only needed for Sonarr's quirky integer-based enums.
|
||||
* Radarr stores strings directly.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// SONARR COLON REPLACEMENT FORMAT
|
||||
// ============================================================================
|
||||
// DB stores: 0, 1, 2, 3, 4, 5
|
||||
// API expects: 'delete', 'dash', 'spaceDash', 'spaceDashSpace', 'smart', 'custom'
|
||||
|
||||
export type SonarrColonReplacementFormat =
|
||||
| 'delete'
|
||||
| 'dash'
|
||||
| 'spaceDash'
|
||||
| 'spaceDashSpace'
|
||||
| 'smart'
|
||||
| 'custom';
|
||||
|
||||
const COLON_REPLACEMENT_FROM_DB: Record<number, SonarrColonReplacementFormat> = {
|
||||
0: 'delete',
|
||||
1: 'dash',
|
||||
2: 'spaceDash',
|
||||
3: 'spaceDashSpace',
|
||||
4: 'smart',
|
||||
5: 'custom'
|
||||
};
|
||||
|
||||
const COLON_REPLACEMENT_TO_DB: Record<SonarrColonReplacementFormat, number> = {
|
||||
delete: 0,
|
||||
dash: 1,
|
||||
spaceDash: 2,
|
||||
spaceDashSpace: 3,
|
||||
smart: 4,
|
||||
custom: 5
|
||||
};
|
||||
|
||||
export function colonReplacementFromDb(value: number): SonarrColonReplacementFormat {
|
||||
return COLON_REPLACEMENT_FROM_DB[value] ?? 'delete';
|
||||
}
|
||||
|
||||
export function colonReplacementToDb(value: SonarrColonReplacementFormat): number {
|
||||
return COLON_REPLACEMENT_TO_DB[value] ?? 0;
|
||||
}
|
||||
|
||||
// UI options for Sonarr colon replacement
|
||||
export const SONARR_COLON_REPLACEMENT_OPTIONS: {
|
||||
value: SonarrColonReplacementFormat;
|
||||
label: string;
|
||||
}[] = [
|
||||
{ value: 'delete', label: 'Delete' },
|
||||
{ value: 'dash', label: 'Replace with Dash' },
|
||||
{ value: 'spaceDash', label: 'Replace with Space Dash' },
|
||||
{ value: 'spaceDashSpace', label: 'Replace with Space Dash Space' },
|
||||
{ value: 'smart', label: 'Smart Replace' },
|
||||
{ value: 'custom', label: 'Custom' }
|
||||
];
|
||||
|
||||
export function getColonReplacementLabel(value: SonarrColonReplacementFormat): string {
|
||||
const option = SONARR_COLON_REPLACEMENT_OPTIONS.find((o) => o.value === value);
|
||||
return option?.label ?? value;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SONARR MULTI-EPISODE STYLE
|
||||
// ============================================================================
|
||||
// DB stores: 0, 1, 2, 3, 4, 5
|
||||
// API expects: 'extend', 'duplicate', 'repeat', 'scene', 'range', 'prefixedRange'
|
||||
|
||||
export type MultiEpisodeStyle =
|
||||
| 'extend'
|
||||
| 'duplicate'
|
||||
| 'repeat'
|
||||
| 'scene'
|
||||
| 'range'
|
||||
| 'prefixedRange';
|
||||
|
||||
const MULTI_EPISODE_FROM_DB: Record<number, MultiEpisodeStyle> = {
|
||||
0: 'extend',
|
||||
1: 'duplicate',
|
||||
2: 'repeat',
|
||||
3: 'scene',
|
||||
4: 'range',
|
||||
5: 'prefixedRange'
|
||||
};
|
||||
|
||||
const MULTI_EPISODE_TO_DB: Record<MultiEpisodeStyle, number> = {
|
||||
extend: 0,
|
||||
duplicate: 1,
|
||||
repeat: 2,
|
||||
scene: 3,
|
||||
range: 4,
|
||||
prefixedRange: 5
|
||||
};
|
||||
|
||||
export function multiEpisodeStyleFromDb(value: number): MultiEpisodeStyle {
|
||||
return MULTI_EPISODE_FROM_DB[value] ?? 'extend';
|
||||
}
|
||||
|
||||
export function multiEpisodeStyleToDb(value: MultiEpisodeStyle): number {
|
||||
return MULTI_EPISODE_TO_DB[value] ?? 0;
|
||||
}
|
||||
|
||||
// UI options for multi-episode style
|
||||
export const MULTI_EPISODE_STYLE_OPTIONS: {
|
||||
value: MultiEpisodeStyle;
|
||||
label: string;
|
||||
}[] = [
|
||||
{ value: 'extend', label: 'Extend' },
|
||||
{ value: 'duplicate', label: 'Duplicate' },
|
||||
{ value: 'repeat', label: 'Repeat' },
|
||||
{ value: 'scene', label: 'Scene' },
|
||||
{ value: 'range', label: 'Range' },
|
||||
{ value: 'prefixedRange', label: 'Prefixed Range' }
|
||||
];
|
||||
|
||||
export function getMultiEpisodeStyleLabel(value: MultiEpisodeStyle): string {
|
||||
const option = MULTI_EPISODE_STYLE_OPTIONS.find((o) => o.value === value);
|
||||
return option?.label ?? value;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RADARR COLON REPLACEMENT FORMAT
|
||||
// ============================================================================
|
||||
// Radarr stores as strings directly in the DB, but we still need UI options
|
||||
|
||||
export type RadarrColonReplacementFormat =
|
||||
| 'delete'
|
||||
| 'dash'
|
||||
| 'spaceDash'
|
||||
| 'spaceDashSpace'
|
||||
| 'smart';
|
||||
|
||||
export const RADARR_COLON_REPLACEMENT_OPTIONS: {
|
||||
value: RadarrColonReplacementFormat;
|
||||
label: string;
|
||||
}[] = [
|
||||
{ value: 'delete', label: 'Delete' },
|
||||
{ value: 'dash', label: 'Replace with Dash' },
|
||||
{ value: 'spaceDash', label: 'Replace with Space Dash' },
|
||||
{ value: 'spaceDashSpace', label: 'Replace with Space Dash Space' },
|
||||
{ value: 'smart', label: 'Smart Replace' }
|
||||
];
|
||||
|
||||
export function getRadarrColonReplacementLabel(value: RadarrColonReplacementFormat): string {
|
||||
const option = RADARR_COLON_REPLACEMENT_OPTIONS.find((o) => o.value === value);
|
||||
return option?.label ?? value;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PROPERS AND REPACKS
|
||||
// ============================================================================
|
||||
// Both Radarr and Sonarr store as strings - no conversion needed, just UI options
|
||||
|
||||
export type PropersRepacks = 'doNotPrefer' | 'preferAndUpgrade' | 'doNotUpgradeAutomatically';
|
||||
|
||||
export const PROPERS_REPACKS_OPTIONS: {
|
||||
value: PropersRepacks;
|
||||
label: string;
|
||||
description: string;
|
||||
}[] = [
|
||||
{
|
||||
value: 'doNotPrefer',
|
||||
label: 'Do Not Prefer',
|
||||
description: 'Propers and repacks are not preferred over existing files'
|
||||
},
|
||||
{
|
||||
value: 'preferAndUpgrade',
|
||||
label: 'Prefer and Upgrade',
|
||||
description: 'Automatically upgrade to propers and repacks when available'
|
||||
},
|
||||
{
|
||||
value: 'doNotUpgradeAutomatically',
|
||||
label: 'Do Not Upgrade Automatically',
|
||||
description: 'Prefer propers/repacks but do not automatically upgrade'
|
||||
}
|
||||
];
|
||||
|
||||
export function getPropersRepacksLabel(value: PropersRepacks): string {
|
||||
const option = PROPERS_REPACKS_OPTIONS.find((o) => o.value === value);
|
||||
return option?.label ?? value;
|
||||
}
|
||||
@@ -37,3 +37,54 @@ export type { DelayProfilesRow } from './types.ts';
|
||||
|
||||
/** Preferred protocol options - extracted for use in mutations */
|
||||
export type PreferredProtocol = DelayProfilesRow['preferred_protocol'];
|
||||
|
||||
// ============================================================================
|
||||
// MEDIA MANAGEMENT
|
||||
// ============================================================================
|
||||
|
||||
import type { ArrType } from './types.ts';
|
||||
|
||||
// Naming
|
||||
export type { RadarrNamingRow, SonarrNamingRow } from './types.ts';
|
||||
|
||||
export interface NamingListItem {
|
||||
name: string;
|
||||
arr_type: Exclude<ArrType, 'all'>;
|
||||
rename: boolean;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
// Media Settings
|
||||
export type { RadarrMediaSettingsRow, SonarrMediaSettingsRow } from './types.ts';
|
||||
|
||||
export interface MediaSettingsListItem {
|
||||
name: string;
|
||||
arr_type: Exclude<ArrType, 'all'>;
|
||||
propers_repacks: string;
|
||||
enable_media_info: boolean;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
// Quality Definitions
|
||||
export type { RadarrQualityDefinitionsRow, SonarrQualityDefinitionsRow } from './types.ts';
|
||||
|
||||
export interface QualityDefinitionListItem {
|
||||
name: string;
|
||||
arr_type: Exclude<ArrType, 'all'>;
|
||||
quality_count: number;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
/** Single quality entry (Row without the config name) */
|
||||
export interface QualityDefinitionEntry {
|
||||
quality_name: string;
|
||||
min_size: number;
|
||||
max_size: number;
|
||||
preferred_size: number;
|
||||
}
|
||||
|
||||
/** Aggregate config with all its entries */
|
||||
export interface QualityDefinitionsConfig {
|
||||
name: string;
|
||||
entries: QualityDefinitionEntry[];
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*
|
||||
* AUTO-GENERATED - DO NOT EDIT MANUALLY
|
||||
*
|
||||
* Generated from: https://github.com/Dictionarry-Hub/schema/blob/local/ops/0.schema.sql
|
||||
* Generated at: 2026-01-27T12:38:46.562Z
|
||||
* Generated from: https://github.com/Dictionarry-Hub/schema/blob/1.0.0/ops/0.schema.sql
|
||||
* Generated at: 2026-01-27T13:04:08.653Z
|
||||
*
|
||||
* To regenerate: deno task generate:pcd-types --version=local
|
||||
* To regenerate: deno task generate:pcd-types --version=1.0.0
|
||||
*/
|
||||
|
||||
import type { Generated } from 'kysely';
|
||||
@@ -226,7 +226,7 @@ export interface DelayProfilesTable {
|
||||
// MEDIA MANAGEMENT
|
||||
|
||||
export interface RadarrNamingTable {
|
||||
name: string | null;
|
||||
name: string;
|
||||
rename: Generated<number>;
|
||||
movie_format: string;
|
||||
movie_folder_format: string;
|
||||
@@ -237,7 +237,7 @@ export interface RadarrNamingTable {
|
||||
}
|
||||
|
||||
export interface SonarrNamingTable {
|
||||
name: string | null;
|
||||
name: string;
|
||||
rename: Generated<number>;
|
||||
standard_episode_format: string;
|
||||
daily_episode_format: string;
|
||||
@@ -253,7 +253,7 @@ export interface SonarrNamingTable {
|
||||
}
|
||||
|
||||
export interface RadarrMediaSettingsTable {
|
||||
name: string | null;
|
||||
name: string;
|
||||
propers_repacks: Generated<string>;
|
||||
enable_media_info: Generated<number>;
|
||||
created_at: Generated<string>;
|
||||
@@ -261,7 +261,7 @@ export interface RadarrMediaSettingsTable {
|
||||
}
|
||||
|
||||
export interface SonarrMediaSettingsTable {
|
||||
name: string | null;
|
||||
name: string;
|
||||
propers_repacks: Generated<string>;
|
||||
enable_media_info: Generated<number>;
|
||||
created_at: Generated<string>;
|
||||
@@ -574,7 +574,7 @@ export interface DelayProfilesRow {
|
||||
// MEDIA MANAGEMENT
|
||||
|
||||
export interface RadarrNamingRow {
|
||||
name: string | null;
|
||||
name: string;
|
||||
rename: boolean;
|
||||
movie_format: string;
|
||||
movie_folder_format: string;
|
||||
@@ -585,7 +585,7 @@ export interface RadarrNamingRow {
|
||||
}
|
||||
|
||||
export interface SonarrNamingRow {
|
||||
name: string | null;
|
||||
name: string;
|
||||
rename: boolean;
|
||||
standard_episode_format: string;
|
||||
daily_episode_format: string;
|
||||
@@ -601,7 +601,7 @@ export interface SonarrNamingRow {
|
||||
}
|
||||
|
||||
export interface RadarrMediaSettingsRow {
|
||||
name: string | null;
|
||||
name: string;
|
||||
propers_repacks: 'doNotPrefer' | 'preferAndUpgrade' | 'doNotUpgradeAutomatically';
|
||||
enable_media_info: boolean;
|
||||
created_at: string;
|
||||
@@ -609,7 +609,7 @@ export interface RadarrMediaSettingsRow {
|
||||
}
|
||||
|
||||
export interface SonarrMediaSettingsRow {
|
||||
name: string | null;
|
||||
name: string;
|
||||
propers_repacks: 'doNotPrefer' | 'preferAndUpgrade' | 'doNotUpgradeAutomatically';
|
||||
enable_media_info: boolean;
|
||||
created_at: string;
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
import { alertStore } from '$alerts/store';
|
||||
import { Check, Save, Trash2 } from 'lucide-svelte';
|
||||
import { current, isDirty, initEdit, initCreate, update } from '$lib/client/stores/dirty';
|
||||
import type { MediaSettings, PropersRepacks } from '$lib/shared/mediaManagement.ts';
|
||||
import { PROPERS_REPACKS_OPTIONS } from '$lib/shared/mediaManagement.ts';
|
||||
import type { ArrType } from '$pcd/queries/mediaManagement/media-settings/types.ts';
|
||||
import type { RadarrMediaSettingsRow } from '$shared/pcd/display.ts';
|
||||
import type { ArrType } from '$shared/pcd/types.ts';
|
||||
import { PROPERS_REPACKS_OPTIONS, type PropersRepacks } from '$shared/pcd/conversions.ts';
|
||||
|
||||
interface MediaSettingsFormData {
|
||||
interface RadarrMediaSettingsRowFormData {
|
||||
name: string;
|
||||
propersRepacks: PropersRepacks;
|
||||
enableMediaInfo: boolean;
|
||||
@@ -24,15 +24,15 @@
|
||||
export let databaseName: string;
|
||||
export let canWriteToBase: boolean = false;
|
||||
export let actionUrl: string = '';
|
||||
export let initialData: MediaSettings | null;
|
||||
export let initialData: RadarrMediaSettingsRow | null;
|
||||
|
||||
const defaults: MediaSettingsFormData = {
|
||||
const defaults: RadarrMediaSettingsRowFormData = {
|
||||
name: '',
|
||||
propersRepacks: 'doNotPrefer',
|
||||
enableMediaInfo: true
|
||||
};
|
||||
|
||||
function mapToFormData(data: MediaSettings | null): MediaSettingsFormData {
|
||||
function mapToFormData(data: RadarrMediaSettingsRow | null): RadarrMediaSettingsRowFormData {
|
||||
if (!data) return defaults;
|
||||
return {
|
||||
name: data.name,
|
||||
@@ -47,7 +47,7 @@
|
||||
initEdit(mapToFormData(initialData));
|
||||
}
|
||||
|
||||
$: formData = $current as MediaSettingsFormData;
|
||||
$: formData = $current as RadarrMediaSettingsRowFormData;
|
||||
|
||||
let saving = false;
|
||||
let deleting = false;
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { PageServerLoad, Actions } from './$types';
|
||||
import { pcdManager } from '$pcd/pcd.ts';
|
||||
import { canWriteToBase } from '$pcd/writer.ts';
|
||||
import type { OperationLayer } from '$pcd/writer.ts';
|
||||
import type { ArrType } from '$pcd/queries/mediaManagement/media-settings/types.ts';
|
||||
import type { PropersRepacks } from '$lib/shared/mediaManagement.ts';
|
||||
import type { ArrType } from '$shared/pcd/types.ts';
|
||||
import type { PropersRepacks } from '$shared/pcd/conversions.ts';
|
||||
import { createRadarrMediaSettings, createSonarrMediaSettings } from '$pcd/queries/mediaManagement/media-settings/index.ts';
|
||||
|
||||
export const load: PageServerLoad = async ({ parent }) => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import RadarrIcon from '$lib/client/assets/Radarr.svg';
|
||||
import SonarrIcon from '$lib/client/assets/Sonarr.svg';
|
||||
import type { PageData } from './$types';
|
||||
import type { ArrType } from '$pcd/queries/mediaManagement/media-settings/types.ts';
|
||||
import type { ArrType } from '$shared/pcd/types.ts';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { pcdManager } from '$pcd/pcd.ts';
|
||||
import { canWriteToBase } from '$pcd/writer.ts';
|
||||
import type { OperationLayer } from '$pcd/writer.ts';
|
||||
import { getRadarrByName, updateRadarrMediaSettings, removeRadarrMediaSettings } from '$pcd/queries/mediaManagement/media-settings/index.ts';
|
||||
import type { PropersRepacks } from '$lib/shared/mediaManagement.ts';
|
||||
import type { PropersRepacks } from '$shared/pcd/conversions.ts';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, parent }) => {
|
||||
const { databaseId, name } = params;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { pcdManager } from '$pcd/pcd.ts';
|
||||
import { canWriteToBase } from '$pcd/writer.ts';
|
||||
import type { OperationLayer } from '$pcd/writer.ts';
|
||||
import { getSonarrByName, updateSonarrMediaSettings, removeSonarrMediaSettings } from '$pcd/queries/mediaManagement/media-settings/index.ts';
|
||||
import type { PropersRepacks } from '$lib/shared/mediaManagement.ts';
|
||||
import type { PropersRepacks } from '$shared/pcd/conversions.ts';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, parent }) => {
|
||||
const { databaseId, name } = params;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import type { Column } from '$ui/table/types';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Tag, Info, RefreshCw } from 'lucide-svelte';
|
||||
import type { MediaSettingsListItem } from '$pcd/queries/mediaManagement/media-settings/types.ts';
|
||||
import type { MediaSettingsListItem } from '$shared/pcd/display.ts';
|
||||
import radarrLogo from '$lib/client/assets/Radarr.svg';
|
||||
import sonarrLogo from '$lib/client/assets/Sonarr.svg';
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
import { alertStore } from '$alerts/store';
|
||||
import { Check, Save, Trash2 } from 'lucide-svelte';
|
||||
import { current, isDirty, initEdit, initCreate, update } from '$lib/client/stores/dirty';
|
||||
import type { RadarrNaming, RadarrColonReplacementFormat } from '$lib/shared/mediaManagement.ts';
|
||||
import { RADARR_COLON_REPLACEMENT_OPTIONS } from '$lib/shared/mediaManagement.ts';
|
||||
import type { RadarrNamingRow } from '$shared/pcd/display.ts';
|
||||
import { RADARR_COLON_REPLACEMENT_OPTIONS, type RadarrColonReplacementFormat } from '$shared/pcd/conversions.ts';
|
||||
|
||||
interface RadarrNamingFormData {
|
||||
name: string;
|
||||
@@ -25,7 +25,7 @@
|
||||
export let databaseName: string;
|
||||
export let canWriteToBase: boolean = false;
|
||||
export let actionUrl: string = '';
|
||||
export let initialData: RadarrNaming | null;
|
||||
export let initialData: RadarrNamingRow | null;
|
||||
|
||||
const defaults: RadarrNamingFormData = {
|
||||
name: '',
|
||||
@@ -36,7 +36,7 @@
|
||||
colonReplacementFormat: 'delete'
|
||||
};
|
||||
|
||||
function mapToFormData(data: RadarrNaming | null): RadarrNamingFormData {
|
||||
function mapToFormData(data: RadarrNamingRow | null): RadarrNamingFormData {
|
||||
if (!data) return defaults;
|
||||
return {
|
||||
name: data.name,
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
import { alertStore } from '$alerts/store';
|
||||
import { Check, Save, Trash2 } from 'lucide-svelte';
|
||||
import { current, isDirty, initEdit, initCreate, update } from '$lib/client/stores/dirty';
|
||||
import type { SonarrNaming, ColonReplacementFormat, MultiEpisodeStyle } from '$lib/shared/mediaManagement.ts';
|
||||
import { COLON_REPLACEMENT_OPTIONS, MULTI_EPISODE_STYLE_OPTIONS } from '$lib/shared/mediaManagement.ts';
|
||||
import type { SonarrNamingRow } from '$shared/pcd/display.ts';
|
||||
import { SONARR_COLON_REPLACEMENT_OPTIONS, MULTI_EPISODE_STYLE_OPTIONS, type SonarrColonReplacementFormat, type MultiEpisodeStyle } from '$shared/pcd/conversions.ts';
|
||||
|
||||
interface SonarrNamingFormData {
|
||||
name: string;
|
||||
@@ -20,7 +20,7 @@
|
||||
seriesFolderFormat: string;
|
||||
seasonFolderFormat: string;
|
||||
replaceIllegalCharacters: boolean;
|
||||
colonReplacementFormat: ColonReplacementFormat;
|
||||
colonReplacementFormat: SonarrColonReplacementFormat;
|
||||
customColonReplacementFormat: string;
|
||||
multiEpisodeStyle: MultiEpisodeStyle;
|
||||
[key: string]: unknown;
|
||||
@@ -30,7 +30,7 @@
|
||||
export let databaseName: string;
|
||||
export let canWriteToBase: boolean = false;
|
||||
export let actionUrl: string = '';
|
||||
export let initialData: SonarrNaming | null;
|
||||
export let initialData: SonarrNamingRow | null;
|
||||
|
||||
const defaults: SonarrNamingFormData = {
|
||||
name: '',
|
||||
@@ -46,7 +46,7 @@
|
||||
multiEpisodeStyle: 'extend'
|
||||
};
|
||||
|
||||
function mapToFormData(data: SonarrNaming | null): SonarrNamingFormData {
|
||||
function mapToFormData(data: SonarrNamingRow | null): SonarrNamingFormData {
|
||||
if (!data) return defaults;
|
||||
return {
|
||||
name: data.name,
|
||||
@@ -345,7 +345,7 @@
|
||||
Colon Replacement
|
||||
</span>
|
||||
<div class="mt-2 grid gap-2">
|
||||
{#each COLON_REPLACEMENT_OPTIONS as option}
|
||||
{#each SONARR_COLON_REPLACEMENT_OPTIONS as option}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => update('colonReplacementFormat', option.value)}
|
||||
|
||||
@@ -3,8 +3,7 @@ import type { PageServerLoad, Actions } from './$types';
|
||||
import { pcdManager } from '$pcd/pcd.ts';
|
||||
import { canWriteToBase } from '$pcd/writer.ts';
|
||||
import type { OperationLayer } from '$pcd/writer.ts';
|
||||
import type { ArrType } from '$pcd/queries/mediaManagement/naming/types.ts';
|
||||
import type { RadarrColonReplacementFormat, ColonReplacementFormat, MultiEpisodeStyle } from '$lib/shared/mediaManagement.ts';
|
||||
import type { RadarrNamingRow, SonarrNamingRow } from '$shared/pcd/display.ts';
|
||||
import { createRadarrNaming, createSonarrNaming } from '$pcd/queries/mediaManagement/naming/index.ts';
|
||||
|
||||
export const load: PageServerLoad = async ({ parent }) => {
|
||||
@@ -33,7 +32,7 @@ export const actions: Actions = {
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const arrType = formData.get('arrType') as ArrType;
|
||||
const arrType = formData.get('arrType') as 'radarr' | 'sonarr';
|
||||
const name = formData.get('name') as string;
|
||||
const layer = (formData.get('layer') as OperationLayer) || 'user';
|
||||
|
||||
@@ -56,7 +55,7 @@ export const actions: Actions = {
|
||||
const replaceIllegalCharacters = formData.get('replaceIllegalCharacters') === 'true';
|
||||
const colonReplacementFormat = formData.get(
|
||||
'colonReplacementFormat'
|
||||
) as RadarrColonReplacementFormat;
|
||||
) as RadarrNamingRow['colon_replacement_format'];
|
||||
|
||||
const result = await createRadarrNaming({
|
||||
databaseId: currentDatabaseId,
|
||||
@@ -85,9 +84,9 @@ export const actions: Actions = {
|
||||
const replaceIllegalCharacters = formData.get('replaceIllegalCharacters') === 'true';
|
||||
const colonReplacementFormat = formData.get(
|
||||
'colonReplacementFormat'
|
||||
) as ColonReplacementFormat;
|
||||
) as SonarrNamingRow['colon_replacement_format'];
|
||||
const customColonReplacementFormat = formData.get('customColonReplacementFormat') as string;
|
||||
const multiEpisodeStyle = formData.get('multiEpisodeStyle') as MultiEpisodeStyle;
|
||||
const multiEpisodeStyle = formData.get('multiEpisodeStyle') as SonarrNamingRow['multi_episode_style'];
|
||||
|
||||
const result = await createSonarrNaming({
|
||||
databaseId: currentDatabaseId,
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
import RadarrIcon from '$lib/client/assets/Radarr.svg';
|
||||
import SonarrIcon from '$lib/client/assets/Sonarr.svg';
|
||||
import type { PageData } from './$types';
|
||||
import type { ArrType } from '$pcd/queries/mediaManagement/naming/types.ts';
|
||||
import type { ArrType } from '$shared/pcd/types.ts';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let selectedArrType: ArrType | null = null;
|
||||
let selectedArrType: Exclude<ArrType, 'all'> | null = null;
|
||||
|
||||
const arrTypeOptions: { value: ArrType; label: string; description: string; icon: string }[] = [
|
||||
const arrTypeOptions: { value: Exclude<ArrType, 'all'>; label: string; description: string; icon: string }[] = [
|
||||
{
|
||||
value: 'radarr',
|
||||
label: 'Radarr',
|
||||
|
||||
@@ -4,7 +4,7 @@ import { pcdManager } from '$pcd/pcd.ts';
|
||||
import { canWriteToBase } from '$pcd/writer.ts';
|
||||
import type { OperationLayer } from '$pcd/writer.ts';
|
||||
import { getRadarrByName, updateRadarrNaming, removeRadarrNaming } from '$pcd/queries/mediaManagement/naming/index.ts';
|
||||
import type { RadarrColonReplacementFormat } from '$lib/shared/mediaManagement.ts';
|
||||
import type { RadarrNamingRow } from '$shared/pcd/display.ts';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, parent }) => {
|
||||
const { databaseId, name } = params;
|
||||
@@ -80,7 +80,7 @@ export const actions: Actions = {
|
||||
const replaceIllegalCharacters = formData.get('replaceIllegalCharacters') === 'true';
|
||||
const colonReplacementFormat = formData.get(
|
||||
'colonReplacementFormat'
|
||||
) as RadarrColonReplacementFormat;
|
||||
) as RadarrNamingRow['colon_replacement_format'];
|
||||
|
||||
const result = await updateRadarrNaming({
|
||||
databaseId: currentDatabaseId,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { pcdManager } from '$pcd/pcd.ts';
|
||||
import { canWriteToBase } from '$pcd/writer.ts';
|
||||
import type { OperationLayer } from '$pcd/writer.ts';
|
||||
import { getSonarrByName, updateSonarrNaming, removeSonarrNaming } from '$pcd/queries/mediaManagement/naming/index.ts';
|
||||
import type { ColonReplacementFormat, MultiEpisodeStyle } from '$lib/shared/mediaManagement.ts';
|
||||
import type { SonarrNamingRow } from '$shared/pcd/display.ts';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, parent }) => {
|
||||
const { databaseId, name } = params;
|
||||
@@ -81,9 +81,9 @@ export const actions: Actions = {
|
||||
const seriesFolderFormat = formData.get('seriesFolderFormat') as string;
|
||||
const seasonFolderFormat = formData.get('seasonFolderFormat') as string;
|
||||
const replaceIllegalCharacters = formData.get('replaceIllegalCharacters') === 'true';
|
||||
const colonReplacementFormat = formData.get('colonReplacementFormat') as ColonReplacementFormat;
|
||||
const colonReplacementFormat = formData.get('colonReplacementFormat') as SonarrNamingRow['colon_replacement_format'];
|
||||
const customColonReplacementFormat = formData.get('customColonReplacementFormat') as string;
|
||||
const multiEpisodeStyle = formData.get('multiEpisodeStyle') as MultiEpisodeStyle;
|
||||
const multiEpisodeStyle = formData.get('multiEpisodeStyle') as SonarrNamingRow['multi_episode_style'];
|
||||
|
||||
const result = await updateSonarrNaming({
|
||||
databaseId: currentDatabaseId,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import type { Column } from '$ui/table/types';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Tag, ToggleRight } from 'lucide-svelte';
|
||||
import type { NamingListItem } from '$pcd/queries/mediaManagement/naming/types.ts';
|
||||
import type { NamingListItem } from '$shared/pcd/display.ts';
|
||||
import radarrLogo from '$lib/client/assets/Radarr.svg';
|
||||
import sonarrLogo from '$lib/client/assets/Sonarr.svg';
|
||||
|
||||
|
||||
@@ -14,13 +14,42 @@
|
||||
import Dropdown from '$ui/dropdown/Dropdown.svelte';
|
||||
import DropdownItem from '$ui/dropdown/DropdownItem.svelte';
|
||||
import { isDirty, initEdit, initCreate, update } from '$lib/client/stores/dirty';
|
||||
import type { ArrType, QualityDefinitionsConfig, QualityDefinitionEntry } from '$pcd/queries/mediaManagement/quality-definitions/types.ts';
|
||||
import {
|
||||
type ResolutionGroup,
|
||||
RESOLUTION_GROUP_ORDER,
|
||||
RESOLUTION_GROUP_LABELS,
|
||||
getResolutionGroup
|
||||
} from '$lib/shared/mediaManagement.ts';
|
||||
import type { ArrType } from '$shared/pcd/types.ts';
|
||||
import type { QualityDefinitionsConfig, QualityDefinitionEntry } from '$shared/pcd/display.ts';
|
||||
|
||||
// Resolution grouping for quality definitions UI
|
||||
type ResolutionGroup = 'SD' | '720p' | '1080p' | '2160p' | 'Prereleases' | 'Other';
|
||||
|
||||
const RESOLUTION_GROUP_ORDER: ResolutionGroup[] = [
|
||||
'2160p',
|
||||
'1080p',
|
||||
'720p',
|
||||
'SD',
|
||||
'Prereleases',
|
||||
'Other'
|
||||
];
|
||||
|
||||
const RESOLUTION_GROUP_LABELS: Record<ResolutionGroup, string> = {
|
||||
'2160p': '4K Ultra HD (2160p)',
|
||||
'1080p': 'Full HD (1080p)',
|
||||
'720p': 'HD (720p)',
|
||||
SD: 'Standard Definition (SD)',
|
||||
Prereleases: 'Prereleases',
|
||||
Other: 'Other'
|
||||
};
|
||||
|
||||
const PRERELEASE_QUALITIES = ['cam', 'dvdscr', 'regional', 'telecine', 'telesync', 'workprint'];
|
||||
const OTHER_QUALITIES = ['raw-hd', 'unknown'];
|
||||
|
||||
function getResolutionGroup(qualityName: string): ResolutionGroup {
|
||||
const name = qualityName.toLowerCase();
|
||||
if (PRERELEASE_QUALITIES.some((q) => name === q || name.includes(q))) return 'Prereleases';
|
||||
if (OTHER_QUALITIES.some((q) => name === q || name.includes(q))) return 'Other';
|
||||
if (name.includes('2160') || name.includes('4k') || name.includes('uhd')) return '2160p';
|
||||
if (name.includes('1080')) return '1080p';
|
||||
if (name.includes('720')) return '720p';
|
||||
return 'SD';
|
||||
}
|
||||
|
||||
export let mode: 'create' | 'edit';
|
||||
export let arrType: ArrType;
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { PageServerLoad, Actions } from './$types';
|
||||
import { pcdManager } from '$pcd/pcd.ts';
|
||||
import { canWriteToBase } from '$pcd/writer.ts';
|
||||
import type { OperationLayer } from '$pcd/writer.ts';
|
||||
import type { ArrType } from '$pcd/queries/mediaManagement/quality-definitions/types.ts';
|
||||
import type { ArrType } from '$shared/pcd/types.ts';
|
||||
import { getAvailableQualities } from '$pcd/queries/mediaManagement/quality-definitions/read.ts';
|
||||
import { createRadarrQualityDefinitions, createSonarrQualityDefinitions } from '$pcd/queries/mediaManagement/quality-definitions/index.ts';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import RadarrIcon from '$lib/client/assets/Radarr.svg';
|
||||
import SonarrIcon from '$lib/client/assets/Sonarr.svg';
|
||||
import type { PageData } from './$types';
|
||||
import type { ArrType } from '$pcd/queries/mediaManagement/quality-definitions/types.ts';
|
||||
import type { ArrType } from '$shared/pcd/types.ts';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type { Column } from '$ui/table/types';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Tag } from 'lucide-svelte';
|
||||
import type { QualityDefinitionListItem } from '$pcd/queries/mediaManagement/quality-definitions/types.ts';
|
||||
import type { QualityDefinitionListItem } from '$shared/pcd/display.ts';
|
||||
import radarrLogo from '$lib/client/assets/Radarr.svg';
|
||||
import sonarrLogo from '$lib/client/assets/Sonarr.svg';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user