make릭ع a few f梦nctions have return types + etc

This commit is contained in:
Reid 2025-04-22 00:57:30 -07:00
parent b10801bf29
commit 237ec061d2
Signed by: reidlab
GPG key ID: DAF5EAF6665839FD
11 changed files with 98 additions and 68 deletions

View file

@ -2,11 +2,10 @@ import { LicenseType, Session } from "node-widevine";
import { env } from "../config.js";
import { appleMusicApi } from "../api/index.js";
import { dataUriToBuffer } from "data-uri-to-buffer";
import psshTools from "pssh-tools";
import * as log from "../log.js";
import fs from "node:fs";
import * as psshTools from "pssh-tools";
export async function getWidevineDecryptionKey(psshDataUri: string, trackId: string): Promise<void> {
export async function getWidevineDecryptionKey(psshDataUri: string, trackId: string): Promise<string> {
let pssh = Buffer.from(dataUriToBuffer(psshDataUri).buffer);
const privateKey = Buffer.from(env.WIDEVINE_PRIVATE_KEY, "base64");
@ -18,7 +17,7 @@ export async function getWidevineDecryptionKey(psshDataUri: string, trackId: str
challenge = session.createLicenseRequest(LicenseType.STREAMING);
} catch (err) {
// for some reason, if gotten from a webplayback manifest, the pssh is in a completely different format
// well, somewhat. we have to rebuild the pssh
// well, somewhat. it's just the raw data, we have to rebuild the pssh
const rebuiltPssh = psshTools.widevine.encodePssh({
contentId: "Hiiii", // lol?? i don't know what this is, random slop go!!!!
dataOnly: false,
@ -26,7 +25,7 @@ export async function getWidevineDecryptionKey(psshDataUri: string, trackId: str
});
log.warn("pssh was invalid, treating it as raw data");
log.warn("this should not error, unless the pssh data is invalid, too");
log.warn("this should not throw an error, unless the pssh data is invalid, too");
pssh = Buffer.from(rebuiltPssh, "base64");
session = new Session({ privateKey, identifierBlob }, pssh);
@ -39,10 +38,11 @@ export async function getWidevineDecryptionKey(psshDataUri: string, trackId: str
challenge.toString("base64")
);
if (typeof response?.license !== "string") { throw "license is missing or not a string! sign that authentication failed (unsupported codec?)"; }
if (typeof response?.license !== "string") { throw "license is gone/not a string! sign that auth failed (unsupported codec?)"; }
const license = session.parseLicense(Buffer.from(response.license, "base64"));
if (license.length === 0) { throw "license(s) failed to be parsed. this could be an error for invalid data! (e.x. pssh/challenge)"; }
if (license.length === 0) { throw "license(s) failed to be parsed. this could be an error showing invalid data! (e.x. pssh/challenge)"; }
log.info(license);
fs.writeFileSync("license", response.license, { encoding: "utf-8" });
const validKey = license.find((keyPair) => { return keyPair?.key?.length === 32; })?.key;
if (validKey === undefined) { throw "no valid key found in license"; }
return validKey;
}

View file

@ -4,12 +4,13 @@ import type { SongAttributes } from "../api/types/appleMusic/attributes.js";
import hls, { Item } from "parse-hls";
import axios from "axios";
import { getWidevineDecryptionKey } from "./keygen.js";
import { widevine, playready, fairplay } from "../constants/keyFormats.js";
import { select } from "@inquirer/prompts";
// ugliest type ever
// this library is so bad
// i wish pain on the person who wrote this /j :smile:
type HLS = ReturnType<typeof hls.default.parse>;
type M3u8 = ReturnType<typeof hls.default.parse>;
// TODO: whole big thing, and a somewhat big issue
// some files can just Not be downloaded
@ -45,14 +46,16 @@ async function getStreamInfo(trackMetadata: SongAttributes<["extendedAssetUrls"]
const playreadyPssh = getPlayreadyPssh(drmInfos, drmIds);
const fairplayKey = getFairplayKey(drmInfos, drmIds);
await getWidevineDecryptionKey(widevinePssh, "1615276490");
const trackId = trackMetadata.playParams?.id;
if (trackId === undefined) { throw "track id is missing, this may indicate your song isn't accessable w/ your subscription!"; }
// TODO: make this a value in the class when we do that
log.info(await getWidevineDecryptionKey(widevinePssh, trackId));
}
// i don't think i wanna write all of the values we need. annoying !
type DrmInfos = { [key: string]: { [key: string]: { "URI": string } }; };
function getDrmInfos(m3u8Data: HLS): DrmInfos {
function getDrmInfos(m3u8Data: M3u8): DrmInfos {
// see `getAssetInfos` for the reason why this is so bad
// filthy. i should write my own m3u8 library that doesn't suck balls
for (const line of m3u8Data.lines) {
if (
line.name === "sessionData" &&
@ -60,6 +63,7 @@ function getDrmInfos(m3u8Data: HLS): DrmInfos {
) {
const value = line.content.match(/VALUE="([^"]+)"/);
if (!value) { throw "could not match for value!"; }
if (!value[1]) { throw "value is empty!"; }
return JSON.parse(Buffer.from(value[1], "base64").toString("utf-8"));
}
@ -70,7 +74,8 @@ function getDrmInfos(m3u8Data: HLS): DrmInfos {
// TODO: remove inquery for the codec, including its library, this is for testing
// add a config option for preferred codec ?
async function getPlaylist(m3u8Data: HLS): Promise<Item> {
// or maybe in the streaminfo function
async function getPlaylist(m3u8Data: M3u8): Promise<Item> {
const masterPlaylists = m3u8Data.streamRenditions;
const masterPlaylist = await select({
message: "codec ?",
@ -85,9 +90,9 @@ async function getPlaylist(m3u8Data: HLS): Promise<Item> {
// TODO: check type more strictly
// does it really exist? we never check,,
// i don't think i wanna write all of the values we need. annoying !
// filthy. i should write my own m3u8 library that doesn't suck balls
type AssetInfos = { [key: string]: { "AUDIO-SESSION-KEY-IDS": string[]; }; }
function getAssetInfos(m3u8Data: HLS): AssetInfos {
function getAssetInfos(m3u8Data: M3u8): AssetInfos {
// LOL??? THIS LIBRARY IS SO BAD
// YOU CAN'T MAKE THIS SHIT UP
// https://files.catbox.moe/ac0ps4.jpg
@ -98,6 +103,7 @@ function getAssetInfos(m3u8Data: HLS): AssetInfos {
) {
const value = line.content.match(/VALUE="([^"]+)"/);
if (!value) { throw "could not match for value!"; }
if (!value[1]) { throw "value is empty!"; }
return JSON.parse(Buffer.from(value[1], "base64").toString("utf-8"));
}
@ -118,10 +124,10 @@ function getDrmData(drmInfos: DrmInfos, drmIds: string[], drmKey: string): strin
return drmInfo[drmKey].URI; // afaik this index is 100% safe?
}
const getWidevinePssh = (drmInfos: DrmInfos, drmIds: string[]): string => getDrmData(drmInfos, drmIds, "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed");
const getPlayreadyPssh = (drmInfos: DrmInfos, drmIds: string[]): string => getDrmData(drmInfos, drmIds, "com.microsoft.playready");
const getFairplayKey = (drmInfos: DrmInfos, drmIds: string[]): string => getDrmData(drmInfos, drmIds, "com.apple.streamingkeydelivery");
const getWidevinePssh = (drmInfos: DrmInfos, drmIds: string[]): string => getDrmData(drmInfos, drmIds, widevine);
const getPlayreadyPssh = (drmInfos: DrmInfos, drmIds: string[]): string => getDrmData(drmInfos, drmIds, playready);
const getFairplayKey = (drmInfos: DrmInfos, drmIds: string[]): string => getDrmData(drmInfos, drmIds, fairplay);
// TODO: remove later, this is just for testing
log.debug(await appleMusicApi.getWebplayback("1615276490"));
// log.debug(await appleMusicApi.getWebplayback("1615276490"));
await getStreamInfo((await appleMusicApi.getSong("1615276490")).data[0].attributes);