Stop Stremio binge prefetch creating phantom next-episode streams.
Remove bingeGroup, skip session creation on HEAD probes, and hide stale sessions in admin. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b1d008e8aa
commit
0608020556
2 changed files with 35 additions and 5 deletions
|
|
@ -164,8 +164,25 @@ export async function registerAdminRoutes(app: FastifyInstance, config: Config)
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/v1/admin/streams", { preHandler: requireAdmin }, async () => {
|
app.get("/api/v1/admin/streams", { preHandler: requireAdmin }, async () => {
|
||||||
|
const now = new Date();
|
||||||
|
// Drop expired rows so prefetch leftovers do not clutter the UI.
|
||||||
|
await prisma.playbackSession.updateMany({
|
||||||
|
where: {
|
||||||
|
status: "ACTIVE",
|
||||||
|
revoked: false,
|
||||||
|
absoluteExpiresAt: { lt: now },
|
||||||
|
},
|
||||||
|
data: { status: "EXPIRED" },
|
||||||
|
});
|
||||||
|
|
||||||
const sessions = await prisma.playbackSession.findMany({
|
const sessions = await prisma.playbackSession.findMany({
|
||||||
where: { status: "ACTIVE", revoked: false },
|
where: {
|
||||||
|
status: "ACTIVE",
|
||||||
|
revoked: false,
|
||||||
|
absoluteExpiresAt: { gt: now },
|
||||||
|
// Only show recently touched sessions (prefetch often never streams bytes).
|
||||||
|
lastActivity: { gt: new Date(now.getTime() - 15 * 60 * 1000) },
|
||||||
|
},
|
||||||
include: {
|
include: {
|
||||||
node: { select: { id: true, name: true } },
|
node: { select: { id: true, name: true } },
|
||||||
mediaFile: {
|
mediaFile: {
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,8 @@ export async function registerStremioRoutes(app: FastifyInstance, config: Config
|
||||||
return { streams };
|
return { streams };
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lazy session: create on play so the token is fresh; redirect to node stream URL.
|
// Lazy session: create on GET play so the token is fresh; redirect to node stream URL.
|
||||||
|
// HEAD is treated as a probe — do not create a session (Stremio binge/prefetch uses HEAD/GET).
|
||||||
app.route({
|
app.route({
|
||||||
method: ["GET", "HEAD"],
|
method: ["GET", "HEAD"],
|
||||||
url: "/stremio/:token/play/:mediaFileId",
|
url: "/stremio/:token/play/:mediaFileId",
|
||||||
|
|
@ -142,9 +143,23 @@ export async function registerStremioRoutes(app: FastifyInstance, config: Config
|
||||||
return reply.status(401).send({ error: "Invalid addon token" });
|
return reply.status(401).send({ error: "Invalid addon token" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.method === "HEAD") {
|
||||||
|
const mediaFile = await prisma.mediaFile.findUnique({
|
||||||
|
where: { id: mediaFileId },
|
||||||
|
include: { node: true },
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
!mediaFile?.available ||
|
||||||
|
mediaFile.node.revoked ||
|
||||||
|
mediaFile.node.status !== "ONLINE"
|
||||||
|
) {
|
||||||
|
return reply.status(404).send();
|
||||||
|
}
|
||||||
|
return reply.status(200).send();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await playback.createSession(mediaFileId, userId);
|
const session = await playback.createSession(mediaFileId, userId);
|
||||||
// 307 keeps method; Stremio/external players follow to the node.
|
|
||||||
return reply.redirect(307, session.streamUrl);
|
return reply.redirect(307, session.streamUrl);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err instanceof Error ? err.message : "Playback unavailable";
|
const message = err instanceof Error ? err.message : "Playback unavailable";
|
||||||
|
|
@ -362,7 +377,6 @@ async function getStreams(
|
||||||
url: playback.resolveStreamUrl(addonToken, file.id),
|
url: playback.resolveStreamUrl(addonToken, file.id),
|
||||||
behaviorHints: {
|
behaviorHints: {
|
||||||
notWebReady: true,
|
notWebReady: true,
|
||||||
bingeGroup: `mc-movie-${movie.id}`,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -409,7 +423,6 @@ async function getStreams(
|
||||||
url: playback.resolveStreamUrl(addonToken, file.id),
|
url: playback.resolveStreamUrl(addonToken, file.id),
|
||||||
behaviorHints: {
|
behaviorHints: {
|
||||||
notWebReady: true,
|
notWebReady: true,
|
||||||
bingeGroup: `mc-series-${series.id}-S${seasonNumber}`,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue