feat: add backdrop_url to content schema and related database operations

This commit is contained in:
4gray committed 2026-04-22 22:20:48 +02:00
1 parent 423f693fe5
commit ad528238b2
4 files changed
+82 -11

No files matched your search

Vendored
+15 -3
View File
@@ -222,14 +222,21 @@ declare global {
) => Promise<any[]>;
dbGetGlobalRecentlyAdded: (
kind: 'all' | 'vod' | 'series',
limit?: number
limit?: number,
playlistType?:
| 'xtream'
| 'stalker'
| 'm3u-file'
| 'm3u-text'
| 'm3u-url'
) => Promise<any[]>;
dbGetRecentlyViewed: () => Promise<any[]>;
dbClearRecentlyViewed: () => Promise<{ success: boolean }>;
// Favorites
dbAddFavorite: (
contentId: number,
playlistId: string
playlistId: string,
backdropUrl?: string
) => Promise<{ success: boolean }>;
dbRemoveFavorite: (
contentId: number,
@@ -249,7 +256,8 @@ declare global {
dbGetRecentItems: (playlistId: string) => Promise<any[]>;
dbAddRecentItem: (
contentId: number,
playlistId: string
playlistId: string,
backdropUrl?: string
) => Promise<{ success: boolean }>;
dbClearPlaylistRecentItems: (
playlistId: string
@@ -263,6 +271,10 @@ declare global {
playlistId: string,
contentType?: 'live' | 'movie' | 'series'
) => Promise<any | null>;
dbSetContentBackdropIfMissing: (
contentId: number,
backdropUrl?: string
) => Promise<{ success: boolean }>;
dbGetAppState: (key: string) => Promise<string | null>;
dbSetAppState: (
key: string,
@@ -28,6 +28,7 @@ export interface XtreamContent {
rating: string;
added: string;
poster_url: string;
backdrop_url?: string | null;
epg_channel_id?: string | null;
tv_archive?: number | null;
tv_archive_duration?: number | null;
@@ -124,6 +125,13 @@ export function isDbAbortError(error: unknown): boolean {
export type GlobalRecentlyAddedKind = 'all' | 'vod' | 'series';
export type GlobalRecentlyAddedPlaylistType =
| 'xtream'
| 'stalker'
| 'm3u-file'
| 'm3u-text'
| 'm3u-url';
export interface GlobalRecentlyAddedItem extends XtreamContent {
playlist_id: string;
playlist_name: string;
@@ -581,15 +589,20 @@ export class DatabaseService {
/**
* Get recently added VOD and series items across all Xtream playlists.
* When `playlistType` is supplied, the LIMIT window is applied only to
* rows from playlists of that type, so Xtream items cannot be squeezed
* out by newer M3U/Stalker content.
*/
async getGlobalRecentlyAdded(
kind: GlobalRecentlyAddedKind,
limit = 200
limit = 200,
playlistType?: GlobalRecentlyAddedPlaylistType
): Promise<GlobalRecentlyAddedItem[]> {
try {
const items = await window.electron.dbGetGlobalRecentlyAdded(
kind,
limit
limit,
playlistType
);
return items || [];
} catch (error) {
@@ -671,14 +684,22 @@ export class DatabaseService {
}
/**
* Add content to favorites
* Add content to favorites.
* @param backdropUrl optionally persisted to `content.backdrop_url` when
* the row doesn't already have one. Lets the dashboard hero surface a
* cinematic backdrop without a separate round-trip.
*/
async addToFavorites(
contentId: number,
playlistId: string
playlistId: string,
backdropUrl?: string
): Promise<boolean> {
try {
await window.electron.dbAddFavorite(contentId, playlistId);
await window.electron.dbAddFavorite(
contentId,
playlistId,
backdropUrl
);
return true;
} catch (error) {
console.error('Error adding to favorites:', error);
@@ -739,14 +760,19 @@ export class DatabaseService {
}
/**
* Add item to recently viewed
* Add item to recently viewed. See `addToFavorites` for `backdropUrl`.
*/
async addRecentItem(
contentId: number,
playlistId: string
playlistId: string,
backdropUrl?: string
): Promise<boolean> {
try {
await window.electron.dbAddRecentItem(contentId, playlistId);
await window.electron.dbAddRecentItem(
contentId,
playlistId,
backdropUrl
);
return true;
} catch (error) {
console.error('Error adding recent item:', error);
@@ -754,6 +780,35 @@ export class DatabaseService {
}
}
/**
* Persist a backdrop URL onto an Xtream content row without touching
* recently viewed ordering or timestamps.
*/
async setContentBackdropIfMissing(
contentId: number,
backdropUrl?: string
): Promise<boolean> {
const normalizedBackdropUrl = backdropUrl?.trim();
if (!normalizedBackdropUrl) {
return true;
}
if (!window.electron?.dbSetContentBackdropIfMissing) {
return true;
}
try {
await window.electron.dbSetContentBackdropIfMissing(
contentId,
normalizedBackdropUrl
);
return true;
} catch (error) {
console.error('Error backfilling content backdrop:', error);
return false;
}
}
/**
* Clear recently viewed for a specific playlist
*/
@@ -112,6 +112,7 @@ const CREATE_TABLE_STATEMENTS = [
rating TEXT,
added TEXT,
poster_url TEXT,
backdrop_url TEXT,
epg_channel_id TEXT,
tv_archive INTEGER,
tv_archive_duration INTEGER,
@@ -269,6 +270,8 @@ const COLUMN_MIGRATION_STATEMENTS = [
`ALTER TABLE content ADD COLUMN tv_archive INTEGER`,
`ALTER TABLE content ADD COLUMN tv_archive_duration INTEGER`,
`ALTER TABLE content ADD COLUMN direct_source TEXT`,
// v1.5.0 -> v1.6.0: Cinematic backdrop persisted on first detail fetch
`ALTER TABLE content ADD COLUMN backdrop_url TEXT`,
];
const INDEX_MIGRATION_STATEMENTS = [
+1
View File
@@ -87,6 +87,7 @@ export const content = sqliteTable(
rating: text('rating'),
added: text('added'),
posterUrl: text('poster_url'),
backdropUrl: text('backdrop_url'),
epgChannelId: text('epg_channel_id'),
tvArchive: integer('tv_archive'),
tvArchiveDuration: integer('tv_archive_duration'),