Floatplane channel plugin for Jellyfin 10.10.x: browse, HLS proxy, cookie session, CI release
Some checks failed
release / build-release (push) Has been cancelled
Some checks failed
release / build-release (push) Has been cancelled
- IChannel: creators -> sub-channels -> video posts (public listing API, search mapped) - IRequiresMediaInfoCallback -> signed proxy URLs; stream controller rewrites HLS so the AES-128 key is fetched server-side with the session cookie, segments stay on the CDN - delivery/info cached per token lifetime and throttled (~2/min) - config page: paste sails.sid + validate, extra creators, quality, search test - scripts/package.sh -> plugin folder + zip + md5 + manifest.json; Gitea release workflow - research docs, HAR analysis (HAR itself gitignored), install-pi5.sh Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
61
doc/research/reference-sources/fpdl_Caches.ts
Normal file
61
doc/research/reference-sources/fpdl_Caches.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type { JSONSafeValue } from "@inrixia/db";
|
||||
import { writeFile } from "fs/promises";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type FetchItem<T> = (id: string, options?: any) => Promise<T>;
|
||||
|
||||
export class ItemCache<T extends JSONSafeValue> {
|
||||
private cache: Record<string, { i: T; t: number }>;
|
||||
private cachePath: string;
|
||||
private fetchItem: FetchItem<T>;
|
||||
private expireAgeMs: number;
|
||||
|
||||
constructor(cachePath: string, fetchItem: FetchItem<T>, expireAgeMins: number = 24 * 60) {
|
||||
this.cachePath = cachePath;
|
||||
this.fetchItem = fetchItem;
|
||||
this.expireAgeMs = expireAgeMins * 60 * 1000;
|
||||
|
||||
try {
|
||||
this.cache = JSON.parse(readFileSync(this.cachePath).toString());
|
||||
} catch {
|
||||
this.cache = {};
|
||||
}
|
||||
}
|
||||
|
||||
private async writeOut() {
|
||||
try {
|
||||
await writeFile(this.cachePath, JSON.stringify(this.cache));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private async set(key: string, i: T) {
|
||||
this.cache[key] = { t: Date.now(), i };
|
||||
await this.writeOut();
|
||||
return i;
|
||||
}
|
||||
|
||||
private deepCopy(i: T): T {
|
||||
return JSON.parse(JSON.stringify(i));
|
||||
}
|
||||
|
||||
public async get(id: string, options?: unknown, noCache: boolean = false): Promise<T> {
|
||||
const key = options !== undefined ? JSON.stringify([id, options]) : id;
|
||||
if (noCache) {
|
||||
delete this.cache[key];
|
||||
return this.get(id, options);
|
||||
}
|
||||
const cacheItem = this.cache[key];
|
||||
if (cacheItem !== undefined) {
|
||||
// Remove expired entries older than expireAge
|
||||
if (Date.now() - cacheItem.t > this.expireAgeMs) {
|
||||
delete this.cache[key];
|
||||
return this.get(id, options);
|
||||
}
|
||||
return this.deepCopy(cacheItem.i);
|
||||
}
|
||||
return this.set(key, await this.fetchItem(id, options));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user