kill the codec enums

This commit is contained in:
Reid 2025-07-09 14:10:08 -07:00
parent 7a3d74dc87
commit 8113c36a47
Signed by: reidlab
GPG key ID: DAF5EAF6665839FD
6 changed files with 71 additions and 49 deletions

View file

@ -0,0 +1,40 @@
import { z } from "zod";
export const regularCodecTypeSchema = z.enum([
"aac",
"aac_he",
"aac_binaural",
"aac_downmix",
"aac_he_binaural",
"aac_he_downmix",
"atmos",
"ac3",
"alac"
]);
export const webplaybackCodecTypeSchema = z.enum([
"aac_legacy",
"aac_he_legacy"
]);
export type RegularCodecType = z.infer<typeof regularCodecTypeSchema>;
export type WebplaybackCodecType = z.infer<typeof webplaybackCodecTypeSchema>;
export class CodecType {
public readonly codecType: RegularCodecType | WebplaybackCodecType;
public readonly regularOrWebplayback: "regular" | "webplayback";
constructor(codecType: string) {
const regularCheck = regularCodecTypeSchema.safeParse(codecType);
const webplaybackCheck = webplaybackCodecTypeSchema.safeParse(codecType);
if (regularCheck.success) {
this.regularOrWebplayback = "regular";
this.codecType = regularCheck.data;
} else if (webplaybackCheck.success) {
this.regularOrWebplayback = "webplayback";
this.codecType = webplaybackCheck.data;
} else {
throw new Error(`invalid codec type: ${codecType}!`);
}
}
}

View file

@ -2,8 +2,9 @@ import { config } from "../config.js";
import { spawn } from "node:child_process";
import path from "node:path";
import { addToCache, isCached } from "../cache.js";
import type { RegularCodecType, WebplaybackCodecType } from "./codecType.js";
export async function downloadSong(streamUrl: string, decryptionKey: string, songCodec: RegularCodec | WebplaybackCodec): Promise<string> {
export async function downloadSong(streamUrl: string, decryptionKey: string, songCodec: RegularCodecType | WebplaybackCodecType): Promise<string> {
let baseOutputName = streamUrl.match(/(?:.*\/)\s*(\S*?)[.?]/)?.[1];
if (!baseOutputName) { throw new Error("could not get base output name from stream url!"); }
baseOutputName += `_${songCodec}`;
@ -53,21 +54,3 @@ export async function downloadSong(streamUrl: string, decryptionKey: string, son
return decryptedPath;
}
// TODO: find a better spot for this
export enum RegularCodec {
Aac = "aac",
AacHe = "aac_he",
AacBinaural = "aac_binaural",
AacDownmix = "aac_downmix",
AacHeBinaural = "aac_he_binaural",
AacHeDownmix = "aac_he_downmix",
Atmos = "atmos",
Ac3 = "ac3",
Alac = "alac"
}
export enum WebplaybackCodec {
AacLegacy = "aac_legacy",
AacHeLegacy = "aac_he_legacy"
}

View file

@ -5,7 +5,7 @@ import axios from "axios";
import { widevine, playready, fairplay } from "../constants/keyFormats.js";
import { songCodecRegex } from "../constants/codecs.js";
import type { WebplaybackResponse } from "appleMusicApi/index.js";
import { RegularCodec, WebplaybackCodec } from "./index.js";
import type { RegularCodecType, WebplaybackCodecType } from "./codecType.js";
// why is this private
// i wish pain on the person who wrote this /j :smile:
@ -34,7 +34,8 @@ export default class StreamInfo {
// TODO: why can't we decrypt widevine ones with this?
// we get a valid key.. but it doesn't work :-(
public static async fromTrackMetadata(trackMetadata: SongAttributes<["extendedAssetUrls"]>, codec: RegularCodec): Promise<StreamInfo> {
// upd: it seems thats just how the cookie crumbles. oh well
public static async fromTrackMetadata(trackMetadata: SongAttributes<["extendedAssetUrls"]>, codec: RegularCodecType): Promise<StreamInfo> {
log.warn("the track metadata method is experimental, and may not work or give correct values!");
log.warn("if there is a failure--use a codec that uses the webplayback method");
@ -70,12 +71,12 @@ export default class StreamInfo {
// webplayback is the more "legacy" way
// only supports widevine, from what i can tell
public static async fromWebplayback(webplayback: WebplaybackResponse, codec: WebplaybackCodec): Promise<StreamInfo> {
public static async fromWebplayback(webplayback: WebplaybackResponse, codec: WebplaybackCodecType): Promise<StreamInfo> {
const song = webplayback.songList[0];
let flavor: string;
if (codec === WebplaybackCodec.AacHeLegacy) { flavor = "32:ctrp64"; }
else if (codec === WebplaybackCodec.AacLegacy) { flavor = "28:ctrp256"; }
if (codec === "aac_he_legacy") { flavor = "32:ctrp64"; }
else if (codec === "aac_legacy") { flavor = "28:ctrp256"; }
const asset = song.assets.find((asset) => { return asset.flavor === flavor; });
if (asset === undefined) { throw new Error("webplayback info for requested flavor doesn't exist!"); }
@ -141,7 +142,7 @@ function getAssetInfos(m3u8Data: M3u8): AssetInfos {
throw new Error("m3u8 missing audio asset metadata!");
}
async function getPlaylist(m3u8Data: M3u8, codec: RegularCodec): Promise<Item> {
async function getPlaylist(m3u8Data: M3u8, codec: RegularCodecType): Promise<Item> {
const masterPlaylists = m3u8Data.streamRenditions;
const masterPlaylist = masterPlaylists.find((playlist) => {
const line = playlist.properties[0].attributes?.audio;