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:
109
doc/research/reference-sources/fpdl_float.ts
Normal file
109
doc/research/reference-sources/fpdl_float.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { defaultSettings } from "./lib/defaults";
|
||||
import { fetchFFMPEG } from "./lib/helpers/fetchFFMPEG";
|
||||
import { args, DownloaderVersion, fApi, settings } from "./lib/helpers/index";
|
||||
import { initProm } from "./lib/prometheus";
|
||||
import { quickStart, validatePlexSettings } from "./quickStart";
|
||||
|
||||
import chalk from "chalk-template";
|
||||
import { loginFloatplane, User } from "./logins";
|
||||
|
||||
import { fetchSubscriptions } from "./subscriptionFetching";
|
||||
|
||||
import semver from "semver";
|
||||
const { gt, diff } = semver;
|
||||
|
||||
import type { ContentPost } from "floatplane/content";
|
||||
import { Self } from "floatplane/user";
|
||||
|
||||
/**
|
||||
* Main function that triggeres everything else in the script
|
||||
*/
|
||||
const downloadNewVideos = async () => {
|
||||
const inProgress = [];
|
||||
|
||||
// Fetch content posts from seek and destroy guids
|
||||
const contentPosts: Promise<ContentPost>[] = [];
|
||||
while (settings.floatplane.seekAndDestroy.length > 0) {
|
||||
const guid = settings.floatplane.seekAndDestroy.pop();
|
||||
if (guid === undefined) continue;
|
||||
console.log(chalk`Seek and Destroy: {red ${guid}}`);
|
||||
contentPosts.push(fApi.content.post(guid));
|
||||
}
|
||||
|
||||
for await (const subscription of fetchSubscriptions()) {
|
||||
// Seek and destroy posts if the content post belongs to this subscription
|
||||
for await (const contentPost of contentPosts) {
|
||||
if (contentPost.creator.id === subscription.creatorId) {
|
||||
for await (const video of subscription.seekAndDestroy(contentPost)) {
|
||||
inProgress.push(video.download());
|
||||
}
|
||||
}
|
||||
}
|
||||
await subscription.deleteOldVideos();
|
||||
for await (const video of subscription.fetchNewVideos()) inProgress.push(video.download());
|
||||
}
|
||||
|
||||
console.log(chalk`Queued {green ${inProgress.length}} videos...`);
|
||||
await Promise.all(inProgress);
|
||||
|
||||
// Enforce search limits after searching once.
|
||||
settings.floatplane.videosToSearch = defaultSettings.floatplane.videosToSearch;
|
||||
|
||||
if (settings.floatplane.waitForNewVideos === true) {
|
||||
console.log(`Checking for new videos in 5 minutes...`);
|
||||
setTimeout(downloadNewVideos, 5 * 60 * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
// Fix for docker
|
||||
process.on("SIGTERM", () => process.exit(143));
|
||||
|
||||
(async () => {
|
||||
if (!args.headless) {
|
||||
console.log(chalk`\n{red ///}{grey ===} {cyan Console} {grey ===}{red \\\\\\}`);
|
||||
}
|
||||
|
||||
if (args.updateCheck) {
|
||||
const latest = await fApi
|
||||
.got("https://raw.githubusercontent.com/Inrixia/Floatplane-Downloader/master/package.json")
|
||||
.json<{ version: string }>()
|
||||
.catch(() => ({ version: DownloaderVersion }));
|
||||
|
||||
if (gt(latest.version, DownloaderVersion))
|
||||
console.log(
|
||||
chalk`There is a ${diff(latest.version, DownloaderVersion)} update available! ${DownloaderVersion} > ${
|
||||
latest.version
|
||||
}.\nHead to {cyanBright https://github.com/Inrixia/Floatplane-Downloader/releases} to update!\n`
|
||||
);
|
||||
}
|
||||
|
||||
if (args.sanityCheck) {
|
||||
console.log("Sanity check passed!");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Earlybird functions, these are run before script start and not run again if script repeating is enabled.
|
||||
await fetchFFMPEG();
|
||||
if (settings.runQuickstartPrompts) {
|
||||
if (args.headless) console.log("headless is set to true! Skipping quickstart prompts.");
|
||||
else await quickStart();
|
||||
}
|
||||
settings.runQuickstartPrompts = false;
|
||||
|
||||
// Get Plex details if not saved
|
||||
await validatePlexSettings();
|
||||
|
||||
// Get Floatplane credentials if not saved
|
||||
let user: Self | User;
|
||||
try {
|
||||
user = await fApi.user.self();
|
||||
} catch (err) {
|
||||
console.log(`Unable to authenticate with floatplane... ${(<Error>err).message}\nAttempting to re-authenticate...`);
|
||||
user = await loginFloatplane();
|
||||
}
|
||||
await initProm(user!.id);
|
||||
|
||||
console.log(chalk`Initalized! Running version {cyan ${DownloaderVersion}} instance {magenta ${user!.id}}`);
|
||||
|
||||
await downloadNewVideos();
|
||||
})();
|
||||
Reference in New Issue
Block a user