webstreamr-github/src/source/Source.ts

61 lines
1.8 KiB
TypeScript

// eslint-disable-next-line import/no-named-as-default
import KeyvSqlite from '@keyv/sqlite';
import { Cacheable, CacheableMemory, Keyv } from 'cacheable';
import { ContentType } from 'stremio-addon-sdk';
import { NotFoundError } from '../error';
import { Context, CountryCode } from '../types';
import { getCacheDir, Id } from '../utils';
export interface SourceResult {
countryCode: CountryCode;
title?: string;
url: URL;
}
export abstract class Source {
public abstract readonly id: string;
public abstract readonly label: string;
public readonly ttl: number = 43200000; // 12h
public abstract readonly contentTypes: ContentType[];
public abstract readonly countryCodes: CountryCode[];
public abstract readonly baseUrl: string;
private readonly sourceResultCache: Cacheable;
public constructor() {
this.sourceResultCache = new Cacheable({
primary: new Keyv({ store: new CacheableMemory({ lruSize: 1024, ttl: this.ttl }) }),
secondary: new Keyv(new KeyvSqlite(`sqlite://${getCacheDir()}/webstreamr-source-cache.sqlite`)),
});
}
protected abstract handleInternal(ctx: Context, type: ContentType, id: Id): Promise<(SourceResult[])>;
public async handle(ctx: Context, type: ContentType, id: Id): Promise<(SourceResult[])> {
const cacheKey = `${this.id}_${id.toString()}`;
let sourceResults = await this.sourceResultCache.get<SourceResult[]>(cacheKey);
if (sourceResults) {
return sourceResults.map(sourceResult => ({ ...sourceResult, url: new URL(sourceResult.url) }));
}
try {
sourceResults = await this.handleInternal(ctx, type, id);
} catch (error) {
if (error instanceof NotFoundError) {
sourceResults = [];
} else {
throw error;
}
}
await this.sourceResultCache.set<SourceResult[]>(cacheKey, sourceResults);
return sourceResults;
}
}