diff --git a/packages/core/src/db/schemas.ts b/packages/core/src/db/schemas.ts index 9be54bdb4..0fc411000 100644 --- a/packages/core/src/db/schemas.ts +++ b/packages/core/src/db/schemas.ts @@ -625,6 +625,13 @@ export const UserDataSchema = z.object({ externalDownloads: z.boolean().optional(), cacheAndPlay: CacheAndPlaySchema.optional(), + strmOutput: z + .object({ + mode: z.enum(['disabled', 'always', 'userAgent']).optional(), + userAgents: z.array(z.string().min(1)).optional(), + }) + .optional(), + autoRemoveDownloads: z.boolean().optional(), }); diff --git a/packages/core/src/streams/context.ts b/packages/core/src/streams/context.ts index c4feb366e..ae98bcfa1 100644 --- a/packages/core/src/streams/context.ts +++ b/packages/core/src/streams/context.ts @@ -189,7 +189,10 @@ export class StreamContext { this.userData.excludedStreamExpressions?.length || this.userData.requiredStreamExpressions?.length || this.userData.includedStreamExpressions?.length || - (this.userData.precacheNextEpisode && this.type === 'series'); + (this.userData.precacheNextEpisode && this.type === 'series') || + // STRM output needs metadata for filename generation (title, year, tmdbId) + (this.userData.strmOutput?.mode && + this.userData.strmOutput.mode !== 'disabled'); if (!needsMetadata || !this.parsedId) { this._metadataFetched = true; diff --git a/packages/frontend/src/components/menu/miscellaneous.tsx b/packages/frontend/src/components/menu/miscellaneous.tsx index aa8a9727b..d78a4fd36 100644 --- a/packages/frontend/src/components/menu/miscellaneous.tsx +++ b/packages/frontend/src/components/menu/miscellaneous.tsx @@ -448,6 +448,69 @@ function Content() { /> )} + {mode === 'pro' && ( + +

+ When enabled, stream URLs are routed through a gate endpoint. + If the player's User-Agent matches, a .strm file is + served instead of a direct link. +

+ +

+ In "User-Agent Dependent" mode, non-matching + clients are transparently redirected to the direct stream + URL. +

+
+ + } + > +