feat: add upcoming episodes and movies timeline (#17)

This commit is contained in:
jake authored and GitHub committed 2026-03-21 12:31:53 -04:00
1 parent 5aaf88591b
commit 1a503df2ae
42 files changed
+2405 -61

No files matched your search

+74 -1
View File
@@ -1,4 +1,4 @@
import { and, desc, eq, inArray, sql } from "drizzle-orm";
import { and, asc, desc, eq, gte, inArray, lte, sql } from "drizzle-orm";
import { db } from "../client";
import {
@@ -312,3 +312,76 @@ export function getRecommendationRowsForTitle(titleId: string) {
export function getTitleByIdOrNull(titleId: string) {
return db.select().from(titles).where(eq(titles.id, titleId)).get() ?? null;
}
// ─── Upcoming feed queries ──────────────────────────────────────────
export function getUpcomingEpisodes(userId: string, fromDate: string, toDate: string) {
return db
.select({
episodeId: episodes.id,
titleId: titles.id,
titleName: titles.title,
posterPath: titles.posterPath,
posterThumbHash: titles.posterThumbHash,
seasonNumber: seasons.seasonNumber,
episodeNumber: episodes.episodeNumber,
episodeName: episodes.name,
airDate: episodes.airDate,
userStatus: userTitleStatus.status,
})
.from(episodes)
.innerJoin(seasons, eq(episodes.seasonId, seasons.id))
.innerJoin(titles, and(eq(seasons.titleId, titles.id), eq(titles.type, "tv")))
.innerJoin(
userTitleStatus,
and(eq(userTitleStatus.titleId, titles.id), eq(userTitleStatus.userId, userId)),
)
.where(and(gte(episodes.airDate, fromDate), lte(episodes.airDate, toDate)))
.orderBy(asc(episodes.airDate), asc(titles.title))
.all();
}
export function getUpcomingMovies(userId: string, fromDate: string, toDate: string) {
return db
.select({
titleId: titles.id,
titleName: titles.title,
posterPath: titles.posterPath,
posterThumbHash: titles.posterThumbHash,
releaseDate: titles.releaseDate,
userStatus: userTitleStatus.status,
})
.from(titles)
.innerJoin(
userTitleStatus,
and(eq(userTitleStatus.titleId, titles.id), eq(userTitleStatus.userId, userId)),
)
.where(
and(
eq(titles.type, "movie"),
gte(titles.releaseDate, fromDate),
lte(titles.releaseDate, toDate),
),
)
.orderBy(asc(titles.releaseDate), asc(titles.title))
.all();
}
export function getAvailabilityByTitleIds(titleIds: string[]) {
if (titleIds.length === 0) return [];
return db
.select({
titleId: availabilityOffers.titleId,
providerId: availabilityOffers.providerId,
providerName: availabilityOffers.providerName,
logoPath: availabilityOffers.logoPath,
})
.from(availabilityOffers)
.where(
and(
inArray(availabilityOffers.titleId, titleIds),
eq(availabilityOffers.offerType, "flatrate"),
),
)
.all();
}
+1
View File
@@ -160,6 +160,7 @@ export const episodes = sqliteTable(
},
(table) => [
uniqueIndex("episodes_seasonId_episodeNumber").on(table.seasonId, table.episodeNumber),
index("episodes_airDate").on(table.airDate),
],
);
+23 -2
View File
@@ -63,6 +63,9 @@ export function insertTitle(
tvdbId?: number;
type?: "movie" | "tv";
title?: string;
releaseDate?: string;
posterPath?: string;
status?: string;
} = {},
) {
const id = overrides.id ?? "title-1";
@@ -74,14 +77,30 @@ export function insertTitle(
tvdbId: overrides.tvdbId,
type: overrides.type ?? "movie",
title: overrides.title ?? "Test Movie",
releaseDate: overrides.releaseDate,
posterPath: overrides.posterPath,
status: overrides.status,
})
.run();
return id;
}
export function insertTvShow(titleId = "tv-1", tmdbId = 99999, seasonCount = 1, epsPerSeason = 3) {
insertTitle({ id: titleId, tmdbId, type: "tv", title: "Test Show" });
export function insertTvShow(
titleId = "tv-1",
tmdbId = 99999,
seasonCount = 1,
epsPerSeason = 3,
options: { title?: string; airDates?: string[]; status?: string } = {},
) {
insertTitle({
id: titleId,
tmdbId,
type: "tv",
title: options.title ?? "Test Show",
status: options.status,
});
const episodeIds: string[] = [];
let airDateIdx = 0;
for (let s = 1; s <= seasonCount; s++) {
const seasonId = `${titleId}-s${s}`;
testDb.insert(seasons).values({ id: seasonId, titleId, seasonNumber: s }).run();
@@ -94,9 +113,11 @@ export function insertTvShow(titleId = "tv-1", tmdbId = 99999, seasonCount = 1,
seasonId,
episodeNumber: e,
name: `S${s}E${e}`,
airDate: options.airDates?.[airDateIdx],
})
.run();
episodeIds.push(epId);
airDateIdx++;
}
}
return { titleId, episodeIds };