From 37f063229c74c52eb67848f04e78f63659fcbbd7 Mon Sep 17 00:00:00 2001 From: H0tChicken Date: Sun, 3 May 2026 14:29:09 +0000 Subject: [PATCH] fix: use BigInt for PlexLibrary.duration to prevent INT4 overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The duration column (Int/int4, max ~2.15B) overflows when storing millisecond values for items with large durations from Audiobookshelf or Plex backends. Change to BigInt (int8) and wrap duration calculations in BigInt() at the Prisma write boundary. Changes: - prisma/schema.prisma: PlexLibrary.duration Int? → BigInt? - plex-recently-added.processor.ts: BigInt(Math.round(...)) wrapping - scan-plex.processor.ts: same BigInt wrapping - documentation/backend/database.md: updated duration type notation Fixes #193 Co-Authored-By: Oz --- documentation/backend/database.md | 2 +- prisma/schema.prisma | 2 +- src/lib/processors/plex-recently-added.processor.ts | 4 ++-- src/lib/processors/scan-plex.processor.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/backend/database.md b/documentation/backend/database.md index a16df96..c1811fb 100644 --- a/documentation/backend/database.md +++ b/documentation/backend/database.md @@ -35,7 +35,7 @@ PostgreSQL database storing users, audiobooks, requests, downloads, configuratio ### Plex_Library (Library Cache) - `id` (UUID PK), `plex_guid` (unique, external ID from Plex or Audiobookshelf), `plex_rating_key` -- `title`, `author`, `narrator`, `summary`, `duration` (milliseconds), `year`, `user_rating` (0-10 scale) +- `title`, `author`, `narrator`, `summary`, `duration` (BigInt, milliseconds), `year`, `user_rating` (0-10 scale) - **Universal identifiers:** `asin` (Audible ASIN), `isbn` (ISBN-10 or ISBN-13) - `file_path`, `thumb_url`, `cached_library_cover_path` (local cached cover path), `plex_library_id`, `added_at` - `last_scanned_at`, `created_at`, `updated_at` diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c3ee742..1e4c2cf 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -132,7 +132,7 @@ model PlexLibrary { author String narrator String? summary String? @db.Text - duration Int? // Duration in milliseconds (Plex format) + duration BigInt? // Duration in milliseconds (Plex format) year Int? userRating Decimal? @map("user_rating") @db.Decimal(3, 1) // User's rating (0-10 scale from Plex) diff --git a/src/lib/processors/plex-recently-added.processor.ts b/src/lib/processors/plex-recently-added.processor.ts index 4bf45ce..7dcb1cd 100644 --- a/src/lib/processors/plex-recently-added.processor.ts +++ b/src/lib/processors/plex-recently-added.processor.ts @@ -106,7 +106,7 @@ export async function processPlexRecentlyAddedCheck(payload: PlexRecentlyAddedPa author: item.author || 'Unknown Author', narrator: item.narrator, summary: item.description, - duration: item.duration ? item.duration * 1000 : null, // Convert seconds to milliseconds + duration: item.duration ? BigInt(Math.round(item.duration * 1000)) : null, // Convert seconds to milliseconds year: item.year, asin: item.asin, // Store ASIN from library backend isbn: item.isbn, // Store ISBN from library backend @@ -146,7 +146,7 @@ export async function processPlexRecentlyAddedCheck(payload: PlexRecentlyAddedPa author: item.author || existing.author, narrator: item.narrator || existing.narrator, summary: item.description || existing.summary, - duration: item.duration ? item.duration * 1000 : existing.duration, + duration: item.duration ? BigInt(Math.round(item.duration * 1000)) : existing.duration, year: item.year || existing.year, asin: item.asin || existing.asin, // Update ASIN if available isbn: item.isbn || existing.isbn, // Update ISBN if available diff --git a/src/lib/processors/scan-plex.processor.ts b/src/lib/processors/scan-plex.processor.ts index 4dfaf85..708045d 100644 --- a/src/lib/processors/scan-plex.processor.ts +++ b/src/lib/processors/scan-plex.processor.ts @@ -90,7 +90,7 @@ export async function processScanPlex(payload: ScanPlexPayload): Promise { author: item.author || existing.author, narrator: item.narrator || existing.narrator, summary: item.description || existing.summary, - duration: item.duration ? item.duration * 1000 : existing.duration, // Convert seconds to milliseconds + duration: item.duration ? BigInt(Math.round(item.duration * 1000)) : existing.duration, // Convert seconds to milliseconds year: item.year || existing.year, asin: item.asin || existing.asin, // Store ASIN from library backend isbn: item.isbn || existing.isbn, // Store ISBN from library backend @@ -132,7 +132,7 @@ export async function processScanPlex(payload: ScanPlexPayload): Promise { author: item.author || 'Unknown Author', narrator: item.narrator, summary: item.description, - duration: item.duration ? item.duration * 1000 : null, // Convert seconds to milliseconds + duration: item.duration ? BigInt(Math.round(item.duration * 1000)) : null, // Convert seconds to milliseconds year: item.year, asin: item.asin, // Store ASIN from library backend (Plex or Audiobookshelf) isbn: item.isbn, // Store ISBN from library backend