Some refactoring

This commit is contained in:
AnidlSupport 2023-12-25 10:46:13 -08:00
parent 7524f4c08f
commit ee38658d0d
4 changed files with 59 additions and 58 deletions

5
.gitignore vendored
View file

@ -36,4 +36,7 @@ crunchyendpoints
/logs
/tmp/*/
/videos/*/
/tmp/*.*
/tmp/*.*
bin
widevine/*
!widevine/.gitkeep

View file

@ -1,4 +1,4 @@
ffmpeg: "ffmpeg.exe"
mkvmerge: "mkvmerge.exe"
ffprobe: "ffprobe.exe"
mp4decrypt: "mp4decrypt.exe"
mp4decrypt: "./bin/mp4decrypt"

View file

@ -1376,6 +1376,7 @@ export default class Crunchy implements ServiceClass {
fileName = parseFileName(options.fileName, variables, options.numbers, options.override).join(path.sep);
const outFile = parseFileName(options.fileName + '.' + (mMeta.lang?.name || lang.name), variables, options.numbers, options.override).join(path.sep);
let [audioDownloaded, videoDownloaded] = [false, false];
// When best selected video quality is already downloaded
if(dlVideoOnce && options.dlVideoOnce) {
@ -1397,7 +1398,7 @@ export default class Crunchy implements ServiceClass {
segments: chosenVideoSegments.segments
};
const videoDownload = await new streamdl({
output: `${tsFile}.video.ts`,
output: `${tsFile}.video.enc.ts`,
timeout: options.timeout,
m3u8json: videoJson,
// baseurl: chunkPlaylist.baseUrl,
@ -1418,13 +1419,8 @@ export default class Crunchy implements ServiceClass {
console.error(`DL Stats: ${JSON.stringify(videoDownload.parts)}\n`);
dlFailed = true;
}
/*files.push({
type: 'Video',
path: `${tsFile}.video.ts`,
lang: lang,
isPrimary: isPrimary
});*/
dlVideoOnce = true;
videoDownloaded = true;
}
if (chosenAudioSegments) {
@ -1444,7 +1440,7 @@ export default class Crunchy implements ServiceClass {
segments: chosenAudioSegments.segments
};
const audioDownload = await new streamdl({
output: `${tsFile}.audio.ts`,
output: `${tsFile}.audio.enc.ts`,
timeout: options.timeout,
m3u8json: audioJson,
// baseurl: chunkPlaylist.baseUrl,
@ -1465,16 +1461,11 @@ export default class Crunchy implements ServiceClass {
console.error(`DL Stats: ${JSON.stringify(audioDownload.parts)}\n`);
dlFailed = true;
}
/*files.push({
type: 'Audio',
path: `${tsFile}.audio.ts`,
lang: lang,
isPrimary: isPrimary
});*/
audioDownloaded = true;
}
//Handle Decryption if needed
if (chosenVideoSegments.pssh || chosenAudioSegments.pssh) {
if ((chosenVideoSegments.pssh || chosenAudioSegments.pssh) && (videoDownloaded || audioDownloaded)) {
const assetIdRegex = chosenVideoSegments.segments[0].uri.match(/\/assets\/(?:p\/)?([^_,]+)/);
const assetId = assetIdRegex ? assetIdRegex[1] : null;
const sessionId = new Date().getUTCMilliseconds().toString().padStart(3, '0') + process.hrtime.bigint().toString().slice(0, 13);
@ -1509,54 +1500,59 @@ export default class Crunchy implements ServiceClass {
if (this.cfg.bin.mp4decrypt) {
const commandBase = `--show-progress --key ${encryptionKeys[1].kid}:${encryptionKeys[1].key} `;
const commandVideo = commandBase+`"${tsFile}.video.ts" "${tsFile}.dec.video.ts"`;
const commandAudio = commandBase+`"${tsFile}.audio.ts" "${tsFile}.dec.audio.ts"`;
const commandVideo = commandBase+`"${tsFile}.video.enc.ts" "${tsFile}.video.ts"`;
const commandAudio = commandBase+`"${tsFile}.audio.enc.ts" "${tsFile}.audio.ts"`;
console.info('Started decrypting video');
const decryptVideo = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandVideo);
if (!decryptVideo.isOk) {
console.error(decryptVideo.err);
console.error(`Decryption failed with exit code ${decryptVideo.err.code}`);
return undefined;
} else {
console.info('Decryption done for video');
if (videoDownloaded) {
console.info('Started decrypting video');
const decryptVideo = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandVideo);
if (!decryptVideo.isOk) {
console.error(decryptVideo.err);
console.error(`Decryption failed with exit code ${decryptVideo.err.code}`);
return undefined;
} else {
console.info('Decryption done for video');
fs.removeSync(`${tsFile}.video.enc.ts`);
files.push({
type: 'Video',
path: `${tsFile}.video.ts`,
lang: lang,
isPrimary: isPrimary
});
}
}
console.info('Started decrypting audio');
const decryptAudio = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandAudio);
if (!decryptAudio.isOk) {
console.error(decryptAudio.err);
console.error(`Decryption failed with exit code ${decryptAudio.err.code}`);
return undefined;
} else {
console.info('Decryption done for video');
if (audioDownloaded) {
console.info('Started decrypting audio');
const decryptAudio = exec('mp4decrypt', `"${this.cfg.bin.mp4decrypt}"`, commandAudio);
if (!decryptAudio.isOk) {
console.error(decryptAudio.err);
console.error(`Decryption failed with exit code ${decryptAudio.err.code}`);
return undefined;
} else {
fs.removeSync(`${tsFile}.audio.enc.ts`);
files.push({
type: 'Audio',
path: `${tsFile}.audio.ts`,
lang: lang,
isPrimary: isPrimary
});
console.info('Decryption done for audio');
}
}
files.push({
type: 'Video',
path: `${tsFile}.dec.video.ts`,
lang: lang,
isPrimary: isPrimary
});
files.push({
type: 'Audio',
path: `${tsFile}.dec.audio.ts`,
lang: lang,
isPrimary: isPrimary
});
} else {
console.warn('mp4decrypt not found, files need decryption. Decryption Keys:', encryptionKeys);
}
} else {
files.push({
type: 'Video',
path: `${tsFile}.video.ts`,
path: `${tsFile}.video.enc.ts`,
lang: lang,
isPrimary: isPrimary
});
files.push({
type: 'Audio',
path: `${tsFile}.audio.ts`,
path: `${tsFile}.audio.enc.ts`,
lang: lang,
isPrimary: isPrimary
});
@ -2053,7 +2049,7 @@ export default class Crunchy implements ServiceClass {
e: epNum,
image: images[Math.floor(images.length / 2)].source,
};
if (item.__links__.streams.href) {
if (item.__links__?.streams?.href) {
epMeta.data[0].playback = item.__links__.streams.href;
if(!item.playback) {
item.playback = item.__links__.streams.href;

View file

@ -1,12 +1,13 @@
import { KeyContainer, Session } from './license';
import fs from 'fs';
import { console } from './log';
import got from 'got';
//read cdm files located in the same directory
const privateKey = fs.readFileSync('./widevine/device_private_key');
const identifierBlob = fs.readFileSync('./widevine/device_client_id_blob');
export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record<string, string>): Promise<KeyContainer[]> {
export default async function getKeys(pssh: string | undefined, licenseServer: string, authData: Record<string, string>): Promise<KeyContainer[]> {
if (!pssh) return [];
//pssh found in the mpd manifest
const psshBuffer = Buffer.from(
@ -18,19 +19,20 @@ export default async function getKeys(pssh: string | undefined, licenseServer: s
const session = new Session({ privateKey, identifierBlob }, psshBuffer);
//Generate license
const response = await fetch(licenseServer, {
const response = await got(licenseServer, {
method: 'POST',
body: session.createLicenseRequest(),
headers: authData
headers: authData,
responseType: 'text'
});
if (response.ok) {
if (response.statusCode === 200) {
//Parse License and return keys
const json = await response.json();
const json = JSON.parse(response.body);
const keys = session.parseLicense(Buffer.from(json['license'], 'base64'));
return keys;
} else {
console.info('License request failed:', response.statusText);
console.info('License request failed:', response.statusMessage);
return [];
}
}
}