fix: use name+tmdbid primary key instead of auto incmremented id

This commit is contained in:
Sam Chau
2026-01-19 04:12:37 +10:30
parent 95930edc53
commit 4f565ebd6f
13 changed files with 87 additions and 85 deletions

View File

@@ -33,7 +33,7 @@ export async function getAllConditionsForEvaluation(
// Get all conditions for all formats
const conditions = await db
.selectFrom('custom_format_conditions')
.select(['id', 'custom_format_id', 'name', 'type', 'negate', 'required'])
.select(['id', 'custom_format_id', 'name', 'type', 'arr_type', 'negate', 'required'])
.execute();
if (conditions.length === 0) {
@@ -198,6 +198,7 @@ export async function getAllConditionsForEvaluation(
id: c.id,
name: c.name,
type: c.type,
arrType: c.arr_type as 'all' | 'radarr' | 'sonarr',
negate: c.negate === 1,
required: c.required === 1,
patterns: patternsMap.get(c.id),

View File

@@ -6,7 +6,8 @@ import type { PCDCache } from '../../cache.ts';
import { writeOperation, type OperationLayer } from '../../writer.ts';
export interface CreateTestReleaseInput {
entityId: number;
entityType: 'movie' | 'series';
entityTmdbId: number;
title: string;
size_bytes: number | null;
languages: string[];
@@ -31,7 +32,8 @@ export async function createRelease(options: CreateTestReleaseOptions) {
const insertRelease = db
.insertInto('test_releases')
.values({
test_entity_id: input.entityId,
entity_type: input.entityType,
entity_tmdb_id: input.entityTmdbId,
title: input.title,
size_bytes: input.size_bytes,
languages: JSON.stringify(input.languages),

View File

@@ -6,7 +6,8 @@ import type { PCDCache } from '../../cache.ts';
import { writeOperation, type OperationLayer } from '../../writer.ts';
export interface CreateTestReleasesInput {
entityId: number;
entityType: 'movie' | 'series';
entityTmdbId: number;
title: string;
size_bytes: number | null;
languages: string[];
@@ -37,14 +38,16 @@ export async function createReleases(options: CreateTestReleasesOptions) {
};
}
// Get the entity ID (all inputs should have the same entityId)
const entityId = inputs[0].entityId;
// Get the entity key (all inputs should have the same entity)
const entityType = inputs[0].entityType;
const entityTmdbId = inputs[0].entityTmdbId;
// Check for existing releases for this entity
const existingReleases = await db
.selectFrom('test_releases')
.select(['title'])
.where('test_entity_id', '=', entityId)
.where('entity_type', '=', entityType)
.where('entity_tmdb_id', '=', entityTmdbId)
.execute();
const existingTitles = new Set(existingReleases.map((r) => r.title));
@@ -69,7 +72,8 @@ export async function createReleases(options: CreateTestReleasesOptions) {
const insertRelease = db
.insertInto('test_releases')
.values({
test_entity_id: input.entityId,
entity_type: input.entityType,
entity_tmdb_id: input.entityTmdbId,
title: input.title,
size_bytes: input.size_bytes,
languages: JSON.stringify(input.languages),

View File

@@ -9,34 +9,42 @@ export interface DeleteTestEntityOptions {
databaseId: number;
cache: PCDCache;
layer: OperationLayer;
entityId: number;
entityType: 'movie' | 'series';
entityTmdbId: number;
entityTitle: string; // For metadata
}
/**
* Delete a test entity and its releases by writing an operation to the specified layer
* Uses stable composite key (type, tmdb_id) instead of auto-generated id
*/
export async function remove(options: DeleteTestEntityOptions) {
const { databaseId, cache, layer, entityId } = options;
const { databaseId, cache, layer, entityType, entityTmdbId, entityTitle } = options;
const db = cache.kb;
// Delete releases first (foreign key constraint)
// Delete releases first (uses composite FK)
const deleteReleases = db
.deleteFrom('test_releases')
.where('test_entity_id', '=', entityId)
.where('entity_type', '=', entityType)
.where('entity_tmdb_id', '=', entityTmdbId)
.compile();
// Delete the entity
const deleteEntity = db.deleteFrom('test_entities').where('id', '=', entityId).compile();
// Delete the entity using stable composite key
const deleteEntity = db
.deleteFrom('test_entities')
.where('type', '=', entityType)
.where('tmdb_id', '=', entityTmdbId)
.compile();
const result = await writeOperation({
databaseId,
layer,
description: `delete-test-entity-${entityId}`,
description: `delete-test-entity-${entityTitle.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()}`,
queries: [deleteReleases, deleteEntity],
metadata: {
operation: 'delete',
entity: 'test_entity',
name: `id:${entityId}`
name: entityTitle
}
});

View File

@@ -28,14 +28,13 @@ export async function list(cache: PCDCache) {
if (entities.length === 0) return [];
const entityIds = entities.map((e) => e.id);
// 2. Get all releases for all entities
const allReleases = await db
.selectFrom('test_releases')
.select([
'id',
'test_entity_id',
'entity_type',
'entity_tmdb_id',
'title',
'size_bytes',
'languages',
@@ -44,24 +43,25 @@ export async function list(cache: PCDCache) {
'created_at',
'updated_at'
])
.where('test_entity_id', 'in', entityIds)
.orderBy('test_entity_id')
.orderBy('entity_type')
.orderBy('entity_tmdb_id')
.orderBy('title')
.execute();
// Build releases map
const releasesMap = new Map<number, typeof allReleases>();
// Build releases map using composite key
const releasesMap = new Map<string, typeof allReleases>();
for (const release of allReleases) {
if (!releasesMap.has(release.test_entity_id)) {
releasesMap.set(release.test_entity_id, []);
const key = `${release.entity_type}-${release.entity_tmdb_id}`;
if (!releasesMap.has(key)) {
releasesMap.set(key, []);
}
releasesMap.get(release.test_entity_id)!.push(release);
releasesMap.get(key)!.push(release);
}
// Build the final result
return entities.map((entity) => ({
...entity,
releases: (releasesMap.get(entity.id) || []).map((r) => ({
releases: (releasesMap.get(`${entity.type}-${entity.tmdb_id}`) || []).map((r) => ({
id: r.id,
title: r.title,
size_bytes: r.size_bytes !== null ? Number(r.size_bytes) : null,

View File

@@ -221,7 +221,8 @@ export interface TestEntitiesTable {
export interface TestReleasesTable {
id: Generated<number>;
test_entity_id: number;
entity_type: 'movie' | 'series';
entity_tmdb_id: number;
title: string;
size_bytes: number | null;
languages: string; // JSON array

View File

@@ -237,36 +237,3 @@ export function groupSonarrReleases(
return groups;
}
// =============================================================================
// Test Release Conversion
// =============================================================================
/**
* Shape for PCD test release creation
*/
export interface TestReleaseInput {
entityId: number;
title: string;
size_bytes: number | null;
languages: string[];
indexers: string[];
flags: string[];
}
/**
* Convert grouped releases to test release inputs
*/
export function groupedReleasesToTestInputs(
groupedReleases: GroupedRelease[],
entityId: number
): TestReleaseInput[] {
return groupedReleases.map((r) => ({
entityId,
title: r.title,
size_bytes: r.size,
languages: r.languages,
indexers: r.indexers,
flags: r.flags
}));
}