mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
feat: add check owned option
This commit is contained in:
@@ -93,6 +93,9 @@ BUILTIN_DEBRID_INSTANT_AVAILABILITY_CACHE_TTL=1800
|
||||
# How long download links for an item remain cached for.
|
||||
# Default: 1 hour
|
||||
BUILTIN_DEBRID_PLAYBACK_LINK_CACHE_TTL=3600
|
||||
# How long the list of downloads in the user's library is cached for.
|
||||
# Default: 5 minutes
|
||||
BUILTIN_DEBRID_LIBRARY_CACHE_TTL=300
|
||||
|
||||
# The timeout for getting the magnet from a torrent link. Happens in bulk during searches when only download URL is present.
|
||||
BUILTIN_GET_TORRENT_TIMEOUT=5000
|
||||
|
||||
@@ -61,6 +61,7 @@ export const BaseDebridConfigSchema = z.object({
|
||||
tmdbReadAccessToken: z.string().optional(),
|
||||
tvdbApiKey: z.string().optional(),
|
||||
cacheAndPlay: CacheAndPlaySchema.optional(),
|
||||
checkOwned: z.boolean().optional().default(true),
|
||||
});
|
||||
export type BaseDebridConfig = z.infer<typeof BaseDebridConfigSchema>;
|
||||
|
||||
@@ -244,7 +245,14 @@ export abstract class BaseDebridAddon<T extends BaseDebridConfig> {
|
||||
searchMetadata,
|
||||
this.clientIp
|
||||
),
|
||||
processNZBs(nzbResults, nzbServices, id, searchMetadata, this.clientIp),
|
||||
processNZBs(
|
||||
nzbResults,
|
||||
nzbServices,
|
||||
id,
|
||||
searchMetadata,
|
||||
this.clientIp,
|
||||
this.userData.checkOwned
|
||||
),
|
||||
]);
|
||||
|
||||
const encryptedStoreAuths = this.userData.services.reduce(
|
||||
@@ -706,7 +714,7 @@ export abstract class BaseDebridAddon<T extends BaseDebridConfig> {
|
||||
: '⏳'
|
||||
: '';
|
||||
|
||||
const name = `[${shortCode} ${cacheIndicator}${torrentOrNzb.service?.owned ? ' ☁️' : ''}] ${this.name}`;
|
||||
const name = `[${shortCode} ${cacheIndicator}${torrentOrNzb.service?.library ? ' ☁️' : ''}] ${this.name}`;
|
||||
const description = `${torrentOrNzb.title}\n${torrentOrNzb.file.name}\n${
|
||||
torrentOrNzb.indexer ? `🔍 ${torrentOrNzb.indexer}` : ''
|
||||
} ${'seeders' in torrentOrNzb && torrentOrNzb.seeders ? `👤 ${torrentOrNzb.seeders}` : ''} ${
|
||||
|
||||
@@ -131,7 +131,7 @@ abstract class SourceHandler {
|
||||
? parseAgeString(torrentOrNzb.age)
|
||||
: undefined;
|
||||
|
||||
const name = `[${shortCode} ${cacheIndicator}${torrentOrNzb.service?.owned ? ' ☁️' : ''}] TorBox Search`;
|
||||
const name = `[${shortCode} ${cacheIndicator}${torrentOrNzb.service?.library ? ' ☁️' : ''}] TorBox Search`;
|
||||
const description = `${torrentOrNzb.title}\n${torrentOrNzb.file.name}\n${
|
||||
torrentOrNzb.indexer ? `🔍 ${torrentOrNzb.indexer}` : ''
|
||||
} ${'seeders' in torrentOrNzb && torrentOrNzb.seeders ? `👤 ${torrentOrNzb.seeders}` : ''} ${
|
||||
@@ -347,7 +347,7 @@ export class TorrentSourceHandler extends SourceHandler {
|
||||
);
|
||||
|
||||
results.forEach((result) => {
|
||||
result.service!.owned =
|
||||
result.service!.library =
|
||||
fetchResult.torrents.find((torrent) => torrent.hash === result.hash)
|
||||
?.owned ?? false;
|
||||
});
|
||||
@@ -586,7 +586,7 @@ export class UsenetSourceHandler extends SourceHandler {
|
||||
);
|
||||
|
||||
results.forEach((result) => {
|
||||
result.service!.owned =
|
||||
result.service!.library =
|
||||
nzbs.find((nzb) => nzb.hash === result.hash)?.owned ?? false;
|
||||
});
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ async function processTorrentsForDebridService(
|
||||
service: {
|
||||
id: service.id,
|
||||
cached: magnetCheckResult?.status === 'cached',
|
||||
owned: false,
|
||||
library: magnetCheckResult?.library === true,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -235,6 +235,8 @@ async function processTorrentsForDebridService(
|
||||
logger.debug(`Finished processing of torrents`, {
|
||||
service: service.id,
|
||||
torrents: torrents.length,
|
||||
validTorrents: validTorrents.length,
|
||||
finalTorrents: results.length,
|
||||
totalTime: getTimeTakenSincePoint(processingStart),
|
||||
parseTime,
|
||||
});
|
||||
@@ -329,7 +331,8 @@ export async function processNZBs(
|
||||
debridServices: BuiltinDebridServices,
|
||||
stremioId: string,
|
||||
metadata?: Metadata,
|
||||
clientIp?: string
|
||||
clientIp?: string,
|
||||
checkOwned: boolean = true
|
||||
): Promise<{
|
||||
results: NZBWithSelectedFile[];
|
||||
errors: { serviceId: BuiltinServiceId; error: Error }[];
|
||||
@@ -347,7 +350,8 @@ export async function processNZBs(
|
||||
service,
|
||||
stremioId,
|
||||
metadata,
|
||||
clientIp
|
||||
clientIp,
|
||||
checkOwned
|
||||
);
|
||||
return { serviceId: service.id, results: serviceResults, error: null };
|
||||
} catch (error) {
|
||||
@@ -375,7 +379,8 @@ async function processNZBsForDebridService(
|
||||
service: BuiltinDebridServices[number],
|
||||
stremioId: string,
|
||||
metadata?: Metadata,
|
||||
clientIp?: string
|
||||
clientIp?: string,
|
||||
checkOwned: boolean = true
|
||||
): Promise<NZBWithSelectedFile[]> {
|
||||
const startTime = Date.now();
|
||||
const debridService = getDebridService(
|
||||
@@ -391,7 +396,8 @@ async function processNZBsForDebridService(
|
||||
const results: NZBWithSelectedFile[] = [];
|
||||
|
||||
const nzbCheckResults = await debridService.checkNzbs(
|
||||
nzbs.map((nzb) => nzb.hash)
|
||||
nzbs.map((nzb) => ({ name: nzb.title, hash: nzb.hash })),
|
||||
checkOwned
|
||||
);
|
||||
|
||||
logger.debug(`Retrieved NZB status from debrid`, {
|
||||
@@ -413,7 +419,7 @@ async function processNZBsForDebridService(
|
||||
// Filter NZBs that pass validation checks
|
||||
const validNZBs: {
|
||||
nzb: NZB;
|
||||
nzbCheckResult: any;
|
||||
nzbCheckResult: DebridDownload | undefined;
|
||||
parsedTitle: ParsedResult;
|
||||
}[] = [];
|
||||
for (const nzb of nzbs) {
|
||||
@@ -471,15 +477,19 @@ async function processNZBsForDebridService(
|
||||
service: {
|
||||
id: service.id,
|
||||
cached: nzbCheckResult?.status === 'cached',
|
||||
owned: false,
|
||||
library: nzbCheckResult?.library === true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`Processed ${nzbs.length} NZBs for ${service.id} in ${getTimeTakenSincePoint(processingStart)}`
|
||||
);
|
||||
logger.debug(`Finished processing of NZBs`, {
|
||||
service: service.id,
|
||||
nzbs: nzbs.length,
|
||||
validNzbs: validNZBs.length,
|
||||
finalNzbs: results.length,
|
||||
totalTime: getTimeTakenSincePoint(processingStart),
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
UsenetStreamService,
|
||||
UsenetStreamServiceConfig,
|
||||
} from './usenet-stream-base.js';
|
||||
import { DebridServiceConfig } from './base.js';
|
||||
import { DebridServiceConfig, PlaybackInfo } from './base.js';
|
||||
import { ServiceId, createLogger, fromUrlSafeBase64 } from '../utils/index.js';
|
||||
import { basename } from 'path';
|
||||
|
||||
@@ -49,7 +49,10 @@ export class AltmountService extends UsenetStreamService {
|
||||
return '/complete';
|
||||
}
|
||||
|
||||
protected getExpectedFolderName(nzbUrl: string, filename: string): string {
|
||||
protected getExpectedFolderName(
|
||||
nzb: PlaybackInfo & { type: 'usenet' }
|
||||
): string {
|
||||
const nzbUrl = nzb.nzb;
|
||||
// Altmount uses basename of the NZB URL
|
||||
return nzbUrl.endsWith('.nzb')
|
||||
? basename(nzbUrl, '.nzb')
|
||||
|
||||
@@ -86,6 +86,7 @@ export type DebridFile = z.infer<typeof DebridFileSchema>;
|
||||
|
||||
export interface DebridDownload {
|
||||
id: string | number;
|
||||
library?: boolean;
|
||||
hash?: string;
|
||||
name?: string;
|
||||
size?: number;
|
||||
@@ -174,7 +175,10 @@ export interface DebridService {
|
||||
generateTorrentLink(link: string, clientIp?: string): Promise<string>;
|
||||
|
||||
// Usenet specific methods
|
||||
checkNzbs?(nzbs: string[]): Promise<DebridDownload[]>;
|
||||
checkNzbs?(
|
||||
nzbs: { name?: string; hash?: string }[],
|
||||
checkOwned?: boolean
|
||||
): Promise<DebridDownload[]>;
|
||||
listNzbs?(id?: string): Promise<DebridDownload[]>;
|
||||
addNzb?(nzb: string, name: string): Promise<DebridDownload>;
|
||||
generateUsenetLink?(
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
UsenetStreamService,
|
||||
UsenetStreamServiceConfig,
|
||||
} from './usenet-stream-base.js';
|
||||
import { DebridServiceConfig } from './base.js';
|
||||
import { DebridServiceConfig, PlaybackInfo } from './base.js';
|
||||
import { ServiceId, createLogger, fromUrlSafeBase64 } from '../utils/index.js';
|
||||
|
||||
const logger = createLogger('nzbdav');
|
||||
@@ -51,8 +51,9 @@ export class NzbDAVService extends UsenetStreamService {
|
||||
return '/content';
|
||||
}
|
||||
|
||||
protected getExpectedFolderName(nzbUrl: string, filename: string): string {
|
||||
// NzbDAV uses the filename parameter
|
||||
return filename;
|
||||
protected getExpectedFolderName(
|
||||
nzb: PlaybackInfo & { type: 'usenet' }
|
||||
): string {
|
||||
return nzb.filename ?? 'unknown_nzb';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,10 +64,16 @@ export class TorboxDebridService implements DebridService {
|
||||
return this.stremthru.generateTorrentLink(link, clientIp);
|
||||
}
|
||||
|
||||
public async checkNzbs(hashes: string[]): Promise<DebridDownload[]> {
|
||||
public async checkNzbs(
|
||||
nzbs: { name?: string; hash?: string }[]
|
||||
): Promise<DebridDownload[]> {
|
||||
nzbs = nzbs.filter((nzb) => nzb.hash);
|
||||
if (nzbs.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const cachedResults: DebridDownload[] = [];
|
||||
const hashesToCheck: string[] = [];
|
||||
for (const hash of hashes) {
|
||||
for (const { hash } of nzbs as { hash: string }[]) {
|
||||
const cacheKey = getSimpleTextHash(hash);
|
||||
const cached =
|
||||
await TorboxDebridService.instantAvailabilityCache.get(cacheKey);
|
||||
|
||||
@@ -118,7 +118,10 @@ export class SABnzbdApi {
|
||||
}
|
||||
|
||||
protected async request<T extends z.ZodType>(
|
||||
params: Record<string, string>,
|
||||
params: Record<
|
||||
string,
|
||||
string | undefined | number | boolean | null | string[]
|
||||
>,
|
||||
schema: T,
|
||||
timeoutMs: number = 80000
|
||||
): Promise<{
|
||||
@@ -129,12 +132,14 @@ export class SABnzbdApi {
|
||||
}> {
|
||||
const url = new URL(this.apiUrl);
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
url.searchParams.append(key, value);
|
||||
if (!value) return;
|
||||
const val = Array.isArray(value) ? value.join(',') : String(value);
|
||||
url.searchParams.append(key, val);
|
||||
});
|
||||
|
||||
this.logger.debug(`Making ${this.serviceName} API request`, {
|
||||
...params,
|
||||
apikey: maskSensitiveInfo(params.apikey),
|
||||
apikey: maskSensitiveInfo(this.apiKey),
|
||||
fullUrl: maskSensitiveInfo(url.toString()),
|
||||
});
|
||||
|
||||
@@ -299,6 +304,47 @@ export class SABnzbdApi {
|
||||
return { nzoId };
|
||||
}
|
||||
|
||||
async history(
|
||||
params: {
|
||||
start?: number;
|
||||
limit?: number;
|
||||
nzoIds?: string[];
|
||||
category?: string;
|
||||
} = {}
|
||||
) {
|
||||
const tParams = {
|
||||
mode: 'history',
|
||||
apikey: this.apiKey,
|
||||
start: params.start ?? 0,
|
||||
limit: params.limit ?? 50,
|
||||
nzo_ids: params.nzoIds ? params.nzoIds.join(',') : undefined,
|
||||
category: params.category,
|
||||
};
|
||||
|
||||
const {
|
||||
data: parsed,
|
||||
statusCode,
|
||||
statusText,
|
||||
headers,
|
||||
} = await this.request(params, HistoryResponseSchema, 60000);
|
||||
const transformed = transformHistoryResponse(parsed);
|
||||
|
||||
if (transformed.status === false || !transformed.history) {
|
||||
throw new DebridError(
|
||||
`Failed to query history: ${transformed.error || 'Unknown error'}`,
|
||||
{
|
||||
statusCode,
|
||||
statusText,
|
||||
code: convertStatusCodeToError(statusCode),
|
||||
headers,
|
||||
body: JSON.stringify(parsed),
|
||||
type: 'api_error',
|
||||
}
|
||||
);
|
||||
}
|
||||
return transformed.history;
|
||||
}
|
||||
|
||||
async waitForHistorySlot(
|
||||
nzoId: string,
|
||||
category: string,
|
||||
@@ -308,40 +354,12 @@ export class SABnzbdApi {
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
|
||||
while (Date.now() < deadline) {
|
||||
const params = {
|
||||
mode: 'history',
|
||||
apikey: this.apiKey,
|
||||
start: '0',
|
||||
limit: '50',
|
||||
nzo_ids: nzoId,
|
||||
const history = await this.history({
|
||||
nzoIds: [nzoId],
|
||||
category,
|
||||
};
|
||||
});
|
||||
|
||||
const {
|
||||
data: parsed,
|
||||
statusCode,
|
||||
statusText,
|
||||
headers,
|
||||
} = await this.request(params, HistoryResponseSchema, 60000);
|
||||
const transformed = transformHistoryResponse(parsed);
|
||||
|
||||
if (transformed.status === false || !transformed.history) {
|
||||
throw new DebridError(
|
||||
`Failed to query history: ${transformed.error || 'Unknown error'}`,
|
||||
{
|
||||
statusCode,
|
||||
statusText,
|
||||
code: convertStatusCodeToError(statusCode),
|
||||
headers,
|
||||
body: JSON.stringify(parsed),
|
||||
type: 'api_error',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const slot = transformed.history.slots.find(
|
||||
(entry) => entry.nzoId === nzoId
|
||||
);
|
||||
const slot = history.slots.find((entry) => entry.nzoId === nzoId);
|
||||
|
||||
if (slot) {
|
||||
if (slot.status === 'completed') {
|
||||
@@ -401,6 +419,9 @@ export abstract class UsenetStreamService implements DebridService {
|
||||
protected static playbackLinkCache = Cache.getInstance<string, string>(
|
||||
'usenet-stream:link'
|
||||
);
|
||||
protected static libraryCache = Cache.getInstance<string, DebridDownload[]>(
|
||||
'usenet-stream:library'
|
||||
);
|
||||
|
||||
readonly supportsUsenet = true;
|
||||
abstract readonly serviceName: ServiceId;
|
||||
@@ -421,8 +442,7 @@ export abstract class UsenetStreamService implements DebridService {
|
||||
* NzbDAV uses the filename parameter, Altmount uses basename of URL
|
||||
*/
|
||||
protected abstract getExpectedFolderName(
|
||||
nzbUrl: string,
|
||||
filename: string
|
||||
nzb: PlaybackInfo & { type: 'usenet' }
|
||||
): string;
|
||||
|
||||
constructor(
|
||||
@@ -566,7 +586,67 @@ export abstract class UsenetStreamService implements DebridService {
|
||||
throw new Error('Unsupported operation');
|
||||
}
|
||||
|
||||
public async checkNzbs(hashes: string[]): Promise<DebridDownload[]> {
|
||||
public async listNzbs(): Promise<DebridDownload[]> {
|
||||
const cacheKey = `${this.serviceName}:${this.config.token}`;
|
||||
|
||||
const { result } = await DistributedLock.getInstance().withLock(
|
||||
`uss:library:${cacheKey}`,
|
||||
async () => {
|
||||
const start = Date.now();
|
||||
const cachedNzbs = await UsenetStreamService.libraryCache.get(cacheKey);
|
||||
if (cachedNzbs) {
|
||||
this.serviceLogger.debug(
|
||||
`Using cached NZB list for ${this.serviceName}`
|
||||
);
|
||||
return cachedNzbs;
|
||||
}
|
||||
|
||||
// const path = `${this.getContentPathPrefix()}/${UsenetStreamService.AIOSTREAMS_CATEGORY}`;
|
||||
// const contents = (await this.webdavClient.getDirectoryContents(
|
||||
// path
|
||||
// )) as FileStat[];
|
||||
// const nzbs = contents.map((item, index) => ({
|
||||
// id: index,
|
||||
// status: 'cached' as const,
|
||||
// hash: item.basename,
|
||||
// size: item.size,
|
||||
// files: [],
|
||||
// }));
|
||||
// this.serviceLogger.debug(`Listed NZBs from WebDAV`, {
|
||||
// count: nzbs.length,
|
||||
// time: getTimeTakenSincePoint(start),
|
||||
// });
|
||||
const history = await this.api.history();
|
||||
const nzbs: DebridDownload[] = history.slots.map((slot, index) => ({
|
||||
id: index,
|
||||
status: 'cached' as const,
|
||||
name: slot.name,
|
||||
}));
|
||||
this.serviceLogger.debug(`Listed NZBs from history`, {
|
||||
count: nzbs.length,
|
||||
time: getTimeTakenSincePoint(start),
|
||||
});
|
||||
await UsenetStreamService.libraryCache.set(
|
||||
cacheKey,
|
||||
nzbs,
|
||||
Env.BUILTIN_DEBRID_LIBRARY_CACHE_TTL,
|
||||
true
|
||||
);
|
||||
|
||||
return nzbs;
|
||||
},
|
||||
{
|
||||
type: 'memory',
|
||||
timeout: 5000,
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async checkNzbs(
|
||||
nzbs: { name?: string; hash?: string }[],
|
||||
checkOwned: boolean = true
|
||||
): Promise<DebridDownload[]> {
|
||||
// if aiostreamsAuth is present, validate it.
|
||||
if (this.auth.aiostreamsAuth) {
|
||||
try {
|
||||
@@ -582,12 +662,29 @@ export abstract class UsenetStreamService implements DebridService {
|
||||
});
|
||||
}
|
||||
}
|
||||
let libraryNzbs: DebridDownload[] = [];
|
||||
|
||||
try {
|
||||
libraryNzbs = checkOwned ? await this.listNzbs() : [];
|
||||
} catch (error) {
|
||||
this.serviceLogger.warn(`Failed to list library NZBs for checkNzbs`, {
|
||||
error: (error as Error).message,
|
||||
});
|
||||
}
|
||||
|
||||
// All NZBs are "cached" since it's streaming-based
|
||||
return hashes.map((h, index) => ({
|
||||
id: index,
|
||||
status: 'cached',
|
||||
hash: h,
|
||||
}));
|
||||
return nzbs.map(({ hash: h, name: n }, index) => {
|
||||
const libraryNzb = libraryNzbs.find(
|
||||
(nzb) => nzb.name === n || nzb.name === h
|
||||
);
|
||||
return {
|
||||
id: index,
|
||||
status: 'cached',
|
||||
library: !!libraryNzb,
|
||||
hash: h,
|
||||
name: n,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public async resolve(
|
||||
@@ -632,7 +729,7 @@ export abstract class UsenetStreamService implements DebridService {
|
||||
});
|
||||
|
||||
const category = metadata?.season || metadata?.episode ? 'Tv' : 'Movies';
|
||||
const expectedFolderName = this.getExpectedFolderName(nzb, filename);
|
||||
const expectedFolderName = this.getExpectedFolderName(playbackInfo);
|
||||
|
||||
// Check if content already exists at the expected path
|
||||
const expectedContentPath = `${this.getContentPathPrefix()}/${category}/${expectedFolderName}`;
|
||||
@@ -676,7 +773,11 @@ export abstract class UsenetStreamService implements DebridService {
|
||||
|
||||
// Only add NZB if content doesn't already exist
|
||||
if (!alreadyExists) {
|
||||
const addResult = await this.api.addUrl(nzb, category, filename);
|
||||
const addResult = await this.api.addUrl(
|
||||
nzb,
|
||||
category,
|
||||
expectedFolderName
|
||||
);
|
||||
nzoId = addResult.nzoId;
|
||||
|
||||
// Poll history until download is complete
|
||||
|
||||
@@ -72,7 +72,7 @@ export interface TorrentWithSelectedFile extends Torrent {
|
||||
service?: {
|
||||
id: BuiltinServiceId;
|
||||
cached: boolean;
|
||||
owned: boolean;
|
||||
library: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ export interface NZBWithSelectedFile extends NZB {
|
||||
service?: {
|
||||
id: BuiltinServiceId;
|
||||
cached: boolean;
|
||||
owned: boolean;
|
||||
library: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -161,6 +161,15 @@ export class NewznabPreset extends BuiltinAddonPreset {
|
||||
default: false,
|
||||
showInSimpleMode: false,
|
||||
},
|
||||
{
|
||||
id: 'checkOwned',
|
||||
name: 'Check Owned NZBs',
|
||||
description:
|
||||
'When searching for NZBs, check if the NZB is already owned (in your library) and mark it as such if so. Note: only applies to nzbDAV/Altmount.',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
showInSimpleMode: false,
|
||||
},
|
||||
{
|
||||
id: 'useMultipleInstances',
|
||||
name: 'Use Multiple Instances',
|
||||
@@ -266,6 +275,7 @@ export class NewznabPreset extends BuiltinAddonPreset {
|
||||
) {
|
||||
const config = {
|
||||
...this.getBaseConfig(userData, services),
|
||||
checkOwned: options.checkOwned ?? true,
|
||||
url: options.newznabUrl,
|
||||
apiPath: options.apiPath,
|
||||
apiKey: options.apiKey,
|
||||
|
||||
@@ -105,6 +105,24 @@ export class NZBHydraPreset extends NewznabPreset {
|
||||
{ label: 'Both', value: 'both' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'paginate',
|
||||
name: 'Paginate Results',
|
||||
description:
|
||||
'Enabling this option will make the addon paginate through all available results to provide a more comprehensive set of results. Enabling this can increase the time taken to return results, some endpoints may not support pagination, and this will also increase the number of requests.',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
showInSimpleMode: false,
|
||||
},
|
||||
{
|
||||
id: 'checkOwned',
|
||||
name: 'Check Owned NZBs',
|
||||
description:
|
||||
'When searching for NZBs, check if the NZB is already owned (in your library) and mark it as such if so. Note: only applies to nzbDAV/Altmount.',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
showInSimpleMode: false,
|
||||
},
|
||||
{
|
||||
id: 'useMultipleInstances',
|
||||
name: 'Use Multiple Instances',
|
||||
|
||||
@@ -1650,6 +1650,10 @@ export const Env = cleanEnv(process.env, {
|
||||
default: 60 * 60, // 1 hour
|
||||
desc: 'Builtin Debrid playback link cache TTL',
|
||||
}),
|
||||
BUILTIN_DEBRID_LIBRARY_CACHE_TTL: num({
|
||||
default: 60 * 5, // 5 minutes
|
||||
desc: 'Builtin Debrid NZB list cache TTL',
|
||||
}),
|
||||
BUILTIN_DEBRID_METADATA_STORE: str({
|
||||
choices: ['redis', 'sql', 'memory'],
|
||||
default: undefined,
|
||||
|
||||
Reference in New Issue
Block a user