mirror of
https://github.com/knightcrawler-stremio/knightcrawler.git
synced 2024-12-20 03:29:51 +00:00
rework removing providers filters, and clean up project a little
Also wraps in pm2, and introduces linting, and dev watch
This commit is contained in:
1
src/node/addon/.eslintignore
Normal file
1
src/node/addon/.eslintignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.ts
|
||||||
39
src/node/addon/.eslintrc.cjs
Normal file
39
src/node/addon/.eslintrc.cjs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/** @type {import("eslint").ESLint.Options} */
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
es2024: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
Atomics: "readonly",
|
||||||
|
SharedArrayBuffer: "readonly",
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
plugins: ["import", "import-helpers"],
|
||||||
|
rules: {
|
||||||
|
"default-case": "off",
|
||||||
|
"import/no-duplicates": "off",
|
||||||
|
"import/no-extraneous-dependencies": ["off", { devDependencies: ["backend", "frontend", "mobile"] }],
|
||||||
|
"import/order": "off",
|
||||||
|
"import-helpers/order-imports": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
alphabetize: {
|
||||||
|
order: "asc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"lines-between-class-members": ["error", "always", { exceptAfterSingleLine: true }],
|
||||||
|
"no-continue": "off",
|
||||||
|
"no-param-reassign": "off",
|
||||||
|
"no-plusplus": ["error", { allowForLoopAfterthoughts: true }],
|
||||||
|
"no-restricted-syntax": "off",
|
||||||
|
"no-unused-expressions": ["off", { allowShortCircuit: true }],
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"no-use-before-define": "off",
|
||||||
|
"one-var": ["error", { uninitialized: "consecutive" }],
|
||||||
|
"prefer-destructuring": "warn",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,17 +1,29 @@
|
|||||||
FROM node:16-alpine
|
# --- Build Stage ---
|
||||||
|
FROM node:lts-alpine AS builder
|
||||||
|
|
||||||
RUN apk update && apk upgrade && \
|
RUN apk update && apk upgrade && \
|
||||||
apk add --no-cache nodejs npm git curl && \
|
apk add --no-cache git
|
||||||
rm -fr /var/cache/apk/*
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package*.json .
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
RUN npm ci --only-production
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# --- Runtime Stage ---
|
||||||
|
FROM node:lts-alpine
|
||||||
|
|
||||||
|
# Install pm2
|
||||||
|
RUN npm install pm2 -g
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
COPY --from=builder /app ./
|
||||||
|
RUN npm prune --omit=dev
|
||||||
|
|
||||||
EXPOSE 7000
|
EXPOSE 7000
|
||||||
|
|
||||||
CMD ["/usr/bin/node", "--insecure-http-parser", "/app/index.js" ]
|
ENTRYPOINT [ "pm2-runtime", "start", "ecosystem.config.cjs"]
|
||||||
14
src/node/addon/ecosystem.config.cjs
Normal file
14
src/node/addon/ecosystem.config.cjs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
module.exports = {
|
||||||
|
apps: [
|
||||||
|
{
|
||||||
|
name: "torrentio-selfhostio",
|
||||||
|
script: "npm start",
|
||||||
|
cwd: "/app",
|
||||||
|
watch: ["./dist/index.cjs"],
|
||||||
|
autorestart: true,
|
||||||
|
env: {
|
||||||
|
...process.env
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
68
src/node/addon/esbuild.js
Normal file
68
src/node/addon/esbuild.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { build } from "esbuild";
|
||||||
|
import { copy } from 'esbuild-plugin-copy';
|
||||||
|
import { readFileSync, rmSync } from "fs";
|
||||||
|
|
||||||
|
const { devDependencies } = JSON.parse(readFileSync("./package.json", "utf8"));
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const outdir = "dist";
|
||||||
|
|
||||||
|
rmSync(outdir, { recursive: true, force: true });
|
||||||
|
|
||||||
|
build({
|
||||||
|
bundle: true,
|
||||||
|
entryPoints: [
|
||||||
|
"./src/index.js",
|
||||||
|
// "./src/**/*.css",
|
||||||
|
// "./src/**/*.hbs",
|
||||||
|
// "./src/**/*.html"
|
||||||
|
],
|
||||||
|
external: [...(devDependencies && Object.keys(devDependencies))],
|
||||||
|
keepNames: true,
|
||||||
|
loader: {
|
||||||
|
".css": "copy",
|
||||||
|
".hbs": "copy",
|
||||||
|
".html": "copy",
|
||||||
|
},
|
||||||
|
minify: true,
|
||||||
|
outbase: "./src",
|
||||||
|
outdir,
|
||||||
|
outExtension: {
|
||||||
|
".js": ".cjs",
|
||||||
|
},
|
||||||
|
platform: "node",
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: "populate-import-meta",
|
||||||
|
setup: ({ onLoad }) => {
|
||||||
|
onLoad({ filter: new RegExp(`${import.meta.dirname}/src/.*.(js|ts)$`) }, args => {
|
||||||
|
const contents = readFileSync(args.path, "utf8");
|
||||||
|
|
||||||
|
const transformedContents = contents
|
||||||
|
.replace(/import\.meta/g, `{dirname:__dirname,filename:__filename}`)
|
||||||
|
.replace(/import\.meta\.filename/g, "__filename")
|
||||||
|
.replace(/import\.meta\.dirname/g, "__dirname");
|
||||||
|
|
||||||
|
return { contents: transformedContents, loader: "default" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
copy({
|
||||||
|
assets: [
|
||||||
|
{
|
||||||
|
from: ['./static/**'],
|
||||||
|
to: ['./static'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
],
|
||||||
|
}).then(() => {
|
||||||
|
// biome-ignore lint/style/useTemplate: <explanation>
|
||||||
|
console.log("⚡ " + "\x1b[32m" + `Done in ${Date.now() - start}ms`);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
21
src/node/addon/jsconfig.json
Normal file
21
src/node/addon/jsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./src",
|
||||||
|
"checkJs": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"lib": ["es6"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"pretty": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"rootDir": "./src",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "ES6",
|
||||||
|
"types": ["node"],
|
||||||
|
"typeRoots": ["node_modules/@types", "src/@types"]
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import { DebridOptions } from '../moch/options.js';
|
|
||||||
import { QualityFilter, Providers, SizeFilter } from './filter.js';
|
|
||||||
import { LanguageOptions } from './languages.js';
|
|
||||||
|
|
||||||
const keysToSplit = [Providers.key, LanguageOptions.key, QualityFilter.key, SizeFilter.key, DebridOptions.key];
|
|
||||||
const keysToUppercase = [SizeFilter.key];
|
|
||||||
|
|
||||||
export function parseConfiguration(configuration) {
|
|
||||||
if (!configuration) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const configValues = configuration.split('|')
|
|
||||||
.reduce((map, next) => {
|
|
||||||
const parameterParts = next.split('=');
|
|
||||||
if (parameterParts.length === 2) {
|
|
||||||
map[parameterParts[0].toLowerCase()] = parameterParts[1];
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}, {});
|
|
||||||
keysToSplit
|
|
||||||
.filter(key => configValues[key])
|
|
||||||
.forEach(key => configValues[key] = configValues[key].split(',')
|
|
||||||
.map(value => keysToUppercase.includes(key) ? value.toUpperCase() : value.toLowerCase()))
|
|
||||||
return configValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
function configValue(config) {
|
|
||||||
return Object.entries(config)
|
|
||||||
.map(([key, value]) => `${key}=${Array.isArray(value) ? value.join(',') : value}`)
|
|
||||||
.join('|');
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import requestIp from 'request-ip';
|
|
||||||
import ip from 'ip';
|
|
||||||
|
|
||||||
const filePath = path.join(process.cwd(), 'allowed_ips.json');
|
|
||||||
|
|
||||||
let ALLOWED_ADDRESSES = [];
|
|
||||||
let ALLOWED_SUBNETS = [];
|
|
||||||
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
const allowedAddresses = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
||||||
|
|
||||||
allowedAddresses.forEach(address => {
|
|
||||||
if (address.indexOf('/') === -1) {
|
|
||||||
ALLOWED_ADDRESSES.push(address);
|
|
||||||
} else {
|
|
||||||
ALLOWED_SUBNETS.push(address);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const IpIsAllowed = function(ipAddress) {
|
|
||||||
if (ALLOWED_ADDRESSES.indexOf(ipAddress) > -1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < ALLOWED_SUBNETS.length; i++) {
|
|
||||||
if (ip.cidrSubnet(ALLOWED_SUBNETS[i]).contains(ipAddress)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ipFilter = function (req, res, next) {
|
|
||||||
const ipAddress = requestIp.getClientIp(req);
|
|
||||||
|
|
||||||
if (ALLOWED_ADDRESSES.length === 0 && ALLOWED_SUBNETS.length === 0) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IpIsAllowed(ipAddress)) {
|
|
||||||
return next();
|
|
||||||
} else {
|
|
||||||
console.log(`IP ${ipAddress} is not allowed`);
|
|
||||||
res.status(404).send(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
3920
src/node/addon/package-lock.json
generated
3920
src/node/addon/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "selfhostio-selfhostio",
|
"name": "selfhostio-addon",
|
||||||
"version": "1.0.0",
|
"version": "0.0.1",
|
||||||
"exports": "./index.js",
|
"exports": "./index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js"
|
"build": "node esbuild.js",
|
||||||
|
"dev": "tsx watch --ignore node_modules src/index.js",
|
||||||
|
"start": "node dist/index.cjs",
|
||||||
|
"lint": "eslint . --ext .ts,.js"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@putdotio/api-client": "^8.42.0",
|
"@putdotio/api-client": "^8.42.0",
|
||||||
"all-debrid-api": "^1.1.0",
|
"all-debrid-api": "^1.1.0",
|
||||||
@@ -35,5 +37,15 @@
|
|||||||
"swagger-stats": "^0.99.7",
|
"swagger-stats": "^0.99.7",
|
||||||
"ua-parser-js": "^1.0.36",
|
"ua-parser-js": "^1.0.36",
|
||||||
"user-agents": "^1.0.1444"
|
"user-agents": "^1.0.1444"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.11.6",
|
||||||
|
"@types/stremio-addon-sdk": "^1.6.10",
|
||||||
|
"esbuild": "^0.19.12",
|
||||||
|
"esbuild-plugin-copy": "^2.1.1",
|
||||||
|
"eslint": "^8.56.0",
|
||||||
|
"eslint-plugin-import": "^2.29.1",
|
||||||
|
"eslint-plugin-import-helpers": "^1.3.1",
|
||||||
|
"tsx": "^4.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import Bottleneck from 'bottleneck';
|
import Bottleneck from 'bottleneck';
|
||||||
import { addonBuilder } from 'stremio-addon-sdk';
|
import { addonBuilder } from 'stremio-addon-sdk';
|
||||||
import { Type } from './lib/types.js';
|
|
||||||
import { dummyManifest } from './lib/manifest.js';
|
|
||||||
import { cacheWrapStream } from './lib/cache.js';
|
import { cacheWrapStream } from './lib/cache.js';
|
||||||
import { toStreamInfo, applyStaticInfo } from './lib/streamInfo.js';
|
import { dummyManifest } from './lib/manifest.js';
|
||||||
import * as repository from './lib/repository.js';
|
import * as repository from './lib/repository.js';
|
||||||
import applySorting from './lib/sort.js';
|
import applySorting from './lib/sort.js';
|
||||||
|
import { toStreamInfo, applyStaticInfo } from './lib/streamInfo.js';
|
||||||
|
import { Type } from './lib/types.js';
|
||||||
import { applyMochs, getMochCatalog, getMochItemMeta } from './moch/moch.js';
|
import { applyMochs, getMochCatalog, getMochItemMeta } from './moch/moch.js';
|
||||||
import StaticLinks from './moch/static.js';
|
import StaticLinks from './moch/static.js';
|
||||||
|
|
||||||
@@ -79,27 +79,24 @@ async function streamHandler(args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function seriesRecordsHandler(args) {
|
async function seriesRecordsHandler(args) {
|
||||||
if (args.id.match(/^tt\d+:\d+:\d+$/)) {
|
if (args.id.match(/^tt\d+:\d+:\d+$/)) {
|
||||||
const parts = args.id.split(':');
|
const [imdbId, season = "1", episode = "1"] = args.id.split(':');
|
||||||
const imdbId = parts[0];
|
const parsedSeason = parseInt(season, 10);
|
||||||
const season = parts[1] !== undefined ? parseInt(parts[1], 10) : 1;
|
const parsedEpisode = parseInt(episode, 10);
|
||||||
const episode = parts[2] !== undefined ? parseInt(parts[2], 10) : 1;
|
return repository.getImdbIdSeriesEntries(imdbId, parsedSeason, parsedEpisode);
|
||||||
return repository.getImdbIdSeriesEntries(imdbId, season, episode);
|
} else if (args.id.match(/^kitsu:\d+(?::\d+)?$/i)) {
|
||||||
} else if (args.id.match(/^kitsu:\d+(?::\d+)?$/i)) {
|
const [, kitsuId, episodePart] = args.id.split(':');
|
||||||
const parts = args.id.split(':');
|
const episode = episodePart !== undefined ? parseInt(episodePart, 10) : undefined;
|
||||||
const kitsuId = parts[1];
|
return episode !== undefined
|
||||||
const episode = parts[2] !== undefined ? parseInt(parts[2], 10) : undefined;
|
? repository.getKitsuIdSeriesEntries(kitsuId, episode)
|
||||||
return episode !== undefined
|
: repository.getKitsuIdMovieEntries(kitsuId);
|
||||||
? repository.getKitsuIdSeriesEntries(kitsuId, episode)
|
}
|
||||||
: repository.getKitsuIdMovieEntries(kitsuId);
|
return Promise.resolve([]);
|
||||||
}
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function movieRecordsHandler(args) {
|
async function movieRecordsHandler(args) {
|
||||||
if (args.id.match(/^tt\d+$/)) {
|
if (args.id.match(/^tt\d+$/)) {
|
||||||
const parts = args.id.split(':');
|
const [imdbId] = args.id.split(':');
|
||||||
const imdbId = parts[0];
|
|
||||||
console.log("imdbId", imdbId);
|
console.log("imdbId", imdbId);
|
||||||
return repository.getImdbIdMovieEntries(imdbId);
|
return repository.getImdbIdMovieEntries(imdbId);
|
||||||
} else if (args.id.match(/^kitsu:\d+(?::\d+)?$/i)) {
|
} else if (args.id.match(/^kitsu:\d+(?::\d+)?$/i)) {
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import serverless from './serverless.js';
|
|
||||||
import { initBestTrackers } from './lib/magnetHelper.js';
|
import { initBestTrackers } from './lib/magnetHelper.js';
|
||||||
import {ipFilter} from "./lib/ipFilter.js";
|
import serverless from './serverless.js';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.enable('trust proxy');
|
app.enable('trust proxy');
|
||||||
app.use(ipFilter);
|
|
||||||
app.use(express.static('static', { maxAge: '1y' }));
|
app.use(express.static('static', { maxAge: '1y' }));
|
||||||
app.use((req, res, next) => serverless(req, res, next));
|
app.use((req, res, next) => serverless(req, res, next));
|
||||||
app.listen(process.env.PORT || 7000, () => {
|
app.listen(process.env.PORT || 7000, () => {
|
||||||
28
src/node/addon/src/lib/configuration.js
Normal file
28
src/node/addon/src/lib/configuration.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { DebridOptions } from '../moch/options.js';
|
||||||
|
import { QualityFilter, SizeFilter } from './filter.js';
|
||||||
|
import { LanguageOptions } from './languages.js';
|
||||||
|
|
||||||
|
const keysToSplit = [LanguageOptions.key, QualityFilter.key, SizeFilter.key, DebridOptions.key];
|
||||||
|
const keysToUppercase = [SizeFilter.key];
|
||||||
|
|
||||||
|
export function parseConfiguration(configuration) {
|
||||||
|
if (!configuration) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configValues = configuration.split('|')
|
||||||
|
.reduce((map, next) => {
|
||||||
|
const [key, value] = next.split('=');
|
||||||
|
if (key && value) {
|
||||||
|
map[key.toLowerCase()] = value;
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
keysToSplit
|
||||||
|
.filter(key => configValues[key])
|
||||||
|
.forEach(key => configValues[key] = configValues[key].split(',')
|
||||||
|
.map(value => keysToUppercase.includes(key) ? value.toUpperCase() : value.toLowerCase()))
|
||||||
|
|
||||||
|
return configValues;
|
||||||
|
}
|
||||||
72
src/node/addon/src/lib/extension.js
Normal file
72
src/node/addon/src/lib/extension.js
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
const VIDEO_EXTENSIONS = [
|
||||||
|
"3g2",
|
||||||
|
"3gp",
|
||||||
|
"avi",
|
||||||
|
"flv",
|
||||||
|
"mkv",
|
||||||
|
"mk3d",
|
||||||
|
"mov",
|
||||||
|
"mp2",
|
||||||
|
"mp4",
|
||||||
|
"m4v",
|
||||||
|
"mpe",
|
||||||
|
"mpeg",
|
||||||
|
"mpg",
|
||||||
|
"mpv",
|
||||||
|
"webm",
|
||||||
|
"wmv",
|
||||||
|
"ogm",
|
||||||
|
"ts",
|
||||||
|
"m2ts"
|
||||||
|
];
|
||||||
|
const SUBTITLE_EXTENSIONS = [
|
||||||
|
"aqt",
|
||||||
|
"gsub",
|
||||||
|
"jss",
|
||||||
|
"sub",
|
||||||
|
"ttxt",
|
||||||
|
"pjs",
|
||||||
|
"psb",
|
||||||
|
"rt",
|
||||||
|
"smi",
|
||||||
|
"slt",
|
||||||
|
"ssf",
|
||||||
|
"srt",
|
||||||
|
"ssa",
|
||||||
|
"ass",
|
||||||
|
"usf",
|
||||||
|
"idx",
|
||||||
|
"vtt"
|
||||||
|
];
|
||||||
|
const DISK_EXTENSIONS = [
|
||||||
|
"iso",
|
||||||
|
"m2ts",
|
||||||
|
"ts",
|
||||||
|
"vob"
|
||||||
|
]
|
||||||
|
|
||||||
|
const ARCHIVE_EXTENSIONS = [
|
||||||
|
"rar",
|
||||||
|
"zip"
|
||||||
|
]
|
||||||
|
|
||||||
|
export function isVideo(filename) {
|
||||||
|
return isExtension(filename, VIDEO_EXTENSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSubtitle(filename) {
|
||||||
|
return isExtension(filename, SUBTITLE_EXTENSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDisk(filename) {
|
||||||
|
return isExtension(filename, DISK_EXTENSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isArchive(filename) {
|
||||||
|
return isExtension(filename, ARCHIVE_EXTENSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isExtension(filename, extensions) {
|
||||||
|
const extensionMatch = filename?.match(/\.(\w{2,4})$/);
|
||||||
|
return extensionMatch && extensions.includes(extensionMatch[1].toLowerCase());
|
||||||
|
}
|
||||||
@@ -1,30 +1,5 @@
|
|||||||
import { extractProvider, parseSize, extractSize } from './titleHelper.js';
|
import { parseSize, extractSize } from './titleHelper.js';
|
||||||
import { Type } from './types.js';
|
import { Type } from './types.js';
|
||||||
export const Providers = {
|
|
||||||
key: 'providers',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
key: 'YTS',
|
|
||||||
label: 'YTS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'EZTV',
|
|
||||||
label: 'EZTV'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'DMM',
|
|
||||||
label: 'DMM'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'TPB',
|
|
||||||
label: 'TPB'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'TorrentGalaxy',
|
|
||||||
label: 'TorrentGalaxy'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
export const QualityFilter = {
|
export const QualityFilter = {
|
||||||
key: 'qualityfilter',
|
key: 'qualityfilter',
|
||||||
options: [
|
options: [
|
||||||
@@ -121,27 +96,14 @@ export const QualityFilter = {
|
|||||||
export const SizeFilter = {
|
export const SizeFilter = {
|
||||||
key: 'sizefilter'
|
key: 'sizefilter'
|
||||||
}
|
}
|
||||||
const defaultProviderKeys = Providers.options.map(provider => provider.key);
|
|
||||||
|
|
||||||
export default function applyFilters(streams, config) {
|
export default function applyFilters(streams, config) {
|
||||||
return [
|
return [
|
||||||
filterByProvider,
|
|
||||||
filterByQuality,
|
filterByQuality,
|
||||||
filterBySize
|
filterBySize
|
||||||
].reduce((filteredStreams, filter) => filter(filteredStreams, config), streams);
|
].reduce((filteredStreams, filter) => filter(filteredStreams, config), streams);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterByProvider(streams, config) {
|
|
||||||
const providers = config.providers || defaultProviderKeys;
|
|
||||||
if (!providers?.length) {
|
|
||||||
return streams;
|
|
||||||
}
|
|
||||||
return streams.filter(stream => {
|
|
||||||
const provider = extractProvider(stream.title)
|
|
||||||
return providers.includes(provider);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterByQuality(streams, config) {
|
function filterByQuality(streams, config) {
|
||||||
const filters = config[QualityFilter.key];
|
const filters = config[QualityFilter.key];
|
||||||
if (!filters) {
|
if (!filters) {
|
||||||
@@ -149,7 +111,7 @@ function filterByQuality(streams, config) {
|
|||||||
}
|
}
|
||||||
const filterOptions = QualityFilter.options.filter(option => filters.includes(option.key));
|
const filterOptions = QualityFilter.options.filter(option => filters.includes(option.key));
|
||||||
return streams.filter(stream => {
|
return streams.filter(stream => {
|
||||||
const streamQuality = stream.name.split('\n')[1];
|
const [ , streamQuality] = stream.name.split('\n');
|
||||||
const bingeGroup = stream.behaviorHints?.bingeGroup;
|
const bingeGroup = stream.behaviorHints?.bingeGroup;
|
||||||
return !filterOptions.some(option => option.test(streamQuality, bingeGroup));
|
return !filterOptions.some(option => option.test(streamQuality, bingeGroup));
|
||||||
});
|
});
|
||||||
@@ -184,14 +184,13 @@ a.install-link {
|
|||||||
box-shadow: 0 0 0 2pt rgb(30, 144, 255, 0.7);
|
box-shadow: 0 0 0 2pt rgb(30, 144, 255, 0.7);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
import { Providers, QualityFilter, SizeFilter } from './filter.js';
|
|
||||||
import { SortOptions } from './sort.js';
|
|
||||||
import { LanguageOptions } from './languages.js';
|
|
||||||
import { DebridOptions } from '../moch/options.js';
|
|
||||||
import { MochOptions } from '../moch/moch.js';
|
import { MochOptions } from '../moch/moch.js';
|
||||||
|
import { DebridOptions } from '../moch/options.js';
|
||||||
|
import { QualityFilter, SizeFilter } from './filter.js';
|
||||||
|
import { LanguageOptions } from './languages.js';
|
||||||
|
import { SortOptions } from './sort.js';
|
||||||
|
|
||||||
export default function landingTemplate(manifest, config = {}) {
|
export default function landingTemplate(manifest, config = {}) {
|
||||||
const providers = config[Providers.key] || Providers.options.map(provider => provider.key);
|
|
||||||
const sort = config[SortOptions.key] || SortOptions.options.qualitySeeders.key;
|
const sort = config[SortOptions.key] || SortOptions.options.qualitySeeders.key;
|
||||||
const languages = config[LanguageOptions.key] || [];
|
const languages = config[LanguageOptions.key] || [];
|
||||||
const qualityFilters = config[QualityFilter.key] || [];
|
const qualityFilters = config[QualityFilter.key] || [];
|
||||||
@@ -211,9 +210,7 @@ export default function landingTemplate(manifest, config = {}) {
|
|||||||
|
|
||||||
const background = manifest.background || 'https://dl.strem.io/addon-background.jpg';
|
const background = manifest.background || 'https://dl.strem.io/addon-background.jpg';
|
||||||
const logo = manifest.logo || 'https://dl.strem.io/addon-logo.png';
|
const logo = manifest.logo || 'https://dl.strem.io/addon-logo.png';
|
||||||
const providersHTML = Providers.options
|
|
||||||
.map(provider => `<option value="${provider.key}">${provider.foreign ? provider.foreign + ' ' : ''}${provider.label}</option>`)
|
|
||||||
.join('\n');
|
|
||||||
const sortOptionsHTML = Object.values(SortOptions.options)
|
const sortOptionsHTML = Object.values(SortOptions.options)
|
||||||
.map((option, i) => `<option value="${option.key}" ${i === 0 ? 'selected' : ''}>${option.description}</option>`)
|
.map((option, i) => `<option value="${option.key}" ${i === 0 ? 'selected' : ''}>${option.description}</option>`)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
@@ -268,11 +265,6 @@ export default function landingTemplate(manifest, config = {}) {
|
|||||||
|
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
|
|
||||||
<label class="label" for="iProviders">Providers:</label>
|
|
||||||
<select id="iProviders" class="input" onchange="generateInstallLink()" name="providers[]" multiple="multiple">
|
|
||||||
${providersHTML}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<label class="label" for="iSort">Sorting:</label>
|
<label class="label" for="iSort">Sorting:</label>
|
||||||
<select id="iSort" class="input" onchange="sortModeChange()">
|
<select id="iSort" class="input" onchange="sortModeChange()">
|
||||||
${sortOptionsHTML}
|
${sortOptionsHTML}
|
||||||
@@ -356,12 +348,6 @@ export default function landingTemplate(manifest, config = {}) {
|
|||||||
const isTvAgent = /\\b(?:tv|wv)\\b/i.test(navigator.userAgent)
|
const isTvAgent = /\\b(?:tv|wv)\\b/i.test(navigator.userAgent)
|
||||||
const isDesktopMedia = window.matchMedia("(pointer:fine)").matches;
|
const isDesktopMedia = window.matchMedia("(pointer:fine)").matches;
|
||||||
if (isDesktopMedia && !isTvMedia && !isTvAgent) {
|
if (isDesktopMedia && !isTvMedia && !isTvAgent) {
|
||||||
$('#iProviders').multiselect({
|
|
||||||
nonSelectedText: 'All providers',
|
|
||||||
buttonTextAlignment: 'left',
|
|
||||||
onChange: () => generateInstallLink()
|
|
||||||
});
|
|
||||||
$('#iProviders').multiselect('select', [${providers.map(provider => '"' + provider + '"')}]);
|
|
||||||
$('#iLanguages').multiselect({
|
$('#iLanguages').multiselect({
|
||||||
nonSelectedText: 'None',
|
nonSelectedText: 'None',
|
||||||
buttonTextAlignment: 'left',
|
buttonTextAlignment: 'left',
|
||||||
@@ -381,7 +367,6 @@ export default function landingTemplate(manifest, config = {}) {
|
|||||||
});
|
});
|
||||||
$('#iDebridOptions').multiselect('select', [${debridOptions.map(option => '"' + option + '"')}]);
|
$('#iDebridOptions').multiselect('select', [${debridOptions.map(option => '"' + option + '"')}]);
|
||||||
} else {
|
} else {
|
||||||
$('#iProviders').val([${providers.map(provider => '"' + provider + '"')}]);
|
|
||||||
$('#iLanguages').val([${languages.map(language => '"' + language + '"')}]);
|
$('#iLanguages').val([${languages.map(language => '"' + language + '"')}]);
|
||||||
$('#iQualityFilter').val([${qualityFilters.map(filter => '"' + filter + '"')}]);
|
$('#iQualityFilter').val([${qualityFilters.map(filter => '"' + filter + '"')}]);
|
||||||
$('#iDebridOptions').val([${debridOptions.map(option => '"' + option + '"')}]);
|
$('#iDebridOptions').val([${debridOptions.map(option => '"' + option + '"')}]);
|
||||||
@@ -422,8 +407,6 @@ export default function landingTemplate(manifest, config = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function generateInstallLink() {
|
function generateInstallLink() {
|
||||||
const providersList = $('#iProviders').val() || [];
|
|
||||||
const providersValue = providersList.join(',');
|
|
||||||
const qualityFilterValue = $('#iQualityFilter').val().join(',') || '';
|
const qualityFilterValue = $('#iQualityFilter').val().join(',') || '';
|
||||||
const sortValue = $('#iSort').val() || '';
|
const sortValue = $('#iSort').val() || '';
|
||||||
const languagesValue = $('#iLanguages').val().join(',') || [];
|
const languagesValue = $('#iLanguages').val().join(',') || [];
|
||||||
@@ -439,8 +422,6 @@ export default function landingTemplate(manifest, config = {}) {
|
|||||||
const putioClientIdValue = $('#iPutioClientId').val() || '';
|
const putioClientIdValue = $('#iPutioClientId').val() || '';
|
||||||
const putioTokenValue = $('#iPutioToken').val() || '';
|
const putioTokenValue = $('#iPutioToken').val() || '';
|
||||||
|
|
||||||
|
|
||||||
const providers = providersList.length && providersList.length < ${Providers.options.length} && providersValue;
|
|
||||||
const qualityFilters = qualityFilterValue.length && qualityFilterValue;
|
const qualityFilters = qualityFilterValue.length && qualityFilterValue;
|
||||||
const sort = sortValue !== '${SortOptions.options.qualitySeeders.key}' && sortValue;
|
const sort = sortValue !== '${SortOptions.options.qualitySeeders.key}' && sortValue;
|
||||||
const languages = languagesValue.length && languagesValue;
|
const languages = languagesValue.length && languagesValue;
|
||||||
@@ -456,7 +437,6 @@ export default function landingTemplate(manifest, config = {}) {
|
|||||||
const putio = putioClientIdValue.length && putioTokenValue.length && putioClientIdValue.trim() + '@' + putioTokenValue.trim();
|
const putio = putioClientIdValue.length && putioTokenValue.length && putioClientIdValue.trim() + '@' + putioTokenValue.trim();
|
||||||
|
|
||||||
let configurationValue = [
|
let configurationValue = [
|
||||||
['${Providers.key}', providers],
|
|
||||||
['${SortOptions.key}', sort],
|
['${SortOptions.key}', sort],
|
||||||
['${LanguageOptions.key}', languages],
|
['${LanguageOptions.key}', languages],
|
||||||
['${QualityFilter.key}', qualityFilters],
|
['${QualityFilter.key}', qualityFilters],
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import magnet from 'magnet-uri';
|
import magnet from 'magnet-uri';
|
||||||
import { getRandomUserAgent } from './requestHelper.js';
|
|
||||||
import { getTorrent } from './repository.js';
|
import { getTorrent } from './repository.js';
|
||||||
|
import { getRandomUserAgent } from './requestHelper.js';
|
||||||
import { Type } from './types.js';
|
import { Type } from './types.js';
|
||||||
import { extractProvider } from "./titleHelper.js";
|
|
||||||
import { Providers } from "./filter.js";
|
|
||||||
|
|
||||||
const TRACKERS_URL = 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt';
|
const TRACKERS_URL = 'https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt';
|
||||||
const DEFAULT_TRACKERS = [
|
const DEFAULT_TRACKERS = [
|
||||||
@@ -44,17 +42,7 @@ const RUSSIAN_TRACKERS = [
|
|||||||
"http://bt3.t-ru.org/ann?magnet",
|
"http://bt3.t-ru.org/ann?magnet",
|
||||||
"http://bt4.t-ru.org/ann?magnet",
|
"http://bt4.t-ru.org/ann?magnet",
|
||||||
];
|
];
|
||||||
// Some trackers have limits on original torrent trackers,
|
|
||||||
// where downloading ip has to seed the torrents for some amount of time,
|
|
||||||
// thus it doesn't work on mochs.
|
|
||||||
// So it's better to exclude them and try to download through DHT,
|
|
||||||
// as the torrent won't start anyway.
|
|
||||||
const RUSSIAN_PROVIDERS = Providers.options
|
|
||||||
.filter(provider => provider.foreign === '🇷🇺')
|
|
||||||
.map(provider => provider.label);
|
|
||||||
const ANIME_PROVIDERS = Providers.options
|
|
||||||
.filter(provider => provider.anime)
|
|
||||||
.map(provider => provider.label);
|
|
||||||
let BEST_TRACKERS = [];
|
let BEST_TRACKERS = [];
|
||||||
let ALL_ANIME_TRACKERS = [];
|
let ALL_ANIME_TRACKERS = [];
|
||||||
let ALL_RUSSIAN_TRACKERS = [];
|
let ALL_RUSSIAN_TRACKERS = [];
|
||||||
@@ -63,8 +51,7 @@ export async function getMagnetLink(infoHash) {
|
|||||||
const torrent = await getTorrent(infoHash).catch(() => ({ infoHash }));
|
const torrent = await getTorrent(infoHash).catch(() => ({ infoHash }));
|
||||||
const torrentTrackers = torrent?.trackers?.split(',') || [];
|
const torrentTrackers = torrent?.trackers?.split(',') || [];
|
||||||
const animeTrackers = torrent.type === Type.ANIME ? ALL_ANIME_TRACKERS : [];
|
const animeTrackers = torrent.type === Type.ANIME ? ALL_ANIME_TRACKERS : [];
|
||||||
const providerTrackers = RUSSIAN_PROVIDERS.includes(torrent.provider) && ALL_RUSSIAN_TRACKERS || [];
|
const trackers = unique([].concat(torrentTrackers).concat(animeTrackers));
|
||||||
const trackers = unique([].concat(torrentTrackers).concat(animeTrackers).concat(providerTrackers));
|
|
||||||
|
|
||||||
return magnet.encode({ infoHash: infoHash, name: torrent.title, announce: trackers });
|
return magnet.encode({ infoHash: infoHash, name: torrent.title, announce: trackers });
|
||||||
}
|
}
|
||||||
@@ -96,19 +83,6 @@ export function getSources(trackersInput, infoHash) {
|
|||||||
return trackers.map(tracker => `tracker:${tracker}`).concat(`dht:${infoHash}`);
|
return trackers.map(tracker => `tracker:${tracker}`).concat(`dht:${infoHash}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enrichStreamSources(stream) {
|
|
||||||
const provider = extractProvider(stream.title);
|
|
||||||
if (ANIME_PROVIDERS.includes(provider)) {
|
|
||||||
const sources = getSources(ALL_ANIME_TRACKERS, stream.infoHash);
|
|
||||||
return { ...stream, sources };
|
|
||||||
}
|
|
||||||
if (RUSSIAN_PROVIDERS.includes(provider)) {
|
|
||||||
const sources = unique([].concat(stream.sources || []).concat(getSources(ALL_RUSSIAN_TRACKERS, stream.infoHash)));
|
|
||||||
return { ...stream, sources };
|
|
||||||
}
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
function unique(array) {
|
function unique(array) {
|
||||||
return Array.from(new Set(array));
|
return Array.from(new Set(array));
|
||||||
}
|
}
|
||||||
20
src/node/addon/src/lib/promises.js
Normal file
20
src/node/addon/src/lib/promises.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Delay promise
|
||||||
|
*/
|
||||||
|
export async function delay(duration) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout promise after a set time in ms
|
||||||
|
*/
|
||||||
|
export async function timeout(timeoutMs, promise, message = 'Timed out') {
|
||||||
|
return Promise.race([
|
||||||
|
promise,
|
||||||
|
new Promise(function (resolve, reject) {
|
||||||
|
setTimeout(function () {
|
||||||
|
reject(message);
|
||||||
|
}, timeoutMs);
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Sequelize } from 'sequelize';
|
import { Sequelize } from 'sequelize';
|
||||||
const Op = Sequelize.Op;
|
|
||||||
|
const { Op } = Sequelize;
|
||||||
|
|
||||||
const DATABASE_URI = process.env.DATABASE_URI || 'postgres://postgres:postgres@localhost:5432/postgres';
|
const DATABASE_URI = process.env.DATABASE_URI || 'postgres://postgres:postgres@localhost:5432/postgres';
|
||||||
|
|
||||||
6
src/node/addon/src/lib/requestHelper.js
Normal file
6
src/node/addon/src/lib/requestHelper.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import UserAgent from 'user-agents';
|
||||||
|
const userAgent = new UserAgent();
|
||||||
|
|
||||||
|
export function getRandomUserAgent() {
|
||||||
|
return userAgent.random().toString();
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import titleParser from 'parse-torrent-title';
|
import titleParser from 'parse-torrent-title';
|
||||||
import { Type } from './types.js';
|
|
||||||
import { mapLanguages } from './languages.js';
|
import { mapLanguages } from './languages.js';
|
||||||
import { enrichStreamSources, getSources } from './magnetHelper.js';
|
import { getSources } from './magnetHelper.js';
|
||||||
import { getSubtitles } from './subtitles.js';
|
import { getSubtitles } from './subtitles.js';
|
||||||
|
import { Type } from './types.js';
|
||||||
|
|
||||||
const ADDON_NAME = 'selfhostio';
|
const ADDON_NAME = 'selfhostio';
|
||||||
const SIZE_DELTA = 0.02;
|
const SIZE_DELTA = 0.02;
|
||||||
@@ -108,7 +108,7 @@ export function applyStaticInfo(streams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function enrichStaticInfo(stream) {
|
function enrichStaticInfo(stream) {
|
||||||
return enrichSubtitles(enrichStreamSources({ ...stream }));
|
return enrichSubtitles(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
function enrichSubtitles(stream) {
|
function enrichSubtitles(stream) {
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import { parse } from 'parse-torrent-title';
|
import { parse } from 'parse-torrent-title';
|
||||||
import { isExtension } from './extension.js';
|
import { isExtension } from './extension.js';
|
||||||
import { Providers } from './filter.js';
|
|
||||||
import { languageFromCode } from './languages.js';
|
|
||||||
|
|
||||||
const languageMapping = {
|
const languageMapping = {
|
||||||
'english': 'eng',
|
'english': 'eng',
|
||||||
@@ -68,17 +66,12 @@ export function getSubtitles(record) {
|
|||||||
function parseLanguage(title, record) {
|
function parseLanguage(title, record) {
|
||||||
const subtitlePathParts = title.split('/');
|
const subtitlePathParts = title.split('/');
|
||||||
const subtitleFileName = subtitlePathParts.pop();
|
const subtitleFileName = subtitlePathParts.pop();
|
||||||
const subtitleTitleNoExt = title.replace(/\.\w{2,5}$/, '');
|
|
||||||
const videoFileName = record.title.split('/').pop().replace(/\.\w{2,5}$/, '');
|
const videoFileName = record.title.split('/').pop().replace(/\.\w{2,5}$/, '');
|
||||||
const fileNameLanguage = getSingleLanguage(subtitleFileName.replace(videoFileName, ''));
|
const fileNameLanguage = getSingleLanguage(subtitleFileName.replace(videoFileName, ''));
|
||||||
if (fileNameLanguage) {
|
if (fileNameLanguage) {
|
||||||
return fileNameLanguage;
|
return fileNameLanguage;
|
||||||
}
|
}
|
||||||
const videoTitleNoExt = record.title.replace(/\.\w{2,5}$/, '');
|
|
||||||
if (subtitleTitleNoExt === record.title || subtitleTitleNoExt === videoTitleNoExt) {
|
|
||||||
const provider = Providers.options.find(provider => provider.label === record.torrent.provider);
|
|
||||||
return provider?.foreign && languageFromCode(provider.foreign) || 'eng';
|
|
||||||
}
|
|
||||||
const folderName = subtitlePathParts.join('/');
|
const folderName = subtitlePathParts.join('/');
|
||||||
const folderNameLanguage = getSingleLanguage(folderName.replace(videoFileName, ''));
|
const folderNameLanguage = getSingleLanguage(folderName.replace(videoFileName, ''));
|
||||||
if (folderNameLanguage) {
|
if (folderNameLanguage) {
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
export function extractSeeders(title) {
|
|
||||||
const seedersMatch = title.match(/👤 (\d+)/);
|
|
||||||
return seedersMatch && parseInt(seedersMatch[1]) || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function extractSize(title) {
|
export function extractSize(title) {
|
||||||
const seedersMatch = title.match(/💾 ([\d.]+ \w+)/);
|
const seedersMatch = title.match(/💾 ([\d.]+ \w+)/);
|
||||||
return seedersMatch && parseSize(seedersMatch[1]) || 0;
|
return seedersMatch && parseSize(seedersMatch[1]) || 0;
|
||||||
6
src/node/addon/src/lib/types.js
Normal file
6
src/node/addon/src/lib/types.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const Type = {
|
||||||
|
MOVIE: 'movie',
|
||||||
|
SERIES: 'series',
|
||||||
|
ANIME: 'anime',
|
||||||
|
OTHER: 'other'
|
||||||
|
};
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import AllDebridClient from 'all-debrid-api';
|
import AllDebridClient from 'all-debrid-api';
|
||||||
import { Type } from '../lib/types.js';
|
|
||||||
import { isVideo, isArchive } from '../lib/extension.js';
|
import { isVideo, isArchive } from '../lib/extension.js';
|
||||||
import StaticResponse from './static.js';
|
|
||||||
import { getMagnetLink } from '../lib/magnetHelper.js';
|
import { getMagnetLink } from '../lib/magnetHelper.js';
|
||||||
|
import { Type } from '../lib/types.js';
|
||||||
import { BadTokenError, AccessDeniedError, sameFilename } from './mochHelper.js';
|
import { BadTokenError, AccessDeniedError, sameFilename } from './mochHelper.js';
|
||||||
|
import StaticResponse from './static.js';
|
||||||
|
|
||||||
const KEY = 'alldebrid';
|
const KEY = 'alldebrid';
|
||||||
const AGENT = 'selfhostio';
|
const AGENT = 'selfhostio';
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import DebridLinkClient from 'debrid-link-api';
|
import DebridLinkClient from 'debrid-link-api';
|
||||||
import { Type } from '../lib/types.js';
|
|
||||||
import { isVideo, isArchive } from '../lib/extension.js';
|
import { isVideo, isArchive } from '../lib/extension.js';
|
||||||
import StaticResponse from './static.js';
|
|
||||||
import { getMagnetLink } from '../lib/magnetHelper.js';
|
import { getMagnetLink } from '../lib/magnetHelper.js';
|
||||||
|
import { Type } from '../lib/types.js';
|
||||||
import { chunkArray, BadTokenError } from './mochHelper.js';
|
import { chunkArray, BadTokenError } from './mochHelper.js';
|
||||||
|
import StaticResponse from './static.js';
|
||||||
|
|
||||||
const KEY = 'debridlink';
|
const KEY = 'debridlink';
|
||||||
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
import namedQueue from 'named-queue';
|
import namedQueue from 'named-queue';
|
||||||
import * as options from './options.js';
|
|
||||||
import * as realdebrid from './realdebrid.js';
|
|
||||||
import * as premiumize from './premiumize.js';
|
|
||||||
import * as alldebrid from './alldebrid.js';
|
|
||||||
import * as debridlink from './debridlink.js';
|
|
||||||
import * as offcloud from './offcloud.js';
|
|
||||||
import * as putio from './putio.js';
|
|
||||||
import StaticResponse, { isStaticUrl } from './static.js';
|
|
||||||
import { cacheWrapResolvedUrl } from '../lib/cache.js';
|
import { cacheWrapResolvedUrl } from '../lib/cache.js';
|
||||||
import { timeout } from '../lib/promises.js';
|
import { timeout } from '../lib/promises.js';
|
||||||
|
import * as alldebrid from './alldebrid.js';
|
||||||
|
import * as debridlink from './debridlink.js';
|
||||||
import { BadTokenError, streamFilename, AccessDeniedError, enrichMeta } from './mochHelper.js';
|
import { BadTokenError, streamFilename, AccessDeniedError, enrichMeta } from './mochHelper.js';
|
||||||
|
import * as offcloud from './offcloud.js';
|
||||||
|
import * as options from './options.js';
|
||||||
|
import * as premiumize from './premiumize.js';
|
||||||
|
import * as putio from './putio.js';
|
||||||
|
import * as realdebrid from './realdebrid.js';
|
||||||
|
import StaticResponse, { isStaticUrl } from './static.js';
|
||||||
|
|
||||||
const RESOLVE_TIMEOUT = 2 * 60 * 1000; // 2 minutes
|
const RESOLVE_TIMEOUT = 2 * 60 * 1000; // 2 minutes
|
||||||
const MIN_API_KEY_SYMBOLS = 15;
|
const MIN_API_KEY_SYMBOLS = 15;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import OffcloudClient from 'offcloud-api';
|
|
||||||
import magnet from 'magnet-uri';
|
import magnet from 'magnet-uri';
|
||||||
import { Type } from '../lib/types.js';
|
import OffcloudClient from 'offcloud-api';
|
||||||
import { isVideo } from '../lib/extension.js';
|
import { isVideo } from '../lib/extension.js';
|
||||||
import StaticResponse from './static.js';
|
|
||||||
import { getMagnetLink } from '../lib/magnetHelper.js';
|
import { getMagnetLink } from '../lib/magnetHelper.js';
|
||||||
|
import { Type } from '../lib/types.js';
|
||||||
import { chunkArray, BadTokenError, sameFilename } from './mochHelper.js';
|
import { chunkArray, BadTokenError, sameFilename } from './mochHelper.js';
|
||||||
|
import StaticResponse from './static.js';
|
||||||
|
|
||||||
const KEY = 'offcloud';
|
const KEY = 'offcloud';
|
||||||
|
|
||||||
29
src/node/addon/src/moch/options.js
Normal file
29
src/node/addon/src/moch/options.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
export const DebridOptions = {
|
||||||
|
key: 'debridoptions',
|
||||||
|
options: {
|
||||||
|
noDownloadLinks: {
|
||||||
|
key: 'nodownloadlinks',
|
||||||
|
description: 'Don\'t show download to debrid links'
|
||||||
|
},
|
||||||
|
noCatalog: {
|
||||||
|
key: 'nocatalog',
|
||||||
|
description: 'Don\'t show debrid catalog'
|
||||||
|
},
|
||||||
|
torrentLinks: {
|
||||||
|
key: 'torrentlinks',
|
||||||
|
description: 'Show P2P torrent links for uncached'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function excludeDownloadLinks(config) {
|
||||||
|
return config[DebridOptions.key]?.includes(DebridOptions.options.noDownloadLinks.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function includeTorrentLinks(config) {
|
||||||
|
return config[DebridOptions.key]?.includes(DebridOptions.options.torrentLinks.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showDebridCatalog(config) {
|
||||||
|
return !config[DebridOptions.key]?.includes(DebridOptions.options.noCatalog.key);
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import PremiumizeClient from 'premiumize-api';
|
|
||||||
import magnet from 'magnet-uri';
|
import magnet from 'magnet-uri';
|
||||||
import { Type } from '../lib/types.js';
|
import PremiumizeClient from 'premiumize-api';
|
||||||
import { isVideo, isArchive } from '../lib/extension.js';
|
import { isVideo, isArchive } from '../lib/extension.js';
|
||||||
import StaticResponse from './static.js';
|
|
||||||
import { getMagnetLink } from '../lib/magnetHelper.js';
|
import { getMagnetLink } from '../lib/magnetHelper.js';
|
||||||
|
import { Type } from '../lib/types.js';
|
||||||
import { BadTokenError, chunkArray, sameFilename } from './mochHelper.js';
|
import { BadTokenError, chunkArray, sameFilename } from './mochHelper.js';
|
||||||
|
import StaticResponse from './static.js';
|
||||||
|
|
||||||
const KEY = 'premiumize';
|
const KEY = 'premiumize';
|
||||||
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import PutioClient from '@putdotio/api-client'
|
import PutioClient from '@putdotio/api-client'
|
||||||
import { isVideo } from '../lib/extension.js';
|
|
||||||
import { delay } from '../lib/promises.js';
|
|
||||||
import StaticResponse from './static.js';
|
|
||||||
import { getMagnetLink } from '../lib/magnetHelper.js';
|
|
||||||
import { Type } from "../lib/types.js";
|
|
||||||
import { decode } from "magnet-uri";
|
import { decode } from "magnet-uri";
|
||||||
|
import { isVideo } from '../lib/extension.js';
|
||||||
|
import { getMagnetLink } from '../lib/magnetHelper.js';
|
||||||
|
import { delay } from '../lib/promises.js';
|
||||||
|
import { Type } from "../lib/types.js";
|
||||||
import { sameFilename } from "./mochHelper.js";
|
import { sameFilename } from "./mochHelper.js";
|
||||||
|
import StaticResponse from './static.js';
|
||||||
const PutioAPI = PutioClient.default;
|
const PutioAPI = PutioClient.default;
|
||||||
|
|
||||||
const KEY = 'putio';
|
const KEY = 'putio';
|
||||||
@@ -198,10 +198,6 @@ function createPutioAPI(apiKey) {
|
|||||||
return Putio;
|
return Putio;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toCommonError(error) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function statusError(status) {
|
function statusError(status) {
|
||||||
return ['ERROR'].includes(status);
|
return ['ERROR'].includes(status);
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import RealDebridClient from 'real-debrid-api';
|
import RealDebridClient from 'real-debrid-api';
|
||||||
import { Type } from '../lib/types.js';
|
|
||||||
import { isVideo, isArchive } from '../lib/extension.js';
|
|
||||||
import { delay } from '../lib/promises.js';
|
|
||||||
import { cacheAvailabilityResults, getCachedAvailabilityResults } from '../lib/cache.js';
|
import { cacheAvailabilityResults, getCachedAvailabilityResults } from '../lib/cache.js';
|
||||||
import StaticResponse from './static.js';
|
import { isVideo, isArchive } from '../lib/extension.js';
|
||||||
import { getMagnetLink } from '../lib/magnetHelper.js';
|
import { getMagnetLink } from '../lib/magnetHelper.js';
|
||||||
|
import { delay } from '../lib/promises.js';
|
||||||
|
import { Type } from '../lib/types.js';
|
||||||
import { chunkArray, BadTokenError, AccessDeniedError } from './mochHelper.js';
|
import { chunkArray, BadTokenError, AccessDeniedError } from './mochHelper.js';
|
||||||
|
import StaticResponse from './static.js';
|
||||||
|
|
||||||
const MIN_SIZE = 5 * 1024 * 1024; // 5 MB
|
const MIN_SIZE = 5 * 1024 * 1024; // 5 MB
|
||||||
const CATALOG_MAX_PAGE = 1;
|
const CATALOG_MAX_PAGE = 1;
|
||||||
16
src/node/addon/src/moch/static.js
Normal file
16
src/node/addon/src/moch/static.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const staticVideoUrls = {
|
||||||
|
DOWNLOADING: `videos/downloading_v2.mp4`,
|
||||||
|
FAILED_DOWNLOAD: `videos/download_failed_v2.mp4`,
|
||||||
|
FAILED_ACCESS: `videos/failed_access_v2.mp4`,
|
||||||
|
FAILED_RAR: `videos/failed_rar_v2.mp4`,
|
||||||
|
FAILED_OPENING: `videos/failed_opening_v2.mp4`,
|
||||||
|
FAILED_UNEXPECTED: `videos/failed_unexpected_v2.mp4`,
|
||||||
|
FAILED_INFRINGEMENT: `videos/failed_infringement_v2.mp4`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function isStaticUrl(url) {
|
||||||
|
return Object.values(staticVideoUrls).some(videoUrl => url?.endsWith(videoUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default staticVideoUrls
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import Router from 'router';
|
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import rateLimit from "express-rate-limit";
|
import rateLimit from "express-rate-limit";
|
||||||
|
import qs from 'querystring';
|
||||||
import requestIp from 'request-ip';
|
import requestIp from 'request-ip';
|
||||||
|
import Router from 'router';
|
||||||
import userAgentParser from 'ua-parser-js';
|
import userAgentParser from 'ua-parser-js';
|
||||||
import addonInterface from './addon.js';
|
import addonInterface from './addon.js';
|
||||||
import qs from 'querystring';
|
|
||||||
import { manifest } from './lib/manifest.js';
|
|
||||||
import { parseConfiguration } from './lib/configuration.js';
|
import { parseConfiguration } from './lib/configuration.js';
|
||||||
import landingTemplate from './lib/landingTemplate.js';
|
import landingTemplate from './lib/landingTemplate.js';
|
||||||
|
import { manifest } from './lib/manifest.js';
|
||||||
import * as moch from './moch/moch.js';
|
import * as moch from './moch/moch.js';
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
@@ -104,4 +104,4 @@ export default function (req, res) {
|
|||||||
res.statusCode = 404;
|
res.statusCode = 404;
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
Reference in New Issue
Block a user