Files
TV/scripts/build-android.mjs

80 lines
3.5 KiB
JavaScript

import { build } from 'esbuild';
import fs from 'node:fs';
import path from 'node:path';
const root = process.cwd();
const tmp = path.join(root, '.build');
const sdk = path.join(tmp, 'sdk');
const shim = path.join(tmp, 'shim');
const www = path.join(root, 'www');
fs.rmSync(tmp, { recursive: true, force: true });
fs.rmSync(www, { recursive: true, force: true });
fs.mkdirSync(shim, { recursive: true });
fs.mkdirSync(path.join(www, 'js'), { recursive: true });
fs.cpSync(path.join(root, 'node_modules/@vyla-entertainment/sdk/src'), sdk, { recursive: true });
const helpers = path.join(sdk, 'utils/helpers.js');
fs.writeFileSync(helpers, fs.readFileSync(helpers, 'utf8').replace(/\}, 60000\)\.unref\(\);/, '}, 60000);'));
fs.writeFileSync(path.join(shim, 'crypto.js'), `export const webcrypto = globalThis.crypto;
export function createHash() { throw new Error('unsupported'); }
export function createDecipheriv() { throw new Error('unsupported'); }
export default { webcrypto, createHash, createDecipheriv };
`);
fs.writeFileSync(path.join(shim, 'inject.js'), `import { Buffer } from 'buffer';
globalThis.Buffer = globalThis.Buffer || Buffer;
globalThis.process = globalThis.process || { env: {} };
if (!AbortSignal.timeout) {
AbortSignal.timeout = ms => { const c = new AbortController(); setTimeout(() => c.abort(), ms); return c.signal; };
}
export { Buffer };
`);
const files = [...new Set([...fs.readFileSync(path.join(sdk, 'config.js'), 'utf8').matchAll(/sourceFile:\s*'([^']+)'/g)].map(m => m[1]))];
const entry = `import SOURCES from './sdk/config.js';
import { createStreamArgs } from './sdk/utils/helpers.js';
import { fetchSubtitles, getSubPathsMovie, getSubPathsTv } from './sdk/subtitles.js';
${files.map((f, i) => `import * as m${i} from './sdk/sources/${f}.js';`).join('\n')}
const MODS = { ${files.map((f, i) => `'${f}': m${i}`).join(', ')} };
const core = { tmdbApiKey: null, penguManifest: null };
const live = () => SOURCES.filter(c => !c.disabled && MODS[c.sourceFile]);
globalThis.VylaLocal = {
init(key) { core.tmdbApiKey = key; globalThis.process.env.TMDB_API_KEY = key; },
sources() { return live(); },
async stream(key, id, s, e, ip) {
const cfg = live().find(c => c.key === key);
if (!cfg) throw new Error('Source not found');
return await MODS[cfg.sourceFile].getStream(await createStreamArgs(cfg, core, id, s, e, ip));
},
async subtitles(id, s, e) {
const paths = s != null && e != null ? await getSubPathsTv(id, s, e) : await getSubPathsMovie(id);
return await fetchSubtitles(paths);
}
};
`;
fs.writeFileSync(path.join(tmp, 'entry.js'), entry);
await build({
entryPoints: [path.join(tmp, 'entry.js')],
bundle: true,
format: 'iife',
target: 'chrome70',
platform: 'browser',
minify: true,
alias: { 'node:crypto': path.join(shim, 'crypto.js') },
inject: [path.join(shim, 'inject.js')],
outfile: path.join(www, 'js/vyla-local.js'),
logLevel: 'info'
});
for (const p of ['index.html', 'css', 'js', 'assets']) {
const from = path.join(root, p);
if (fs.existsSync(from)) fs.cpSync(from, path.join(www, p), { recursive: true, filter: s => !s.endsWith('vyla-local.js') });
}
fs.copyFileSync(path.join(root, 'node_modules/hls.js/dist/hls.min.js'), path.join(www, 'js/hls.min.js'));
const androidAssets = path.join(root, 'android/app/src/main/assets');
if (!fs.existsSync(androidAssets)) fs.mkdirSync(androidAssets, { recursive: true });
fs.copyFileSync(path.join(www, 'js/vyla-local.js'), path.join(androidAssets, 'vyla-local.js'));