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 () => {
|
||||
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({
|
||||
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: {
|
||||
node: { select: { id: true, name: true } },
|
||||
mediaFile: {
|
||||
|
|
|
|||
|
|
@ -131,7 +131,8 @@ export async function registerStremioRoutes(app: FastifyInstance, config: Config
|
|||
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({
|
||||
method: ["GET", "HEAD"],
|
||||
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" });
|
||||
}
|
||||
|
||||
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 {
|
||||
const session = await playback.createSession(mediaFileId, userId);
|
||||
// 307 keeps method; Stremio/external players follow to the node.
|
||||
return reply.redirect(307, session.streamUrl);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : "Playback unavailable";
|
||||
|
|
@ -362,7 +377,6 @@ async function getStreams(
|
|||
url: playback.resolveStreamUrl(addonToken, file.id),
|
||||
behaviorHints: {
|
||||
notWebReady: true,
|
||||
bingeGroup: `mc-movie-${movie.id}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -409,7 +423,6 @@ async function getStreams(
|
|||
url: playback.resolveStreamUrl(addonToken, file.id),
|
||||
behaviorHints: {
|
||||
notWebReady: true,
|
||||
bingeGroup: `mc-series-${series.id}-S${seasonNumber}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue