Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Playready support for Crunchyroll, AnimeOnegai and Hidive #792

Merged
merged 11 commits into from
Jan 4, 2025
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ crunchyendpoints
/tmp/*.*
bin
widevine/*
!widevine/.gitkeep
!widevine/.gitkeep
playready/*
!playready/.gitkeep
22 changes: 15 additions & 7 deletions ao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import * as yamlCfg from './modules/module.cfg-loader';
import * as yargs from './modules/module.app-args';
import * as reqModule from './modules/module.fetch';
import Merger, { Font, MergerInput, SubtitleInput } from './modules/module.merger';
import getKeys, { canDecrypt } from './modules/widevine';
import { canDecrypt, getKeysWVD, cdm, getKeysPRD } from './modules/cdm';
import streamdl, { M3U8Json } from './modules/hls-download';
import { exec } from './modules/sei-helper-fixes';
import { console } from './modules/log';
Expand Down Expand Up @@ -597,7 +597,7 @@ export default class AnimeOnegai implements ServiceClass {
};
try {
const videoDownload = await new streamdl({
output: chosenVideoSegments.pssh ? `${tempTsFile}.video.enc.mp4` : `${tsFile}.video.mp4`,
output: chosenVideoSegments.pssh_wvd ? `${tempTsFile}.video.enc.mp4` : `${tsFile}.video.mp4`,
timeout: options.timeout,
m3u8json: videoJson,
// baseurl: chunkPlaylist.baseUrl,
Expand Down Expand Up @@ -645,7 +645,7 @@ export default class AnimeOnegai implements ServiceClass {
};
try {
const audioDownload = await new streamdl({
output: chosenAudioSegments.pssh ? `${tempTsFile}.audio.enc.mp4` : `${tsFile}.audio.mp4`,
output: chosenAudioSegments.pssh_wvd ? `${tempTsFile}.audio.enc.mp4` : `${tsFile}.audio.mp4`,
timeout: options.timeout,
m3u8json: audioJson,
// baseurl: chunkPlaylist.baseUrl,
Expand Down Expand Up @@ -677,10 +677,18 @@ export default class AnimeOnegai implements ServiceClass {
}

//Handle Decryption if needed
if ((chosenVideoSegments.pssh || chosenAudioSegments.pssh) && (videoDownloaded || audioDownloaded)) {
if ((chosenVideoSegments.pssh_wvd || chosenAudioSegments.pssh_wvd) && (videoDownloaded || audioDownloaded)) {
console.info('Decryption Needed, attempting to decrypt');
const encryptionKeys = await getKeys(chosenVideoSegments.pssh, streamData.widevine_proxy, {});
if (encryptionKeys.length == 0) {
let encryptionKeys;

if (cdm === 'widevine') {
encryptionKeys = await getKeysWVD(chosenVideoSegments.pssh_wvd, streamData.widevine_proxy, {});
}
if (cdm === 'playready') {
encryptionKeys = await getKeysPRD(chosenVideoSegments.pssh_prd, streamData.playready_proxy, {});
}

if (!encryptionKeys || encryptionKeys.length == 0) {
console.error('Failed to get encryption keys');
return undefined;
}
Expand All @@ -690,7 +698,7 @@ export default class AnimeOnegai implements ServiceClass {
});*/

if (this.cfg.bin.mp4decrypt) {
const commandBase = `--show-progress --key ${encryptionKeys[1].kid}:${encryptionKeys[1].key} `;
const commandBase = `--show-progress --key ${encryptionKeys[cdm === 'playready' ? 0 : 1].kid}:${encryptionKeys[cdm === 'playready' ? 0 : 1].key} `;
const commandVideo = commandBase+`"${tempTsFile}.video.enc.mp4" "${tempTsFile}.video.mp4"`;
const commandAudio = commandBase+`"${tempTsFile}.audio.enc.mp4" "${tempTsFile}.audio.mp4"`;

Expand Down
33 changes: 23 additions & 10 deletions crunchy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import * as langsData from './modules/module.langsData';
import * as yamlCfg from './modules/module.cfg-loader';
import * as yargs from './modules/module.app-args';
import Merger, { Font, MergerInput, SubtitleInput } from './modules/module.merger';
import getKeys, { canDecrypt } from './modules/widevine';
import { canDecrypt, getKeysPRD, getKeysWVD, cdm } from './modules/cdm';
//import vttConvert from './modules/module.vttconvert';

// args
Expand Down Expand Up @@ -1652,7 +1652,7 @@ export default class Crunchy implements ServiceClass {
segments: chosenVideoSegments.segments
};
const videoDownload = await new streamdl({
output: chosenVideoSegments.pssh ? `${tempTsFile}.video.enc.m4s` : `${tsFile}.video.m4s`,
output: chosenVideoSegments.pssh_wvd || chosenVideoSegments.pssh_prd ? `${tempTsFile}.video.enc.m4s` : `${tsFile}.video.m4s`,
timeout: options.timeout,
m3u8json: videoJson,
// baseurl: chunkPlaylist.baseUrl,
Expand Down Expand Up @@ -1694,7 +1694,7 @@ export default class Crunchy implements ServiceClass {
segments: chosenAudioSegments.segments
};
const audioDownload = await new streamdl({
output: chosenAudioSegments.pssh ? `${tempTsFile}.audio.enc.m4s` : `${tsFile}.audio.m4s`,
output: chosenVideoSegments.pssh_wvd || chosenVideoSegments.pssh_prd ? `${tempTsFile}.audio.enc.m4s` : `${tsFile}.audio.m4s`,
timeout: options.timeout,
m3u8json: audioJson,
// baseurl: chunkPlaylist.baseUrl,
Expand All @@ -1721,7 +1721,7 @@ export default class Crunchy implements ServiceClass {
}

//Handle Decryption if needed
if ((chosenVideoSegments.pssh || chosenAudioSegments.pssh) && (videoDownloaded || audioDownloaded)) {
if ((chosenVideoSegments.pssh_wvd ||chosenVideoSegments.pssh_prd || chosenAudioSegments.pssh_wvd || chosenAudioSegments.pssh_prd) && (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);
Expand All @@ -1741,11 +1741,24 @@ export default class Crunchy implements ServiceClass {
return undefined;
}
const authData = await decReq.res.json() as {'custom_data': string, 'token': string};
const encryptionKeys = await getKeys(chosenVideoSegments.pssh, 'https://lic.drmtoday.com/license-proxy-widevine/cenc/', {
'dt-custom-data': authData.custom_data,
'x-dt-auth-token': authData.token
});
if (encryptionKeys.length == 0) {

let encryptionKeys;

if (cdm === 'widevine') {
encryptionKeys = await getKeysWVD(chosenVideoSegments.pssh_wvd, 'https://lic.drmtoday.com/license-proxy-widevine/cenc/', {
'dt-custom-data': authData.custom_data,
'x-dt-auth-token': authData.token
});
}

if (cdm === 'playready') {
encryptionKeys = await getKeysPRD(chosenVideoSegments.pssh_prd, 'https://lic.drmtoday.com/license-proxy-headerauth/drmtoday/RightsManager.asmx', {
'dt-custom-data': authData.custom_data,
'x-dt-auth-token': authData.token
});
}

if (!encryptionKeys || encryptionKeys.length == 0) {
console.error('Failed to get encryption keys');
return undefined;
}
Expand All @@ -1755,7 +1768,7 @@ export default class Crunchy implements ServiceClass {
});*/

if (this.cfg.bin.mp4decrypt) {
const commandBase = `--show-progress --key ${encryptionKeys[1].kid}:${encryptionKeys[1].key} `;
const commandBase = `--show-progress --key ${encryptionKeys[cdm === 'playready' ? 0 : 1].kid}:${encryptionKeys[cdm === 'playready' ? 0 : 1].key} `;
const commandVideo = commandBase+`"${tempTsFile}.video.enc.m4s" "${tempTsFile}.video.m4s"`;
const commandAudio = commandBase+`"${tempTsFile}.audio.enc.m4s" "${tempTsFile}.audio.m4s"`;

Expand Down
7 changes: 6 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ If you want to package the application, run pnpm run build-`{platform}`-`{type}`

* mp4decrypt >= Any (http://www.bento4.com/) - Only required for decrypting

### Instructions
### Instructions (Widevine)

In order to decrypt DRM content, you will need to have a dumped CDM, after that you will need to place the CDM files (`device_client_id_blob` and `device_private_key`) into the `./widevine/` directory. For legal reasons we do not include the CDM with the software, and you will have to source one yourself.

### Instructions (Playready)

Playready CDMs are very easy to obtain, you can find them even on Github.
Place the CDM in the `./playready/` directory and you're all set!
Loading
Loading