chore: update public repo

This commit is contained in:
desu-bot 2026-01-01 18:52:50 +00:00
parent ef375d1188
commit 46cf487f04
No known key found for this signature in database
5 changed files with 229 additions and 26 deletions

View file

@ -9,8 +9,9 @@ import { z } from 'zod'
import { $, ProcessOutput, question } from 'zx'
import { downloadFile, ffetch as ffetchBase } from '../../utils/fetch.ts'
import { sanitizeFilename } from '../../utils/fs.ts'
import { generateOpusImageBlob, pipeIntoProc } from '../../utils/media-metadata.ts'
import { chunks, getEnv } from '../../utils/misc.ts'
import { generateOpusImageBlob } from '../../utils/media-metadata.ts'
import { concatSegments, parseSimpleHls } from '../../utils/mp4-streaming.ts'
const ffetchApi = ffetchBase.extend({
baseUrl: 'https://api-v2.soundcloud.com',
@ -29,6 +30,9 @@ const ffetchApi = ffetchBase.extend({
})
const ffetchHtml = ffetchBase.extend({
baseUrl: 'https://soundcloud.com',
retry: {
maxRetries: 3,
},
headers: {
Cookie: `oauth_token=${getEnv('SOUNDCLOUD_TOKEN')}`,
},
@ -156,7 +160,7 @@ async function downloadTrack(track: ScTrack, opts: {
transcoding = t
}
const { url: hlsUrl } = await ffetchApi(transcoding.url, {
const { url: mediaUrl } = await ffetchApi(transcoding.url, {
query: {
track_authorization: track.track_authorization,
},
@ -174,14 +178,39 @@ async function downloadTrack(track: ScTrack, opts: {
url: z.string(),
}))
let ext = transcoding.format.mime_type.match(/^audio\/(\w+)(;|$)/)![1]
if (ext === 'mp4') ext = 'm4a'
const ext = {
aac_256k: 'm4a',
aac_160k: 'm4a',
aac_1_0: 'm4a',
aac_hq: 'm4a',
abr_hq: 'm4a',
abr_sq: 'm4a',
mp3_0_0: 'mp3',
opus_0_0: 'ogg',
}[transcoding.preset]
if (!ext) {
throw new Error(`Unsupported transcoding preset: ${transcoding.preset}`)
}
const filename = `${opts.destination}.${ext}`
let stream: ReadableStream | null = null
let ffmpegInput: string
if (transcoding.format.protocol === 'hls') {
const segments = parseSimpleHls(await ffetchHtml(mediaUrl).text())
stream = concatSegments({
segments,
poolSize: 4,
fetch: async url => new Uint8Array(await ffetchHtml(url).arrayBuffer()),
})
ffmpegInput = 'pipe:0'
} else {
ffmpegInput = mediaUrl
}
const params: string[] = [
'-y',
'-i',
hlsUrl,
ffmpegInput,
]
if (artworkBytes) {
@ -228,13 +257,17 @@ async function downloadTrack(track: ScTrack, opts: {
'-metadata',
`artist=${track.user.username}`,
'-metadata',
`comment=${track.description ?? ''}`,
`comment=${`${track.description ?? ''}\n\nripped from soundcloud (id: ${track.id}, url: ${track.permalink_url})`}`.trimStart(),
filename,
)
while (true) {
try {
await $`ffmpeg ${params}`.quiet(true)
const promise = $`ffmpeg ${params}`.quiet(true)
if (stream) {
await pipeIntoProc(promise, stream)
}
await promise
break
} catch (e) {
if (!(e instanceof ProcessOutput)) {

View file

@ -9,8 +9,7 @@ import { ffetch as ffetchBase } from '../../utils/fetch.ts'
import { sanitizeFilename } from '../../utils/fs.ts'
import { pipeIntoProc, runMetaflac, writeIntoProc } from '../../utils/media-metadata.ts'
import { getEnv } from '../../utils/misc.ts'
import { concatMpdSegments, parseSimpleMpd } from '../../utils/mpd.ts'
import { createLibcurlFetch } from '../../utils/temkakit/libcurl.ts'
import { concatSegments, parseSimpleMpd } from '../../utils/mp4-streaming.ts'
const oauthResponse = await ffetchBase('https://auth.tidal.com/v1/oauth2/token', {
form: {
@ -192,8 +191,8 @@ async function downloadTrack(options: {
]
const proc = $`ffmpeg ${params}`
await pipeIntoProc(proc, concatMpdSegments({
mpd: parseSimpleMpd(utf8.decoder.decode(manifest)),
await pipeIntoProc(proc, concatSegments({
segments: parseSimpleMpd(utf8.decoder.decode(manifest)).segments,
fetch: async url => new Uint8Array(await ffetch(url).arrayBuffer()),
}))
await proc
@ -408,7 +407,7 @@ if ((m = url.match(/\/track\/(\d+)/))) {
await rm(tmpAlbumCoverPath)
} else if ((m = url.match(/\/album\/(\d+)/))) {
await downloadAlbum(m[1])
await downloadAlbum(Number(m[1]))
} else if ((m = url.match(/\/artist\/(\d+)/))) {
const withAppearsOn = (await question('include appears on albums? (y/N) > ')).toLowerCase() === 'y'