diff --git a/src/api/routes/service/service.service.ts b/src/api/routes/service/service.service.ts index a0e7028..2fbdef2 100644 --- a/src/api/routes/service/service.service.ts +++ b/src/api/routes/service/service.service.ts @@ -710,11 +710,38 @@ export async function downloadCrunchyrollPlaylist( if (playlist.mediaGroups.AUDIO.audio.main.playlists[0].contentProtection) { if (!playlist.mediaGroups.AUDIO.audio.main.playlists[0].contentProtection['com.widevine.alpha'].pssh) { console.log('No PSSH found, exiting.') + messageBox( + 'error', + ['Cancel'], + 2, + 'Encryption Detect error', + 'Encryption Detect error', + 'Audio file is decrypted, but it looks like not with widevine. Stopping Download. Contact Developer' + ) + server.logger.log({ + level: 'error', + message: `Audio file is decrypted, but it looks like not with widevine in Download ${downloadID}`, + error: 'No PSSH found', + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessAudioDecryption' + }) return } pssh = Uint8ArrayToBase64(playlist.mediaGroups.AUDIO.audio.main.playlists[0].contentProtection['com.widevine.alpha'].pssh) keys = await getDRMKeys(pssh, assetId[1], list.account_id) + + if (!keys) { + await updatePlaylistByID(downloadID, 'failed') + server.logger.log({ + level: 'error', + message: `No decryption keys, failing Download ${downloadID}`, + error: 'No decryption keys', + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessAudioDecryption' + }) + throw Error(`No decryption keys, failing Download ${downloadID}`) + } } if ( @@ -925,6 +952,18 @@ export async function downloadCrunchyrollPlaylist( pssh = Uint8ArrayToBase64(hq.contentProtection['com.widevine.alpha'].pssh) keys = await getDRMKeys(pssh, assetId[1], play.account_id) + + if (!keys) { + await updatePlaylistByID(downloadID, 'failed') + server.logger.log({ + level: 'error', + message: `No decryption keys, failing Download ${downloadID}`, + error: 'No decryption keys', + timestamp: new Date().toISOString(), + section: 'crunchyrollDownloadProcessVideoDecryption' + }) + throw Error(`No decryption keys, failing Download ${downloadID}`) + } } if ((hq.contentProtection && !drmL3blob && !drmL3key) || (hq.contentProtection && !drmL3blob) || (hq.contentProtection && !drmL3key)) { diff --git a/src/api/services/decryption.ts b/src/api/services/decryption.ts index 78933a0..d81c3ed 100644 --- a/src/api/services/decryption.ts +++ b/src/api/services/decryption.ts @@ -1,9 +1,22 @@ import { Session } from '../modules/license' import { readFileSync } from 'fs' import { getWVKPath } from './widevine' +import { messageBox } from '../../electron/background' +import { server } from '../api' export async function getDRMKeys(pssh: string, assetID: string, userID: string) { const auth = await getWVKey(assetID, userID) + + if (!auth) { + server.logger.log({ + level: 'error', + message: 'WVKey missing, aborting', + timestamp: new Date().toISOString(), + section: 'drmAuthCrunchyrollStatus' + }) + return + } + const depssh = Buffer.from(pssh, 'base64') const keys = await getWVKPath() @@ -14,23 +27,36 @@ export async function getDRMKeys(pssh: string, assetID: string, userID: string) if (!keys.client) return - const privateKey = readFileSync(keys.key) - const identifierBlob = readFileSync(keys.client) + try { + const privateKey = readFileSync(keys.key) + const identifierBlob = readFileSync(keys.client) - const session = new Session({ privateKey, identifierBlob }, depssh) + const session = new Session({ privateKey, identifierBlob }, depssh) - const response = await fetch('https://lic.drmtoday.com/license-proxy-widevine/cenc/', { - method: 'POST', - body: session.createLicenseRequest(), - headers: { - 'dt-custom-data': auth.custom_data, - 'x-dt-auth-token': auth.token + const response = await fetch('https://lic.drmtoday.com/license-proxy-widevine/cenc/', { + method: 'POST', + body: session.createLicenseRequest(), + headers: { + 'dt-custom-data': auth.custom_data, + 'x-dt-auth-token': auth.token + } + }) + + if (response.ok) { + const json = JSON.parse(await response.text()) + return session.parseLicense(Buffer.from(json['license'], 'base64')) as { kid: string; key: string }[] + } else { + throw Error(await response.text()) } - }) - - if (response.ok) { - const json = JSON.parse(await response.text()) - return session.parseLicense(Buffer.from(json['license'], 'base64')) as { kid: string; key: string }[] + } catch (e) { + messageBox('error', ['Cancel'], 2, 'Error while getting video/audio decryption keys', 'Error while getting video/audio decryption keys', String(e)) + server.logger.log({ + level: 'error', + message: 'Error while getting video/audio decryption keys', + error: String(e), + timestamp: new Date().toISOString(), + section: 'drmVideoAudioKeyCrunchyrollFetch' + }) } } @@ -43,7 +69,7 @@ export async function getWVKey(assetID: string, userID: string) { } try { - const response = await fetch(`https://pl.crunchyroll.com/drm/v1/auth`, { + const response = await fetch(`https://beta-api.crunchyroll.com/drm/v1/auth`, { method: 'POST', body: JSON.stringify(body) }) @@ -59,7 +85,14 @@ export async function getWVKey(assetID: string, userID: string) { throw new Error(await response.text()) } } catch (e) { - throw new Error(e as string) + messageBox('error', ['Cancel'], 2, 'Failed to Fetch Decryption token', 'Failed to Fetch Decryption token', String(e)) + server.logger.log({ + level: 'error', + message: 'Failed to Fetch Decryption token', + error: String(e), + timestamp: new Date().toISOString(), + section: 'drmAuthCrunchyrollFetch' + }) } }