Initial commit for WV Decryption
This commit is contained in:
parent
d1d9840629
commit
28518bb461
12 changed files with 6439 additions and 34 deletions
|
|
@ -1,3 +1,4 @@
|
|||
ffmpeg: "ffmpeg.exe"
|
||||
mkvmerge: "mkvmerge.exe"
|
||||
ffprobe: "ffprobe.exe"
|
||||
mp4decrypt: "mp4decrypt.exe"
|
||||
|
|
|
|||
114
crunchy.ts
114
crunchy.ts
|
|
@ -4,11 +4,13 @@ import fs from 'fs-extra';
|
|||
|
||||
// package program
|
||||
import packageJson from './package.json';
|
||||
|
||||
// plugins
|
||||
import { console } from './modules/log';
|
||||
import shlp from 'sei-helper';
|
||||
import m3u8 from 'm3u8-parsed';
|
||||
import streamdl, { M3U8Json } from './modules/hls-download';
|
||||
import { exec } from './modules/sei-helper-fixes';
|
||||
|
||||
// custom modules
|
||||
import * as fontsData from './modules/module.fontsData';
|
||||
|
|
@ -16,6 +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 from './modules/cr_widevine';
|
||||
|
||||
// args
|
||||
|
||||
|
|
@ -1388,12 +1391,12 @@ export default class Crunchy implements ServiceClass {
|
|||
console.error(`DL Stats: ${JSON.stringify(videoDownload.parts)}\n`);
|
||||
dlFailed = true;
|
||||
}
|
||||
files.push({
|
||||
/*files.push({
|
||||
type: 'Video',
|
||||
path: `${tsFile}.video.ts`,
|
||||
lang: lang,
|
||||
isPrimary: isPrimary
|
||||
});
|
||||
});*/
|
||||
dlVideoOnce = true;
|
||||
}
|
||||
|
||||
|
|
@ -1435,6 +1438,104 @@ 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
|
||||
});*/
|
||||
}
|
||||
|
||||
//Handle Decryption if needed
|
||||
if (chosenVideoSegments.pssh || chosenAudioSegments.pssh) {
|
||||
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);
|
||||
console.info('Decryption Needed, attempting to decrypt');
|
||||
|
||||
const decReq = await this.req.getData('https://pl.crunchyroll.com/drm/v1/auth', {
|
||||
'method': 'POST',
|
||||
'body': JSON.stringify({
|
||||
'accounting_id': 'crunchyroll',
|
||||
'asset_id': assetId,
|
||||
'session_id': sessionId,
|
||||
'user_id': this.token.account_id
|
||||
})
|
||||
});
|
||||
console.info({
|
||||
'body': JSON.stringify({
|
||||
'accounting_id': 'crunchyroll',
|
||||
'asset_id': assetId,
|
||||
'session_id': sessionId,
|
||||
'user_id': this.token.account_id
|
||||
})
|
||||
});
|
||||
if(!decReq.ok || !decReq.res){
|
||||
console.error('Request to DRM Authentication failed:', decReq.error?.code, decReq.error?.message);
|
||||
return undefined;
|
||||
}
|
||||
const authData = JSON.parse(decReq.res.body) as {'custom_data': string, 'token': string};
|
||||
console.info(authData);
|
||||
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) {
|
||||
console.error('Failed to get encryption keys');
|
||||
return undefined;
|
||||
}
|
||||
/*const keys = {} as Record<string, string>;
|
||||
encryptionKeys.forEach(function(key) {
|
||||
keys[key.kid] = key.key;
|
||||
});*/
|
||||
|
||||
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"`;
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
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`,
|
||||
lang: lang,
|
||||
isPrimary: isPrimary
|
||||
});
|
||||
files.push({
|
||||
type: 'Audio',
|
||||
path: `${tsFile}.audio.ts`,
|
||||
|
|
@ -1442,15 +1543,6 @@ export default class Crunchy implements ServiceClass {
|
|||
isPrimary: isPrimary
|
||||
});
|
||||
}
|
||||
|
||||
//Handle Decryption if needed
|
||||
if (chosenVideoSegments.pssh) {
|
||||
console.info('Decryption of Video Needed');
|
||||
}
|
||||
|
||||
if (chosenAudioSegments.pssh) {
|
||||
console.info('Decryption of Audio Needed');
|
||||
}
|
||||
} else {
|
||||
const streamPlaylists = m3u8(streamPlaylistsReq.res.body);
|
||||
const plServerList: string[] = [],
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ This application is not endorsed by or affiliated with *Funimation*, *Crunchyrol
|
|||
* PNPM >= 7.0.0 (https://pnpm.io/)
|
||||
* ffmpeg >= 4.0.0 (https://www.videohelp.com/software/ffmpeg)
|
||||
* MKVToolNix >= 60.0.0 (https://www.videohelp.com/software/MKVToolNix)
|
||||
* mp4decrypt >= Any (http://www.bento4.com/) - Only required for decrypting
|
||||
|
||||
### Paths Configuration
|
||||
|
||||
|
|
@ -23,6 +24,7 @@ By default this application uses the following paths to programs (main executabl
|
|||
* `ffmpeg.exe` (From PATH)
|
||||
* `ffprobe.exe` (From PATH)
|
||||
* `mkvmerge.exe` (From PATH)
|
||||
* `mp4decrypt.exe` (From PATH)
|
||||
|
||||
To change these paths you need to edit `bin-path.yml` in `./config/` directory.
|
||||
|
||||
|
|
|
|||
114
modules/cmac.ts
Normal file
114
modules/cmac.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//Originally from https://github.com/Frooastside/node-widevine/blob/main/src/cmac.ts
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
export class AES_CMAC {
|
||||
private readonly BLOCK_SIZE = 16;
|
||||
private readonly XOR_RIGHT = Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87]);
|
||||
private readonly EMPTY_BLOCK_SIZE_BUFFER = Buffer.alloc(this.BLOCK_SIZE);
|
||||
|
||||
private _key: Buffer;
|
||||
private _subkeys: { first: Buffer; second: Buffer };
|
||||
|
||||
public constructor(key: Buffer) {
|
||||
if (![16, 24, 32].includes(key.length)) {
|
||||
throw new Error('Key size must be 128, 192, or 256 bits.');
|
||||
}
|
||||
this._key = key;
|
||||
this._subkeys = this._generateSubkeys();
|
||||
}
|
||||
|
||||
public calculate(message: Buffer): Buffer {
|
||||
const blockCount = this._getBlockCount(message);
|
||||
|
||||
let x = this.EMPTY_BLOCK_SIZE_BUFFER;
|
||||
let y;
|
||||
|
||||
for (let i = 0; i < blockCount - 1; i++) {
|
||||
const from = i * this.BLOCK_SIZE;
|
||||
const block = message.subarray(from, from + this.BLOCK_SIZE);
|
||||
y = this._xor(x, block);
|
||||
x = this._aes(y);
|
||||
}
|
||||
|
||||
y = this._xor(x, this._getLastBlock(message));
|
||||
x = this._aes(y);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
private _generateSubkeys(): { first: Buffer; second: Buffer } {
|
||||
const l = this._aes(this.EMPTY_BLOCK_SIZE_BUFFER);
|
||||
|
||||
let first = this._bitShiftLeft(l);
|
||||
if (l[0] & 0x80) {
|
||||
first = this._xor(first, this.XOR_RIGHT);
|
||||
}
|
||||
|
||||
let second = this._bitShiftLeft(first);
|
||||
if (first[0] & 0x80) {
|
||||
second = this._xor(second, this.XOR_RIGHT);
|
||||
}
|
||||
|
||||
return { first: first, second: second };
|
||||
}
|
||||
|
||||
private _getBlockCount(message: Buffer): number {
|
||||
const blockCount = Math.ceil(message.length / this.BLOCK_SIZE);
|
||||
return blockCount === 0 ? 1 : blockCount;
|
||||
}
|
||||
|
||||
private _aes(message: Buffer): Buffer {
|
||||
const cipher = crypto.createCipheriv(`aes-${this._key.length * 8}-cbc`, this._key, Buffer.alloc(this.BLOCK_SIZE));
|
||||
const result = cipher.update(message).subarray(0, 16);
|
||||
cipher.destroy();
|
||||
return result;
|
||||
}
|
||||
|
||||
private _getLastBlock(message: Buffer): Buffer {
|
||||
const blockCount = this._getBlockCount(message);
|
||||
const paddedBlock = this._padding(message, blockCount - 1);
|
||||
|
||||
let complete = false;
|
||||
if (message.length > 0) {
|
||||
complete = message.length % this.BLOCK_SIZE === 0;
|
||||
}
|
||||
|
||||
const key = complete ? this._subkeys.first : this._subkeys.second;
|
||||
return this._xor(paddedBlock, key);
|
||||
}
|
||||
|
||||
private _padding(message: Buffer, blockIndex: number): Buffer {
|
||||
const block = Buffer.alloc(this.BLOCK_SIZE);
|
||||
|
||||
const from = blockIndex * this.BLOCK_SIZE;
|
||||
|
||||
const slice = message.subarray(from, from + this.BLOCK_SIZE);
|
||||
block.set(slice);
|
||||
|
||||
if (slice.length !== this.BLOCK_SIZE) {
|
||||
block[slice.length] = 0x80;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
private _bitShiftLeft(input: Buffer): Buffer {
|
||||
const output = Buffer.alloc(input.length);
|
||||
let overflow = 0;
|
||||
for (let i = input.length - 1; i >= 0; i--) {
|
||||
output[i] = (input[i] << 1) | overflow;
|
||||
overflow = input[i] & 0x80 ? 1 : 0;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
private _xor(a: Buffer, b: Buffer): Buffer {
|
||||
const length = Math.min(a.length, b.length);
|
||||
const output = Buffer.alloc(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
output[i] = a[i] ^ b[i];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
36
modules/cr_widevine.ts
Normal file
36
modules/cr_widevine.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { KeyContainer, Session } from './license';
|
||||
import fs from 'fs';
|
||||
import { console } from './log';
|
||||
|
||||
//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[]> {
|
||||
if (!pssh) return [];
|
||||
//pssh found in the mpd manifest
|
||||
const psshBuffer = Buffer.from(
|
||||
pssh,
|
||||
'base64'
|
||||
);
|
||||
|
||||
//Create a new widevine session
|
||||
const session = new Session({ privateKey, identifierBlob }, psshBuffer);
|
||||
|
||||
//Generate license
|
||||
const response = await fetch(licenseServer, {
|
||||
method: 'POST',
|
||||
body: session.createLicenseRequest(),
|
||||
headers: authData
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
//Parse License and return keys
|
||||
const json = await response.json();
|
||||
const keys = session.parseLicense(Buffer.from(json['license'], 'base64'));
|
||||
return keys;
|
||||
} else {
|
||||
console.info('License request failed:', response.statusText);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
161
modules/license.ts
Normal file
161
modules/license.ts
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
//Originaly from https://github.com/Frooastside/node-widevine/blob/main/src/license.ts
|
||||
|
||||
import crypto from 'crypto';
|
||||
import Long from 'long';
|
||||
import { AES_CMAC } from './cmac';
|
||||
import {
|
||||
ClientIdentification,
|
||||
License,
|
||||
LicenseRequest,
|
||||
LicenseRequest_RequestType,
|
||||
LicenseType,
|
||||
ProtocolVersion,
|
||||
SignedMessage,
|
||||
SignedMessage_MessageType,
|
||||
SignedMessage_SessionKeyType,
|
||||
WidevinePsshData
|
||||
} from './license_protocol';
|
||||
|
||||
const WIDEVINE_SYSTEM_ID = new Uint8Array([237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237]);
|
||||
|
||||
export type KeyContainer = {
|
||||
kid: string;
|
||||
key: string;
|
||||
};
|
||||
|
||||
export type ContentDecryptionModule = {
|
||||
privateKey: Buffer;
|
||||
identifierBlob: Buffer;
|
||||
};
|
||||
|
||||
export class Session {
|
||||
private _devicePrivateKey: crypto.KeyObject;
|
||||
private _identifierBlob: ClientIdentification;
|
||||
private _identifier: Buffer;
|
||||
private _pssh: Buffer;
|
||||
private _rawLicenseRequest?: Buffer;
|
||||
|
||||
constructor(contentDecryptionModule: ContentDecryptionModule, pssh: Buffer) {
|
||||
this._devicePrivateKey = crypto.createPrivateKey(contentDecryptionModule.privateKey);
|
||||
this._identifierBlob = ClientIdentification.decode(contentDecryptionModule.identifierBlob);
|
||||
this._identifier = this._generateIdentifier();
|
||||
this._pssh = pssh;
|
||||
}
|
||||
|
||||
createLicenseRequest(): Buffer {
|
||||
if (!this._pssh.subarray(12, 28).equals(Buffer.from(WIDEVINE_SYSTEM_ID))) {
|
||||
throw new Error('the pssh is not an actuall pssh');
|
||||
}
|
||||
const pssh = this._parsePSSH(this._pssh);
|
||||
if (!pssh) {
|
||||
throw new Error('pssh is invalid');
|
||||
}
|
||||
|
||||
const licenseRequest: LicenseRequest = {
|
||||
type: LicenseRequest_RequestType.NEW,
|
||||
clientId: this._identifierBlob,
|
||||
contentId: {
|
||||
widevinePsshData: {
|
||||
psshData: [this._pssh.subarray(32)],
|
||||
licenseType: LicenseType.STREAMING,
|
||||
requestId: this._identifier
|
||||
}
|
||||
},
|
||||
requestTime: Long.fromNumber(Date.now()).divide(1000),
|
||||
protocolVersion: ProtocolVersion.VERSION_2_1,
|
||||
keyControlNonce: crypto.randomInt(2 ** 31),
|
||||
keyControlNonceDeprecated: Buffer.alloc(0),
|
||||
encryptedClientId: undefined
|
||||
};
|
||||
|
||||
this._rawLicenseRequest = Buffer.from(LicenseRequest.encode(licenseRequest).finish());
|
||||
|
||||
const signature = crypto
|
||||
.createSign('sha1')
|
||||
.update(this._rawLicenseRequest)
|
||||
.sign({ key: this._devicePrivateKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: 20 });
|
||||
|
||||
const signedLicenseRequest: SignedMessage = {
|
||||
type: SignedMessage_MessageType.LICENSE_REQUEST,
|
||||
msg: this._rawLicenseRequest,
|
||||
signature: Buffer.from(signature),
|
||||
sessionKey: Buffer.alloc(0),
|
||||
remoteAttestation: Buffer.alloc(0),
|
||||
metricData: [],
|
||||
serviceVersionInfo: undefined,
|
||||
sessionKeyType: SignedMessage_SessionKeyType.UNDEFINED,
|
||||
oemcryptoCoreMessage: Buffer.alloc(0)
|
||||
};
|
||||
|
||||
return Buffer.from(SignedMessage.encode(signedLicenseRequest).finish());
|
||||
}
|
||||
|
||||
parseLicense(rawLicense: Buffer) {
|
||||
if (!this._rawLicenseRequest) {
|
||||
throw new Error('please request a license first');
|
||||
}
|
||||
const signedLicense = SignedMessage.decode(rawLicense);
|
||||
const sessionKey = crypto.privateDecrypt(this._devicePrivateKey, signedLicense.sessionKey);
|
||||
|
||||
const cmac = new AES_CMAC(Buffer.from(sessionKey));
|
||||
|
||||
const encKeyBase = Buffer.concat([
|
||||
Buffer.from('ENCRYPTION'),
|
||||
Buffer.from('\x00', 'ascii'),
|
||||
this._rawLicenseRequest,
|
||||
Buffer.from('\x00\x00\x00\x80', 'ascii')
|
||||
]);
|
||||
const authKeyBase = Buffer.concat([
|
||||
Buffer.from('AUTHENTICATION'),
|
||||
Buffer.from('\x00', 'ascii'),
|
||||
this._rawLicenseRequest,
|
||||
Buffer.from('\x00\x00\x02\x00', 'ascii')
|
||||
]);
|
||||
|
||||
const encKey = cmac.calculate(Buffer.concat([Buffer.from('\x01'), encKeyBase]));
|
||||
const serverKey = Buffer.concat([
|
||||
cmac.calculate(Buffer.concat([Buffer.from('\x01'), authKeyBase])),
|
||||
cmac.calculate(Buffer.concat([Buffer.from('\x02'), authKeyBase]))
|
||||
]);
|
||||
/*const clientKey = Buffer.concat([
|
||||
cmac.calculate(Buffer.concat([Buffer.from("\x03"), authKeyBase])),
|
||||
cmac.calculate(Buffer.concat([Buffer.from("\x04"), authKeyBase]))
|
||||
]);*/
|
||||
|
||||
const calculatedSignature = crypto.createHmac('sha256', serverKey).update(signedLicense.msg).digest();
|
||||
|
||||
if (!calculatedSignature.equals(signedLicense.signature)) {
|
||||
throw new Error('signatures do not match');
|
||||
}
|
||||
|
||||
const license = License.decode(signedLicense.msg);
|
||||
|
||||
return license.key.map((keyContainer) => {
|
||||
const keyId = keyContainer.id.length ? keyContainer.id.toString('hex') : keyContainer.type.toString();
|
||||
const decipher = crypto.createDecipheriv(`aes-${encKey.length * 8}-cbc`, encKey, keyContainer.iv);
|
||||
const decryptedKey = decipher.update(keyContainer.key);
|
||||
decipher.destroy();
|
||||
const key: KeyContainer = {
|
||||
kid: keyId,
|
||||
key: decryptedKey.toString('hex')
|
||||
};
|
||||
return key;
|
||||
});
|
||||
}
|
||||
|
||||
private _parsePSSH(pssh: Buffer): WidevinePsshData | null {
|
||||
try {
|
||||
return WidevinePsshData.decode(pssh.subarray(32));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private _generateIdentifier(): Buffer {
|
||||
return Buffer.from(`${crypto.randomBytes(8).toString('hex')}${'01'}${'00000000000000'}`);
|
||||
}
|
||||
|
||||
get pssh(): Buffer {
|
||||
return this._pssh;
|
||||
}
|
||||
}
|
||||
749
modules/license_protocol.proto
Normal file
749
modules/license_protocol.proto
Normal file
|
|
@ -0,0 +1,749 @@
|
|||
//Originally from https://github.com/Frooastside/node-widevine/blob/main/src/license_protocol.proto
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package license_protocol;
|
||||
|
||||
enum LicenseType {
|
||||
STREAMING = 1;
|
||||
OFFLINE = 2;
|
||||
// License type decision is left to provider.
|
||||
AUTOMATIC = 3;
|
||||
}
|
||||
|
||||
enum PlatformVerificationStatus {
|
||||
// The platform is not verified.
|
||||
PLATFORM_UNVERIFIED = 0;
|
||||
// Tampering detected on the platform.
|
||||
PLATFORM_TAMPERED = 1;
|
||||
// The platform has been verified by means of software.
|
||||
PLATFORM_SOFTWARE_VERIFIED = 2;
|
||||
// The platform has been verified by means of hardware (e.g. secure boot).
|
||||
PLATFORM_HARDWARE_VERIFIED = 3;
|
||||
// Platform verification was not performed.
|
||||
PLATFORM_NO_VERIFICATION = 4;
|
||||
// Platform and secure storage capability have been verified by means of
|
||||
// software.
|
||||
PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED = 5;
|
||||
}
|
||||
|
||||
// LicenseIdentification is propagated from LicenseRequest to License,
|
||||
// incrementing version with each iteration.
|
||||
message LicenseIdentification {
|
||||
optional bytes request_id = 1;
|
||||
optional bytes session_id = 2;
|
||||
optional bytes purchase_id = 3;
|
||||
optional LicenseType type = 4;
|
||||
optional int32 version = 5;
|
||||
optional bytes provider_session_token = 6;
|
||||
}
|
||||
|
||||
message License {
|
||||
message Policy {
|
||||
// Indicates that playback of the content is allowed.
|
||||
optional bool can_play = 1 [default = false];
|
||||
|
||||
// Indicates that the license may be persisted to non-volatile
|
||||
// storage for offline use.
|
||||
optional bool can_persist = 2 [default = false];
|
||||
|
||||
// Indicates that renewal of this license is allowed.
|
||||
optional bool can_renew = 3 [default = false];
|
||||
|
||||
// For the |*duration*| fields, playback must halt when
|
||||
// license_start_time (seconds since the epoch (UTC)) +
|
||||
// license_duration_seconds is exceeded. A value of 0
|
||||
// indicates that there is no limit to the duration.
|
||||
|
||||
// Indicates the rental window.
|
||||
optional int64 rental_duration_seconds = 4 [default = 0];
|
||||
|
||||
// Indicates the viewing window, once playback has begun.
|
||||
optional int64 playback_duration_seconds = 5 [default = 0];
|
||||
|
||||
// Indicates the time window for this specific license.
|
||||
optional int64 license_duration_seconds = 6 [default = 0];
|
||||
|
||||
// The |renewal*| fields only apply if |can_renew| is true.
|
||||
|
||||
// The window of time, in which playback is allowed to continue while
|
||||
// renewal is attempted, yet unsuccessful due to backend problems with
|
||||
// the license server.
|
||||
optional int64 renewal_recovery_duration_seconds = 7 [default = 0];
|
||||
|
||||
// All renewal requests for this license shall be directed to the
|
||||
// specified URL.
|
||||
optional string renewal_server_url = 8;
|
||||
|
||||
// How many seconds after license_start_time, before renewal is first
|
||||
// attempted.
|
||||
optional int64 renewal_delay_seconds = 9 [default = 0];
|
||||
|
||||
// Specifies the delay in seconds between subsequent license
|
||||
// renewal requests, in case of failure.
|
||||
optional int64 renewal_retry_interval_seconds = 10 [default = 0];
|
||||
|
||||
// Indicates that the license shall be sent for renewal when usage is
|
||||
// started.
|
||||
optional bool renew_with_usage = 11 [default = false];
|
||||
|
||||
// Indicates to client that license renewal and release requests ought to
|
||||
// include ClientIdentification (client_id).
|
||||
optional bool always_include_client_id = 12 [default = false];
|
||||
|
||||
// Duration of grace period before playback_duration_seconds (short window)
|
||||
// goes into effect. Optional.
|
||||
optional int64 play_start_grace_period_seconds = 13 [default = 0];
|
||||
|
||||
// Enables "soft enforcement" of playback_duration_seconds, letting the user
|
||||
// finish playback even if short window expires. Optional.
|
||||
optional bool soft_enforce_playback_duration = 14 [default = false];
|
||||
|
||||
// Enables "soft enforcement" of rental_duration_seconds. Initial playback
|
||||
// must always start before rental duration expires. In order to allow
|
||||
// subsequent playbacks to start after the rental duration expires,
|
||||
// soft_enforce_playback_duration must be true. Otherwise, subsequent
|
||||
// playbacks will not be allowed once rental duration expires. Optional.
|
||||
optional bool soft_enforce_rental_duration = 15 [default = true];
|
||||
}
|
||||
|
||||
message KeyContainer {
|
||||
enum KeyType {
|
||||
SIGNING = 1; // Exactly one key of this type must appear.
|
||||
CONTENT = 2; // Content key.
|
||||
KEY_CONTROL = 3; // Key control block for license renewals. No key.
|
||||
OPERATOR_SESSION = 4; // wrapped keys for auxiliary crypto operations.
|
||||
ENTITLEMENT = 5; // Entitlement keys.
|
||||
OEM_CONTENT = 6; // Partner-specific content key.
|
||||
}
|
||||
|
||||
// The SecurityLevel enumeration allows the server to communicate the level
|
||||
// of robustness required by the client, in order to use the key.
|
||||
enum SecurityLevel {
|
||||
// Software-based whitebox crypto is required.
|
||||
SW_SECURE_CRYPTO = 1;
|
||||
|
||||
// Software crypto and an obfuscated decoder is required.
|
||||
SW_SECURE_DECODE = 2;
|
||||
|
||||
// The key material and crypto operations must be performed within a
|
||||
// hardware backed trusted execution environment.
|
||||
HW_SECURE_CRYPTO = 3;
|
||||
|
||||
// The crypto and decoding of content must be performed within a hardware
|
||||
// backed trusted execution environment.
|
||||
HW_SECURE_DECODE = 4;
|
||||
|
||||
// The crypto, decoding and all handling of the media (compressed and
|
||||
// uncompressed) must be handled within a hardware backed trusted
|
||||
// execution environment.
|
||||
HW_SECURE_ALL = 5;
|
||||
}
|
||||
|
||||
message KeyControl {
|
||||
// |key_control| is documented in:
|
||||
// Widevine Modular DRM Security Integration Guide for CENC
|
||||
// If present, the key control must be communicated to the secure
|
||||
// environment prior to any usage. This message is automatically generated
|
||||
// by the Widevine License Server SDK.
|
||||
optional bytes key_control_block = 1;
|
||||
optional bytes iv = 2;
|
||||
}
|
||||
|
||||
message OutputProtection {
|
||||
// Indicates whether HDCP is required on digital outputs, and which
|
||||
// version should be used.
|
||||
enum HDCP {
|
||||
HDCP_NONE = 0;
|
||||
HDCP_V1 = 1;
|
||||
HDCP_V2 = 2;
|
||||
HDCP_V2_1 = 3;
|
||||
HDCP_V2_2 = 4;
|
||||
HDCP_V2_3 = 5;
|
||||
HDCP_NO_DIGITAL_OUTPUT = 0xff;
|
||||
}
|
||||
optional HDCP hdcp = 1 [default = HDCP_NONE];
|
||||
|
||||
// Indicate the CGMS setting to be inserted on analog output.
|
||||
enum CGMS {
|
||||
CGMS_NONE = 42;
|
||||
COPY_FREE = 0;
|
||||
COPY_ONCE = 2;
|
||||
COPY_NEVER = 3;
|
||||
}
|
||||
optional CGMS cgms_flags = 2 [default = CGMS_NONE];
|
||||
|
||||
enum HdcpSrmRule {
|
||||
HDCP_SRM_RULE_NONE = 0;
|
||||
// In 'required_protection', this means most current SRM is required.
|
||||
// Update the SRM on the device. If update cannot happen,
|
||||
// do not allow the key.
|
||||
// In 'requested_protection', this means most current SRM is requested.
|
||||
// Update the SRM on the device. If update cannot happen,
|
||||
// allow use of the key anyway.
|
||||
CURRENT_SRM = 1;
|
||||
}
|
||||
optional HdcpSrmRule hdcp_srm_rule = 3 [default = HDCP_SRM_RULE_NONE];
|
||||
// Optional requirement to indicate analog output is not allowed.
|
||||
optional bool disable_analog_output = 4 [default = false];
|
||||
// Optional requirement to indicate digital output is not allowed.
|
||||
optional bool disable_digital_output = 5 [default = false];
|
||||
}
|
||||
|
||||
message VideoResolutionConstraint {
|
||||
// Minimum and maximum video resolutions in the range (height x width).
|
||||
optional uint32 min_resolution_pixels = 1;
|
||||
optional uint32 max_resolution_pixels = 2;
|
||||
// Optional output protection requirements for this range. If not
|
||||
// specified, the OutputProtection in the KeyContainer applies.
|
||||
optional OutputProtection required_protection = 3;
|
||||
}
|
||||
|
||||
message OperatorSessionKeyPermissions {
|
||||
// Permissions/key usage flags for operator service keys
|
||||
// (type = OPERATOR_SESSION).
|
||||
optional bool allow_encrypt = 1 [default = false];
|
||||
optional bool allow_decrypt = 2 [default = false];
|
||||
optional bool allow_sign = 3 [default = false];
|
||||
optional bool allow_signature_verify = 4 [default = false];
|
||||
}
|
||||
|
||||
optional bytes id = 1;
|
||||
optional bytes iv = 2;
|
||||
optional bytes key = 3;
|
||||
optional KeyType type = 4;
|
||||
optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO];
|
||||
optional OutputProtection required_protection = 6;
|
||||
// NOTE: Use of requested_protection is not recommended as it is only
|
||||
// supported on a small number of platforms.
|
||||
optional OutputProtection requested_protection = 7;
|
||||
optional KeyControl key_control = 8;
|
||||
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
|
||||
// Optional video resolution constraints. If the video resolution of the
|
||||
// content being decrypted/decoded falls within one of the specified ranges,
|
||||
// the optional required_protections may be applied. Otherwise an error will
|
||||
// be reported.
|
||||
// NOTE: Use of this feature is not recommended, as it is only supported on
|
||||
// a small number of platforms.
|
||||
repeated VideoResolutionConstraint video_resolution_constraints = 10;
|
||||
// Optional flag to indicate the key must only be used if the client
|
||||
// supports anti rollback of the user table. Content provider can query the
|
||||
// client capabilities to determine if the client support this feature.
|
||||
optional bool anti_rollback_usage_table = 11 [default = false];
|
||||
// Optional not limited to commonly known track types such as SD, HD.
|
||||
// It can be some provider defined label to identify the track.
|
||||
optional string track_label = 12;
|
||||
}
|
||||
|
||||
optional LicenseIdentification id = 1;
|
||||
optional Policy policy = 2;
|
||||
repeated KeyContainer key = 3;
|
||||
// Time of the request in seconds (UTC) as set in
|
||||
// LicenseRequest.request_time. If this time is not set in the request,
|
||||
// the local time at the license service is used in this field.
|
||||
optional int64 license_start_time = 4;
|
||||
optional bool remote_attestation_verified = 5 [default = false];
|
||||
// Client token generated by the content provider. Optional.
|
||||
optional bytes provider_client_token = 6;
|
||||
// 4cc code specifying the CENC protection scheme as defined in the CENC 3.0
|
||||
// specification. Propagated from Widevine PSSH box. Optional.
|
||||
optional uint32 protection_scheme = 7;
|
||||
// 8 byte verification field "HDCPDATA" followed by unsigned 32 bit minimum
|
||||
// HDCP SRM version (whether the version is for HDCP1 SRM or HDCP2 SRM
|
||||
// depends on client max_hdcp_version).
|
||||
// Additional details can be found in Widevine Modular DRM Security
|
||||
// Integration Guide for CENC.
|
||||
optional bytes srm_requirement = 8;
|
||||
// If present this contains a signed SRM file (either HDCP1 SRM or HDCP2 SRM
|
||||
// depending on client max_hdcp_version) that should be installed on the
|
||||
// client device.
|
||||
optional bytes srm_update = 9;
|
||||
// Indicates the status of any type of platform verification performed by the
|
||||
// server.
|
||||
optional PlatformVerificationStatus platform_verification_status = 10
|
||||
[default = PLATFORM_NO_VERIFICATION];
|
||||
// IDs of the groups for which keys are delivered in this license, if any.
|
||||
repeated bytes group_ids = 11;
|
||||
}
|
||||
|
||||
enum ProtocolVersion {
|
||||
VERSION_2_0 = 20;
|
||||
VERSION_2_1 = 21;
|
||||
VERSION_2_2 = 22;
|
||||
}
|
||||
|
||||
message LicenseRequest {
|
||||
message ContentIdentification {
|
||||
message WidevinePsshData {
|
||||
repeated bytes pssh_data = 1;
|
||||
optional LicenseType license_type = 2;
|
||||
optional bytes request_id = 3; // Opaque, client-specified.
|
||||
}
|
||||
|
||||
message WebmKeyId {
|
||||
optional bytes header = 1;
|
||||
optional LicenseType license_type = 2;
|
||||
optional bytes request_id = 3; // Opaque, client-specified.
|
||||
}
|
||||
|
||||
message ExistingLicense {
|
||||
optional LicenseIdentification license_id = 1;
|
||||
optional int64 seconds_since_started = 2;
|
||||
optional int64 seconds_since_last_played = 3;
|
||||
optional bytes session_usage_table_entry = 4;
|
||||
}
|
||||
|
||||
message InitData {
|
||||
enum InitDataType {
|
||||
CENC = 1;
|
||||
WEBM = 2;
|
||||
}
|
||||
|
||||
optional InitDataType init_data_type = 1 [default = CENC];
|
||||
optional bytes init_data = 2;
|
||||
optional LicenseType license_type = 3;
|
||||
optional bytes request_id = 4;
|
||||
}
|
||||
|
||||
oneof content_id_variant {
|
||||
// Exactly one of these must be present.
|
||||
WidevinePsshData widevine_pssh_data = 1;
|
||||
WebmKeyId webm_key_id = 2;
|
||||
ExistingLicense existing_license = 3;
|
||||
InitData init_data = 4;
|
||||
}
|
||||
}
|
||||
|
||||
enum RequestType {
|
||||
NEW = 1;
|
||||
RENEWAL = 2;
|
||||
RELEASE = 3;
|
||||
}
|
||||
|
||||
// The client_id provides information authenticating the calling device. It
|
||||
// contains the Widevine keybox token that was installed on the device at the
|
||||
// factory. This field or encrypted_client_id below is required for a valid
|
||||
// license request, but both should never be present in the same request.
|
||||
optional ClientIdentification client_id = 1;
|
||||
optional ContentIdentification content_id = 2;
|
||||
optional RequestType type = 3;
|
||||
// Time of the request in seconds (UTC) as set by the client.
|
||||
optional int64 request_time = 4;
|
||||
// Old-style decimal-encoded string key control nonce.
|
||||
optional bytes key_control_nonce_deprecated = 5;
|
||||
optional ProtocolVersion protocol_version = 6 [default = VERSION_2_0];
|
||||
// New-style uint32 key control nonce, please use instead of
|
||||
// key_control_nonce_deprecated.
|
||||
optional uint32 key_control_nonce = 7;
|
||||
// Encrypted ClientIdentification message, used for privacy purposes.
|
||||
optional EncryptedClientIdentification encrypted_client_id = 8;
|
||||
}
|
||||
|
||||
message MetricData {
|
||||
enum MetricType {
|
||||
// The time spent in the 'stage', specified in microseconds.
|
||||
LATENCY = 1;
|
||||
// The UNIX epoch timestamp at which the 'stage' was first accessed in
|
||||
// microseconds.
|
||||
TIMESTAMP = 2;
|
||||
}
|
||||
|
||||
message TypeValue {
|
||||
optional MetricType type = 1;
|
||||
// The value associated with 'type'. For example if type == LATENCY, the
|
||||
// value would be the time in microseconds spent in this 'stage'.
|
||||
optional int64 value = 2 [default = 0];
|
||||
}
|
||||
|
||||
// 'stage' that is currently processing the SignedMessage. Required.
|
||||
optional string stage_name = 1;
|
||||
// metric and associated value.
|
||||
repeated TypeValue metric_data = 2;
|
||||
}
|
||||
|
||||
message VersionInfo {
|
||||
// License SDK version reported by the Widevine License SDK. This field
|
||||
// is populated automatically by the SDK.
|
||||
optional string license_sdk_version = 1;
|
||||
// Version of the service hosting the license SDK. This field is optional.
|
||||
// It may be provided by the hosting service.
|
||||
optional string license_service_version = 2;
|
||||
}
|
||||
|
||||
message SignedMessage {
|
||||
enum MessageType {
|
||||
LICENSE_REQUEST = 1;
|
||||
LICENSE = 2;
|
||||
ERROR_RESPONSE = 3;
|
||||
SERVICE_CERTIFICATE_REQUEST = 4;
|
||||
SERVICE_CERTIFICATE = 5;
|
||||
SUB_LICENSE = 6;
|
||||
CAS_LICENSE_REQUEST = 7;
|
||||
CAS_LICENSE = 8;
|
||||
EXTERNAL_LICENSE_REQUEST = 9;
|
||||
EXTERNAL_LICENSE = 10;
|
||||
}
|
||||
|
||||
enum SessionKeyType {
|
||||
UNDEFINED = 0;
|
||||
WRAPPED_AES_KEY = 1;
|
||||
EPHERMERAL_ECC_PUBLIC_KEY = 2;
|
||||
}
|
||||
optional MessageType type = 1;
|
||||
optional bytes msg = 2;
|
||||
// Required field that contains the signature of the bytes of msg.
|
||||
// For license requests, the signing algorithm is determined by the
|
||||
// certificate contained in the request.
|
||||
// For license responses, the signing algorithm is HMAC with signing key based
|
||||
// on |session_key|.
|
||||
optional bytes signature = 3;
|
||||
// If populated, the contents of this field will be signaled by the
|
||||
// |session_key_type| type. If the |session_key_type| is WRAPPED_AES_KEY the
|
||||
// key is the bytes of an encrypted AES key. If the |session_key_type| is
|
||||
// EPHERMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1
|
||||
// serialized ECC public key.
|
||||
optional bytes session_key = 4;
|
||||
// Remote attestation data which will be present in the initial license
|
||||
// request for ChromeOS client devices operating in verified mode. Remote
|
||||
// attestation challenge data is |msg| field above. Optional.
|
||||
optional bytes remote_attestation = 5;
|
||||
|
||||
repeated MetricData metric_data = 6;
|
||||
// Version information from the SDK and license service. This information is
|
||||
// provided in the license response.
|
||||
optional VersionInfo service_version_info = 7;
|
||||
// Optional field that contains the algorithm type used to generate the
|
||||
// session_key and signature in a LICENSE message.
|
||||
optional SessionKeyType session_key_type = 8 [default = WRAPPED_AES_KEY];
|
||||
// The core message is the simple serialization of fields used by OEMCrypto.
|
||||
// This field was introduced in OEMCrypto API v16.
|
||||
optional bytes oemcrypto_core_message = 9;
|
||||
}
|
||||
|
||||
enum HashAlgorithmProto {
|
||||
// Unspecified hash algorithm: SHA_256 shall be used for ECC based algorithms
|
||||
// and SHA_1 shall be used otherwise.
|
||||
HASH_ALGORITHM_UNSPECIFIED = 0;
|
||||
HASH_ALGORITHM_SHA_1 = 1;
|
||||
HASH_ALGORITHM_SHA_256 = 2;
|
||||
HASH_ALGORITHM_SHA_384 = 3;
|
||||
}
|
||||
|
||||
// ClientIdentification message used to authenticate the client device.
|
||||
message ClientIdentification {
|
||||
enum TokenType {
|
||||
KEYBOX = 0;
|
||||
DRM_DEVICE_CERTIFICATE = 1;
|
||||
REMOTE_ATTESTATION_CERTIFICATE = 2;
|
||||
OEM_DEVICE_CERTIFICATE = 3;
|
||||
}
|
||||
|
||||
message NameValue {
|
||||
optional string name = 1;
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
// Capabilities which not all clients may support. Used for the license
|
||||
// exchange protocol only.
|
||||
message ClientCapabilities {
|
||||
enum HdcpVersion {
|
||||
HDCP_NONE = 0;
|
||||
HDCP_V1 = 1;
|
||||
HDCP_V2 = 2;
|
||||
HDCP_V2_1 = 3;
|
||||
HDCP_V2_2 = 4;
|
||||
HDCP_V2_3 = 5;
|
||||
HDCP_NO_DIGITAL_OUTPUT = 0xff;
|
||||
}
|
||||
|
||||
enum CertificateKeyType {
|
||||
RSA_2048 = 0;
|
||||
RSA_3072 = 1;
|
||||
ECC_SECP256R1 = 2;
|
||||
ECC_SECP384R1 = 3;
|
||||
ECC_SECP521R1 = 4;
|
||||
}
|
||||
|
||||
enum AnalogOutputCapabilities {
|
||||
ANALOG_OUTPUT_UNKNOWN = 0;
|
||||
ANALOG_OUTPUT_NONE = 1;
|
||||
ANALOG_OUTPUT_SUPPORTED = 2;
|
||||
ANALOG_OUTPUT_SUPPORTS_CGMS_A = 3;
|
||||
}
|
||||
|
||||
optional bool client_token = 1 [default = false];
|
||||
optional bool session_token = 2 [default = false];
|
||||
optional bool video_resolution_constraints = 3 [default = false];
|
||||
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
|
||||
optional uint32 oem_crypto_api_version = 5;
|
||||
// Client has hardware support for protecting the usage table, such as
|
||||
// storing the generation number in secure memory. For Details, see:
|
||||
// Widevine Modular DRM Security Integration Guide for CENC
|
||||
optional bool anti_rollback_usage_table = 6 [default = false];
|
||||
// The client shall report |srm_version| if available.
|
||||
optional uint32 srm_version = 7;
|
||||
// A device may have SRM data, and report a version, but may not be capable
|
||||
// of updating SRM data.
|
||||
optional bool can_update_srm = 8 [default = false];
|
||||
repeated CertificateKeyType supported_certificate_key_type = 9;
|
||||
optional AnalogOutputCapabilities analog_output_capabilities = 10
|
||||
[default = ANALOG_OUTPUT_UNKNOWN];
|
||||
optional bool can_disable_analog_output = 11 [default = false];
|
||||
// Clients can indicate a performance level supported by OEMCrypto.
|
||||
// This will allow applications and providers to choose an appropriate
|
||||
// quality of content to serve. Currently defined tiers are
|
||||
// 1 (low), 2 (medium) and 3 (high). Any other value indicates that
|
||||
// the resource rating is unavailable or reporting erroneous values
|
||||
// for that device. For details see,
|
||||
// Widevine Modular DRM Security Integration Guide for CENC
|
||||
optional uint32 resource_rating_tier = 12 [default = 0];
|
||||
}
|
||||
|
||||
message ClientCredentials {
|
||||
optional TokenType type = 1 [default = KEYBOX];
|
||||
optional bytes token = 2;
|
||||
}
|
||||
|
||||
// Type of factory-provisioned device root of trust. Optional.
|
||||
optional TokenType type = 1 [default = KEYBOX];
|
||||
// Factory-provisioned device root of trust. Required.
|
||||
optional bytes token = 2;
|
||||
// Optional client information name/value pairs.
|
||||
repeated NameValue client_info = 3;
|
||||
// Client token generated by the content provider. Optional.
|
||||
optional bytes provider_client_token = 4;
|
||||
// Number of licenses received by the client to which the token above belongs.
|
||||
// Only present if client_token is specified.
|
||||
optional uint32 license_counter = 5;
|
||||
// List of non-baseline client capabilities.
|
||||
optional ClientCapabilities client_capabilities = 6;
|
||||
// Serialized VmpData message. Optional.
|
||||
optional bytes vmp_data = 7;
|
||||
// Optional field that may contain additional provisioning credentials.
|
||||
repeated ClientCredentials device_credentials = 8;
|
||||
}
|
||||
|
||||
// EncryptedClientIdentification message used to hold ClientIdentification
|
||||
// messages encrypted for privacy purposes.
|
||||
message EncryptedClientIdentification {
|
||||
// Provider ID for which the ClientIdentifcation is encrypted (owner of
|
||||
// service certificate).
|
||||
optional string provider_id = 1;
|
||||
// Serial number for the service certificate for which ClientIdentification is
|
||||
// encrypted.
|
||||
optional bytes service_certificate_serial_number = 2;
|
||||
// Serialized ClientIdentification message, encrypted with the privacy key
|
||||
// using AES-128-CBC with PKCS#5 padding.
|
||||
optional bytes encrypted_client_id = 3;
|
||||
// Initialization vector needed to decrypt encrypted_client_id.
|
||||
optional bytes encrypted_client_id_iv = 4;
|
||||
// AES-128 privacy key, encrypted with the service public key using RSA-OAEP.
|
||||
optional bytes encrypted_privacy_key = 5;
|
||||
}
|
||||
|
||||
// DRM certificate definition for user devices, intermediate, service, and root
|
||||
// certificates.
|
||||
message DrmCertificate {
|
||||
enum Type {
|
||||
ROOT = 0; // ProtoBestPractices: ignore.
|
||||
DEVICE_MODEL = 1;
|
||||
DEVICE = 2;
|
||||
SERVICE = 3;
|
||||
PROVISIONER = 4;
|
||||
}
|
||||
enum ServiceType {
|
||||
UNKNOWN_SERVICE_TYPE = 0;
|
||||
LICENSE_SERVER_SDK = 1;
|
||||
LICENSE_SERVER_PROXY_SDK = 2;
|
||||
PROVISIONING_SDK = 3;
|
||||
CAS_PROXY_SDK = 4;
|
||||
}
|
||||
enum Algorithm {
|
||||
UNKNOWN_ALGORITHM = 0;
|
||||
RSA = 1;
|
||||
ECC_SECP256R1 = 2;
|
||||
ECC_SECP384R1 = 3;
|
||||
ECC_SECP521R1 = 4;
|
||||
}
|
||||
|
||||
message EncryptionKey {
|
||||
// Device public key. PKCS#1 ASN.1 DER-encoded. Required.
|
||||
optional bytes public_key = 1;
|
||||
// Required. The algorithm field contains the curve used to create the
|
||||
// |public_key| if algorithm is one of the ECC types.
|
||||
// The |algorithm| is used for both to determine the if the certificate is
|
||||
// ECC or RSA. The |algorithm| also specifies the parameters that were used
|
||||
// to create |public_key| and are used to create an ephemeral session key.
|
||||
optional Algorithm algorithm = 2 [default = RSA];
|
||||
}
|
||||
|
||||
// Type of certificate. Required.
|
||||
optional Type type = 1;
|
||||
// 128-bit globally unique serial number of certificate.
|
||||
// Value is 0 for root certificate. Required.
|
||||
optional bytes serial_number = 2;
|
||||
// POSIX time, in seconds, when the certificate was created. Required.
|
||||
optional uint32 creation_time_seconds = 3;
|
||||
// POSIX time, in seconds, when the certificate should expire. Value of zero
|
||||
// denotes indefinite expiry time. For more information on limited lifespan
|
||||
// DRM certificates see (go/limited-lifespan-drm-certificates).
|
||||
optional uint32 expiration_time_seconds = 12;
|
||||
// Device public key. PKCS#1 ASN.1 DER-encoded. Required.
|
||||
optional bytes public_key = 4;
|
||||
// Widevine system ID for the device. Required for intermediate and
|
||||
// user device certificates.
|
||||
optional uint32 system_id = 5;
|
||||
// Deprecated field, which used to indicate whether the device was a test
|
||||
// (non-production) device. The test_device field in ProvisionedDeviceInfo
|
||||
// below should be observed instead.
|
||||
optional bool test_device_deprecated = 6 [deprecated = true];
|
||||
// Service identifier (web origin) for the provider which owns the
|
||||
// certificate. Required for service and provisioner certificates.
|
||||
optional string provider_id = 7;
|
||||
// This field is used only when type = SERVICE to specify which SDK uses
|
||||
// service certificate. This repeated field is treated as a set. A certificate
|
||||
// may be used for the specified service SDK if the appropriate ServiceType
|
||||
// is specified in this field.
|
||||
repeated ServiceType service_types = 8;
|
||||
// Required. The algorithm field contains the curve used to create the
|
||||
// |public_key| if algorithm is one of the ECC types.
|
||||
// The |algorithm| is used for both to determine the if the certificate is ECC
|
||||
// or RSA. The |algorithm| also specifies the parameters that were used to
|
||||
// create |public_key| and are used to create an ephemeral session key.
|
||||
optional Algorithm algorithm = 9 [default = RSA];
|
||||
// Optional. May be present in DEVICE certificate types. This is the root
|
||||
// of trust identifier that holds an encrypted value that identifies the
|
||||
// keybox or other root of trust that was used to provision a DEVICE drm
|
||||
// certificate.
|
||||
optional bytes rot_id = 10;
|
||||
// Optional. May be present in devices that explicitly support dual keys. When
|
||||
// present the |public_key| is used for verification of received license
|
||||
// request messages.
|
||||
optional EncryptionKey encryption_key = 11;
|
||||
}
|
||||
|
||||
// DrmCertificate signed by a higher (CA) DRM certificate.
|
||||
message SignedDrmCertificate {
|
||||
// Serialized certificate. Required.
|
||||
optional bytes drm_certificate = 1;
|
||||
// Signature of certificate. Signed with root or intermediate
|
||||
// certificate specified below. Required.
|
||||
optional bytes signature = 2;
|
||||
// SignedDrmCertificate used to sign this certificate.
|
||||
optional SignedDrmCertificate signer = 3;
|
||||
// Optional field that indicates the hash algorithm used in signature scheme.
|
||||
optional HashAlgorithmProto hash_algorithm = 4;
|
||||
}
|
||||
|
||||
message WidevinePsshData {
|
||||
enum Type {
|
||||
SINGLE = 0; // Single PSSH to be used to retrieve content keys.
|
||||
ENTITLEMENT = 1; // Primary PSSH used to retrieve entitlement keys.
|
||||
ENTITLED_KEY = 2; // Secondary PSSH containing entitled key(s).
|
||||
}
|
||||
|
||||
message EntitledKey {
|
||||
// ID of entitlement key used for wrapping |key|.
|
||||
optional bytes entitlement_key_id = 1;
|
||||
// ID of the entitled key.
|
||||
optional bytes key_id = 2;
|
||||
// Wrapped key. Required.
|
||||
optional bytes key = 3;
|
||||
// IV used for wrapping |key|. Required.
|
||||
optional bytes iv = 4;
|
||||
// Size of entitlement key used for wrapping |key|.
|
||||
optional uint32 entitlement_key_size_bytes = 5 [default = 32];
|
||||
}
|
||||
|
||||
// Entitlement or content key IDs. Can onnly present in SINGLE or ENTITLEMENT
|
||||
// PSSHs. May be repeated to facilitate delivery of multiple keys in a
|
||||
// single license. Cannot be used in conjunction with content_id or
|
||||
// group_ids, which are the preferred mechanism.
|
||||
repeated bytes key_ids = 2;
|
||||
|
||||
// Content identifier which may map to multiple entitlement or content key
|
||||
// IDs to facilitate the delivery of multiple keys in a single license.
|
||||
// Cannot be present in conjunction with key_ids, but if used must be in all
|
||||
// PSSHs.
|
||||
optional bytes content_id = 4;
|
||||
|
||||
// Crypto period index, for media using key rotation. Always corresponds to
|
||||
// The content key period. This means that if using entitlement licensing
|
||||
// the ENTITLED_KEY PSSHs will have sequential crypto_period_index's, whereas
|
||||
// the ENTITELEMENT PSSHs will have gaps in the sequence. Required if doing
|
||||
// key rotation.
|
||||
optional uint32 crypto_period_index = 7;
|
||||
|
||||
// Protection scheme identifying the encryption algorithm. The protection
|
||||
// scheme is represented as a uint32 value. The uint32 contains 4 bytes each
|
||||
// representing a single ascii character in one of the 4CC protection scheme
|
||||
// values. To be deprecated in favor of signaling from content.
|
||||
// 'cenc' (AES-CTR) protection_scheme = 0x63656E63,
|
||||
// 'cbc1' (AES-CBC) protection_scheme = 0x63626331,
|
||||
// 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73,
|
||||
// 'cbcs' (AES-CBC pattern encryption) protection_scheme = 0x63626373.
|
||||
optional uint32 protection_scheme = 9;
|
||||
|
||||
// Optional. For media using key rotation, this represents the duration
|
||||
// of each crypto period in seconds.
|
||||
optional uint32 crypto_period_seconds = 10;
|
||||
|
||||
// Type of PSSH. Required if not SINGLE.
|
||||
optional Type type = 11 [default = SINGLE];
|
||||
|
||||
// Key sequence for Widevine-managed keys. Optional.
|
||||
optional uint32 key_sequence = 12;
|
||||
|
||||
// Group identifiers for all groups to which the content belongs. This can
|
||||
// be used to deliver licenses to unlock multiple titles / channels.
|
||||
// Optional, and may only be present in ENTITLEMENT and ENTITLED_KEY PSSHs, and
|
||||
// not in conjunction with key_ids.
|
||||
repeated bytes group_ids = 13;
|
||||
|
||||
// Copy/copies of the content key used to decrypt the media stream in which
|
||||
// the PSSH box is embedded, each wrapped with a different entitlement key.
|
||||
// May also contain sub-licenses to support devices with OEMCrypto 13 or
|
||||
// older. May be repeated if using group entitlement keys. Present only in
|
||||
// PSSHs of type ENTITLED_KEY.
|
||||
repeated EntitledKey entitled_keys = 14;
|
||||
|
||||
// Video feature identifier, which is used in conjunction with |content_id|
|
||||
// to determine the set of keys to be returned in the license. Cannot be
|
||||
// present in conjunction with |key_ids|.
|
||||
// Current values are "HDR".
|
||||
optional string video_feature = 15;
|
||||
|
||||
//////////////////////////// Deprecated Fields ////////////////////////////
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
AESCTR = 1;
|
||||
};
|
||||
optional Algorithm algorithm = 1 [deprecated = true];
|
||||
|
||||
// Content provider name.
|
||||
optional string provider = 3 [deprecated = true];
|
||||
|
||||
// Track type. Acceptable values are SD, HD and AUDIO. Used to
|
||||
// differentiate content keys used by an asset.
|
||||
optional string track_type = 5 [deprecated = true];
|
||||
|
||||
// The name of a registered policy to be used for this asset.
|
||||
optional string policy = 6 [deprecated = true];
|
||||
|
||||
// Optional protected context for group content. The grouped_license is a
|
||||
// serialized SignedMessage.
|
||||
optional bytes grouped_license = 8 [deprecated = true];
|
||||
}
|
||||
|
||||
// File Hashes for Verified Media Path (VMP) support.
|
||||
message FileHashes {
|
||||
message Signature {
|
||||
optional string filename = 1;
|
||||
optional bool test_signing = 2; //0 - release, 1 - testing
|
||||
optional bytes SHA512Hash = 3;
|
||||
optional bool main_exe = 4; //0 for dlls, 1 for exe, this is field 3 in file
|
||||
optional bytes signature = 5;
|
||||
}
|
||||
optional bytes signer = 1;
|
||||
repeated Signature signatures = 2;
|
||||
}
|
||||
4996
modules/license_protocol.ts
Normal file
4996
modules/license_protocol.ts
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -82,7 +82,8 @@ export type ConfigObject = {
|
|||
bin: {
|
||||
ffmpeg?: string,
|
||||
mkvmerge?: string,
|
||||
ffprobe?: string
|
||||
ffprobe?: string,
|
||||
mp4decrypt?: string
|
||||
},
|
||||
cli: {
|
||||
[key: string]: any
|
||||
|
|
@ -146,7 +147,8 @@ const loadBinCfg = async () => {
|
|||
const defaultBin = {
|
||||
ffmpeg: 'ffmpeg',
|
||||
mkvmerge: 'mkvmerge',
|
||||
ffprobe: 'ffprobe'
|
||||
ffprobe: 'ffprobe',
|
||||
mp4decrypt: 'mp4decrypt'
|
||||
};
|
||||
const keys = Object.keys(defaultBin) as (keyof typeof defaultBin)[];
|
||||
for(const dir of keys){
|
||||
|
|
|
|||
|
|
@ -56,10 +56,12 @@
|
|||
"got": "^11.8.6",
|
||||
"iso-639": "^0.2.2",
|
||||
"log4js": "^6.9.1",
|
||||
"long": "^5.2.3",
|
||||
"lookpath": "^1.2.2",
|
||||
"m3u8-parsed": "^1.3.0",
|
||||
"mpd-parser": "^1.3.0",
|
||||
"open": "^8.4.2",
|
||||
"protobufjs": "^7.2.5",
|
||||
"sei-helper": "^3.3.0",
|
||||
"typescript-eslint": "0.0.1-alpha.0",
|
||||
"ws": "^8.13.0",
|
||||
|
|
@ -70,7 +72,6 @@
|
|||
"@types/cors": "^2.8.13",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/ffprobe": "^1.1.4",
|
||||
"@types/ffprobe-static": "^2.0.1",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/ws": "^8.5.5",
|
||||
|
|
@ -83,6 +84,7 @@
|
|||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-react": "7.32.2",
|
||||
"pkg": "^5.8.1",
|
||||
"protoc": "^1.1.3",
|
||||
"removeNPMAbsolutePaths": "^3.0.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "5.1.6"
|
||||
|
|
|
|||
290
pnpm-lock.yaml
290
pnpm-lock.yaml
|
|
@ -47,6 +47,9 @@ dependencies:
|
|||
log4js:
|
||||
specifier: ^6.9.1
|
||||
version: 6.9.1
|
||||
long:
|
||||
specifier: ^5.2.3
|
||||
version: 5.2.3
|
||||
lookpath:
|
||||
specifier: ^1.2.2
|
||||
version: 1.2.2
|
||||
|
|
@ -59,6 +62,9 @@ dependencies:
|
|||
open:
|
||||
specifier: ^8.4.2
|
||||
version: 8.4.2
|
||||
protobufjs:
|
||||
specifier: ^7.2.5
|
||||
version: 7.2.5
|
||||
sei-helper:
|
||||
specifier: ^3.3.0
|
||||
version: 3.3.0
|
||||
|
|
@ -85,9 +91,6 @@ devDependencies:
|
|||
'@types/ffprobe':
|
||||
specifier: ^1.1.4
|
||||
version: 1.1.4
|
||||
'@types/ffprobe-static':
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
'@types/fs-extra':
|
||||
specifier: ^11.0.1
|
||||
version: 11.0.1
|
||||
|
|
@ -121,6 +124,9 @@ devDependencies:
|
|||
pkg:
|
||||
specifier: ^5.8.1
|
||||
version: 5.8.1
|
||||
protoc:
|
||||
specifier: ^1.1.3
|
||||
version: 1.1.3
|
||||
removeNPMAbsolutePaths:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
|
|
@ -1780,6 +1786,49 @@ packages:
|
|||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.15.0
|
||||
|
||||
/@protobufjs/aspromise@1.1.2:
|
||||
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/base64@1.1.2:
|
||||
resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/codegen@2.0.4:
|
||||
resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/eventemitter@1.1.0:
|
||||
resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/fetch@1.1.0:
|
||||
resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
|
||||
dependencies:
|
||||
'@protobufjs/aspromise': 1.1.2
|
||||
'@protobufjs/inquire': 1.1.0
|
||||
dev: false
|
||||
|
||||
/@protobufjs/float@1.0.2:
|
||||
resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/inquire@1.1.0:
|
||||
resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/path@1.1.2:
|
||||
resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/pool@1.1.0:
|
||||
resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
|
||||
dev: false
|
||||
|
||||
/@protobufjs/utf8@1.1.0:
|
||||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
||||
dev: false
|
||||
|
||||
/@rushstack/eslint-patch@1.2.0:
|
||||
resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==}
|
||||
dev: true
|
||||
|
|
@ -1857,10 +1906,6 @@ packages:
|
|||
'@types/serve-static': 1.15.1
|
||||
dev: true
|
||||
|
||||
/@types/ffprobe-static@2.0.1:
|
||||
resolution: {integrity: sha512-V5CrKUfms0lBGSXliKmKzSFFZWgJusQks1YfjRI/+2dXFF+aK7qBAarCe/ryYHQI44jYQX7xtlgH0fCuJepuGQ==}
|
||||
dev: true
|
||||
|
||||
/@types/ffprobe@1.1.4:
|
||||
resolution: {integrity: sha512-gtfU+bD4FDoF1S2ybmIWEIz0K5ijeHpi+CgJUtXl3FTGnf+61HmsZksqDn8M9M9lRJU9SRyMLt5yrIwUSep4Uw==}
|
||||
dev: true
|
||||
|
|
@ -1968,7 +2013,7 @@ packages:
|
|||
grapheme-splitter: 1.0.4
|
||||
ignore: 5.2.4
|
||||
natural-compare-lite: 1.4.0
|
||||
semver: 7.3.8
|
||||
semver: 7.5.4
|
||||
tsutils: 3.21.0(typescript@5.1.6)
|
||||
typescript: 5.1.6
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -2137,7 +2182,7 @@ packages:
|
|||
debug: 4.3.4
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
semver: 7.3.8
|
||||
semver: 7.5.4
|
||||
tsutils: 3.21.0(typescript@5.1.6)
|
||||
typescript: 5.1.6
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -2178,7 +2223,7 @@ packages:
|
|||
'@typescript-eslint/typescript-estree': 5.57.1(typescript@5.1.6)
|
||||
eslint: 8.45.0
|
||||
eslint-scope: 5.1.1
|
||||
semver: 7.3.8
|
||||
semver: 7.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
|
@ -2492,6 +2537,18 @@ packages:
|
|||
/base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
/big-integer@1.6.52:
|
||||
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
|
||||
engines: {node: '>=0.6'}
|
||||
dev: true
|
||||
|
||||
/binary@0.3.0:
|
||||
resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==}
|
||||
dependencies:
|
||||
buffers: 0.1.1
|
||||
chainsaw: 0.1.0
|
||||
dev: true
|
||||
|
||||
/bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
dependencies:
|
||||
|
|
@ -2499,6 +2556,10 @@ packages:
|
|||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
|
||||
/bluebird@3.4.7:
|
||||
resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==}
|
||||
dev: true
|
||||
|
||||
/body-parser@1.20.1:
|
||||
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
|
|
@ -2556,12 +2617,22 @@ packages:
|
|||
node-releases: 2.0.13
|
||||
update-browserslist-db: 1.0.11(browserslist@4.21.9)
|
||||
|
||||
/buffer-indexof-polyfill@1.0.2:
|
||||
resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==}
|
||||
engines: {node: '>=0.10'}
|
||||
dev: true
|
||||
|
||||
/buffer@5.7.1:
|
||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
/buffers@0.1.1:
|
||||
resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==}
|
||||
engines: {node: '>=0.2.0'}
|
||||
dev: true
|
||||
|
||||
/bytes@3.1.2:
|
||||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
|
@ -2602,6 +2673,12 @@ packages:
|
|||
/caniuse-lite@1.0.30001516:
|
||||
resolution: {integrity: sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==}
|
||||
|
||||
/chainsaw@0.1.0:
|
||||
resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==}
|
||||
dependencies:
|
||||
traverse: 0.3.9
|
||||
dev: true
|
||||
|
||||
/chalk@2.4.2:
|
||||
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -2662,12 +2739,34 @@ packages:
|
|||
wrap-ansi: 7.0.0
|
||||
dev: false
|
||||
|
||||
/clone-buffer@1.0.0:
|
||||
resolution: {integrity: sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==}
|
||||
engines: {node: '>= 0.10'}
|
||||
dev: true
|
||||
|
||||
/clone-response@1.0.3:
|
||||
resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
|
||||
dependencies:
|
||||
mimic-response: 1.0.1
|
||||
dev: false
|
||||
|
||||
/clone-stats@1.0.0:
|
||||
resolution: {integrity: sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==}
|
||||
dev: true
|
||||
|
||||
/clone@2.1.2:
|
||||
resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: true
|
||||
|
||||
/cloneable-readable@1.1.3:
|
||||
resolution: {integrity: sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==}
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
process-nextick-args: 2.0.1
|
||||
readable-stream: 2.3.8
|
||||
dev: true
|
||||
|
||||
/color-convert@1.9.3:
|
||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
dependencies:
|
||||
|
|
@ -2783,6 +2882,11 @@ packages:
|
|||
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
||||
dev: true
|
||||
|
||||
/data-uri-to-buffer@4.0.1:
|
||||
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
|
||||
engines: {node: '>= 12'}
|
||||
dev: true
|
||||
|
||||
/date-format@4.0.14:
|
||||
resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==}
|
||||
engines: {node: '>=4.0'}
|
||||
|
|
@ -2958,6 +3062,12 @@ packages:
|
|||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/duplexer2@0.1.4:
|
||||
resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
|
||||
dependencies:
|
||||
readable-stream: 2.3.8
|
||||
dev: true
|
||||
|
||||
/ee-first@1.1.1:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
dev: false
|
||||
|
|
@ -3537,6 +3647,14 @@ packages:
|
|||
dependencies:
|
||||
reusify: 1.0.4
|
||||
|
||||
/fetch-blob@3.2.0:
|
||||
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||
engines: {node: ^12.20 || >= 14.13}
|
||||
dependencies:
|
||||
node-domexception: 1.0.0
|
||||
web-streams-polyfill: 3.2.1
|
||||
dev: true
|
||||
|
||||
/ffprobe@1.1.2:
|
||||
resolution: {integrity: sha512-a+oTbhyeM7Z8PRy+mpzmVUAnATZT7z4BO94HSKeqHupdmjiKZ1djzcZkyoyXA21zCOCG7oVRrsBMmvvtmzoz4g==}
|
||||
dependencies:
|
||||
|
|
@ -3603,6 +3721,13 @@ packages:
|
|||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/formdata-polyfill@4.0.10:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
dev: true
|
||||
|
||||
/forwarded@0.2.0:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
|
@ -3655,6 +3780,16 @@ packages:
|
|||
/fs.realpath@1.0.0:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
|
||||
/fstream@1.0.12:
|
||||
resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==}
|
||||
engines: {node: '>=0.6'}
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
inherits: 2.0.4
|
||||
mkdirp: 0.5.6
|
||||
rimraf: 2.7.1
|
||||
dev: true
|
||||
|
||||
/function-bind@1.1.1:
|
||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||
|
||||
|
|
@ -4195,6 +4330,10 @@ packages:
|
|||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
dev: true
|
||||
|
||||
/listenercount@1.0.1:
|
||||
resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==}
|
||||
dev: true
|
||||
|
||||
/locate-path@6.0.0:
|
||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -4225,6 +4364,10 @@ packages:
|
|||
- supports-color
|
||||
dev: false
|
||||
|
||||
/long@5.2.3:
|
||||
resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==}
|
||||
dev: false
|
||||
|
||||
/lookpath@1.2.2:
|
||||
resolution: {integrity: sha512-k2Gmn8iV6qdME3ztZC2spubmQISimFOPLuQKiPaLcVdRz0IpdxrNClVepMlyTJlhodm/zG/VfbkWERm3kUIh+Q==}
|
||||
engines: {npm: '>=6.13.4'}
|
||||
|
|
@ -4342,6 +4485,13 @@ packages:
|
|||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||
dev: true
|
||||
|
||||
/mkdirp@0.5.6:
|
||||
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
minimist: 1.2.8
|
||||
dev: true
|
||||
|
||||
/mpd-parser@1.3.0:
|
||||
resolution: {integrity: sha512-WgeIwxAqkmb9uTn4ClicXpEQYCEduDqRKfmUdp4X8vmghKfBNXZLYpREn9eqrDx/Tf5LhzRcJLSpi4ohfV742Q==}
|
||||
hasBin: true
|
||||
|
|
@ -4393,7 +4543,12 @@ packages:
|
|||
resolution: {integrity: sha512-jAlSOFR1Bls963NmFwxeQkNTzqjUF0NThm8Le7eRIRGzFUVJuMOFZDLv5Y30W/Oaw+KEebEJLAigwO9gQHoEmw==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
semver: 7.3.8
|
||||
semver: 7.5.4
|
||||
dev: true
|
||||
|
||||
/node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
dev: true
|
||||
|
||||
/node-fetch@2.6.9:
|
||||
|
|
@ -4408,6 +4563,15 @@ packages:
|
|||
whatwg-url: 5.0.0
|
||||
dev: true
|
||||
|
||||
/node-fetch@3.3.2:
|
||||
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dependencies:
|
||||
data-uri-to-buffer: 4.0.1
|
||||
fetch-blob: 3.2.0
|
||||
formdata-polyfill: 4.0.10
|
||||
dev: true
|
||||
|
||||
/node-releases@2.0.10:
|
||||
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
|
||||
dev: true
|
||||
|
|
@ -4614,7 +4778,7 @@ packages:
|
|||
https-proxy-agent: 5.0.1
|
||||
node-fetch: 2.6.9
|
||||
progress: 2.0.3
|
||||
semver: 7.3.8
|
||||
semver: 7.5.4
|
||||
tar-fs: 2.1.1
|
||||
yargs: 16.2.0
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -4695,6 +4859,39 @@ packages:
|
|||
react-is: 16.13.1
|
||||
dev: true
|
||||
|
||||
/protobufjs@7.2.5:
|
||||
resolution: {integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@protobufjs/aspromise': 1.1.2
|
||||
'@protobufjs/base64': 1.1.2
|
||||
'@protobufjs/codegen': 2.0.4
|
||||
'@protobufjs/eventemitter': 1.1.0
|
||||
'@protobufjs/fetch': 1.1.0
|
||||
'@protobufjs/float': 1.0.2
|
||||
'@protobufjs/inquire': 1.1.0
|
||||
'@protobufjs/path': 1.1.2
|
||||
'@protobufjs/pool': 1.1.0
|
||||
'@protobufjs/utf8': 1.1.0
|
||||
'@types/node': 18.15.11
|
||||
long: 5.2.3
|
||||
dev: false
|
||||
|
||||
/protoc@1.1.3:
|
||||
resolution: {integrity: sha512-Vy4OBxCcF0W38YrZZRFix659gFu8ujIxVDP1SUBK9ELzyeMSBe8m8tYyYlX1PI5j9gse9hWu4c4nzQaHesAf8Q==}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
mkdirp: 0.5.6
|
||||
node-fetch: 3.3.2
|
||||
rimraf: 3.0.2
|
||||
unzipper: 0.10.14
|
||||
uuid: 9.0.1
|
||||
vinyl: 2.2.1
|
||||
dev: true
|
||||
|
||||
/proxy-addr@2.0.7:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
|
@ -4824,12 +5021,21 @@ packages:
|
|||
jsesc: 0.5.0
|
||||
dev: true
|
||||
|
||||
/remove-trailing-separator@1.1.0:
|
||||
resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==}
|
||||
dev: true
|
||||
|
||||
/removeNPMAbsolutePaths@3.0.1:
|
||||
resolution: {integrity: sha512-rJc1aHu5LT4rncs7gga/asiyQG+G1TkweO7L27D/oMBtfbHmSFSp7RgUtrfuZzk9c/4P2xHjHnD+cUNzWRFB4A==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/replace-ext@1.0.1:
|
||||
resolution: {integrity: sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==}
|
||||
engines: {node: '>= 0.10'}
|
||||
dev: true
|
||||
|
||||
/require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -4873,6 +5079,13 @@ packages:
|
|||
resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
|
||||
dev: false
|
||||
|
||||
/rimraf@2.7.1:
|
||||
resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
dev: true
|
||||
|
||||
/rimraf@3.0.2:
|
||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||
hasBin: true
|
||||
|
|
@ -4914,14 +5127,6 @@ packages:
|
|||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
|
||||
/semver@7.3.8:
|
||||
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
lru-cache: 6.0.0
|
||||
dev: true
|
||||
|
||||
/semver@7.5.4:
|
||||
resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -4962,6 +5167,10 @@ packages:
|
|||
- supports-color
|
||||
dev: false
|
||||
|
||||
/setimmediate@1.0.5:
|
||||
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
|
||||
dev: true
|
||||
|
||||
/setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
dev: false
|
||||
|
|
@ -5167,6 +5376,10 @@ packages:
|
|||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: true
|
||||
|
||||
/traverse@0.3.9:
|
||||
resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==}
|
||||
dev: true
|
||||
|
||||
/ts-api-utils@1.0.1(typescript@5.1.6):
|
||||
resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
|
||||
engines: {node: '>=16.13.0'}
|
||||
|
|
@ -5313,6 +5526,21 @@ packages:
|
|||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/unzipper@0.10.14:
|
||||
resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==}
|
||||
dependencies:
|
||||
big-integer: 1.6.52
|
||||
binary: 0.3.0
|
||||
bluebird: 3.4.7
|
||||
buffer-indexof-polyfill: 1.0.2
|
||||
duplexer2: 0.1.4
|
||||
fstream: 1.0.12
|
||||
graceful-fs: 4.2.11
|
||||
listenercount: 1.0.1
|
||||
readable-stream: 2.3.8
|
||||
setimmediate: 1.0.5
|
||||
dev: true
|
||||
|
||||
/update-browserslist-db@1.0.10(browserslist@4.21.5):
|
||||
resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==}
|
||||
hasBin: true
|
||||
|
|
@ -5351,6 +5579,11 @@ packages:
|
|||
engines: {node: '>= 0.4.0'}
|
||||
dev: false
|
||||
|
||||
/uuid@9.0.1:
|
||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/v8-compile-cache-lib@3.0.1:
|
||||
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||
dev: true
|
||||
|
|
@ -5360,6 +5593,23 @@ packages:
|
|||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/vinyl@2.2.1:
|
||||
resolution: {integrity: sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==}
|
||||
engines: {node: '>= 0.10'}
|
||||
dependencies:
|
||||
clone: 2.1.2
|
||||
clone-buffer: 1.0.0
|
||||
clone-stats: 1.0.0
|
||||
cloneable-readable: 1.1.3
|
||||
remove-trailing-separator: 1.1.0
|
||||
replace-ext: 1.0.1
|
||||
dev: true
|
||||
|
||||
/web-streams-polyfill@3.2.1:
|
||||
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
|
||||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: true
|
||||
|
|
|
|||
0
widevine/.gitkeep
Normal file
0
widevine/.gitkeep
Normal file
Loading…
Reference in a new issue