mirror of
https://git.stupid.fish/teidesu/scripts.git
synced 2025-07-28 02:32:11 +10:00
chore: update public repo
This commit is contained in:
parent
2423324540
commit
090a502ece
3 changed files with 335 additions and 40 deletions
|
@ -1,12 +1,12 @@
|
|||
import { mkdir, writeFile } from 'node:fs/promises'
|
||||
import { mkdir, rm, writeFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { ffetchAddons } from '@fuman/fetch'
|
||||
import { asyncPool, base64 } from '@fuman/utils'
|
||||
import { assert, asyncPool, base64 } from '@fuman/utils'
|
||||
import { load } from 'cheerio'
|
||||
import Spinnies from 'spinnies'
|
||||
import { ProxyAgent } from 'undici'
|
||||
import { z } from 'zod'
|
||||
import { $ } from 'zx'
|
||||
import { $, question } from 'zx'
|
||||
import { downloadFile, ffetch as ffetchBase } from '../../utils/fetch.ts'
|
||||
import { sanitizeFilename } from '../../utils/fs.ts'
|
||||
import { chunks, getEnv } from '../../utils/misc.ts'
|
||||
|
@ -133,21 +133,18 @@ async function downloadTrack(track: ScTrack, opts: {
|
|||
const artworkBytes = track.artwork_url ? new Uint8Array(await ffetchHtml(track.artwork_url).arrayBuffer()) : null
|
||||
|
||||
// find the best transcoding
|
||||
const transcoding = track.media.transcodings.sort((a, b) => {
|
||||
// prefer non-legacy transcodings
|
||||
if (a.is_legacy_transcoding && !b.is_legacy_transcoding) return -1
|
||||
if (!a.is_legacy_transcoding && b.is_legacy_transcoding) return 1
|
||||
|
||||
// prefer hq
|
||||
if (a.quality === 'sq' && b.quality === 'hq') return -1
|
||||
if (a.quality === 'hq' && b.quality === 'sq') return 1
|
||||
|
||||
// prefer opus
|
||||
if (a.preset === 'opus_0_0' && b.preset !== 'opus_0_0') return -1
|
||||
if (a.preset !== 'opus_0_0' && b.preset === 'opus_0_0') return 1
|
||||
|
||||
return 0
|
||||
})[0]
|
||||
let transcoding!: typeof track.media.transcodings[0]
|
||||
for (const t of track.media.transcodings) {
|
||||
if (t.quality === 'hq') {
|
||||
transcoding = t
|
||||
break
|
||||
}
|
||||
if (t.preset === 'opus_0_0') {
|
||||
transcoding = t
|
||||
break
|
||||
}
|
||||
transcoding = t
|
||||
}
|
||||
|
||||
const { url: hlsUrl } = await ffetchApi(transcoding.url, {
|
||||
query: {
|
||||
|
@ -157,46 +154,55 @@ async function downloadTrack(track: ScTrack, opts: {
|
|||
url: z.string(),
|
||||
}))
|
||||
|
||||
const ext = transcoding.format.mime_type.match(/^audio\/(\w+)(;|$)/)![1]
|
||||
let ext = transcoding.format.mime_type.match(/^audio\/(\w+)(;|$)/)![1]
|
||||
if (ext === 'mp4') ext = 'm4a'
|
||||
const filename = `${opts.destination}.${ext}`
|
||||
|
||||
const params: string[] = [
|
||||
'-y',
|
||||
'-i',
|
||||
hlsUrl,
|
||||
'-c',
|
||||
'copy',
|
||||
]
|
||||
|
||||
if (ext === 'mp3') {
|
||||
if (artworkBytes) {
|
||||
if (artworkBytes) {
|
||||
if (ext === 'mp3') {
|
||||
await writeFile(artworkPath, artworkBytes)
|
||||
params.push(
|
||||
'-i',
|
||||
artworkPath,
|
||||
'-map',
|
||||
'0:a',
|
||||
'1:v:0',
|
||||
'-id3v2_version',
|
||||
'3',
|
||||
'-metadata:s:v',
|
||||
'title=Album cover',
|
||||
'-metadata:s:v',
|
||||
'comment=Cover (front)',
|
||||
)
|
||||
} else if (ext === 'ogg') {
|
||||
const blob = base64.encode(await generateOpusImageBlob(artworkBytes))
|
||||
params.push(
|
||||
'-metadata',
|
||||
`metadata_block_picture=${blob}`,
|
||||
)
|
||||
} else if (ext === 'm4a') {
|
||||
await writeFile(artworkPath, artworkBytes)
|
||||
params.push(
|
||||
'-i',
|
||||
artworkPath,
|
||||
'-map',
|
||||
'1:0',
|
||||
'1',
|
||||
'-disposition:v',
|
||||
'attached_pic',
|
||||
)
|
||||
}
|
||||
params.push(
|
||||
'-id3v2_version',
|
||||
'3',
|
||||
'-metadata:s:v',
|
||||
'title="Album cover"',
|
||||
'-metadata:s:v',
|
||||
'comment="Cover (front)"',
|
||||
)
|
||||
} else if (ext === 'ogg' && artworkBytes) {
|
||||
const blob = base64.encode(await generateOpusImageBlob(artworkBytes))
|
||||
params.push(
|
||||
'-metadata',
|
||||
`metadata_block_picture=${blob}`,
|
||||
)
|
||||
}
|
||||
|
||||
params.push(
|
||||
'-map',
|
||||
'0:a',
|
||||
'-c',
|
||||
'copy',
|
||||
'-metadata',
|
||||
`title=${track.title}`,
|
||||
'-metadata',
|
||||
|
@ -205,6 +211,8 @@ async function downloadTrack(track: ScTrack, opts: {
|
|||
)
|
||||
|
||||
await $`ffmpeg ${params}`.quiet(true)
|
||||
|
||||
await rm(artworkPath, { force: true })
|
||||
}
|
||||
|
||||
async function downloadPlaylist(playlist: ScPlaylist) {
|
||||
|
@ -259,4 +267,19 @@ async function downloadPlaylist(playlist: ScPlaylist) {
|
|||
spinnies.stopAll()
|
||||
}
|
||||
|
||||
await downloadPlaylist(await fetchPlaylistByUrl('https://soundcloud.com/user-398958278/sets/l2grace'))
|
||||
const url = process.argv[2] ?? await question('url > ')
|
||||
if (!url.startsWith('https://soundcloud.com/')) {
|
||||
console.error('url must start with https://soundcloud.com/')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (url.match(/^https:\/\/soundcloud.com\/[a-z0-9-]+\/sets\//i)) {
|
||||
await downloadPlaylist(await fetchPlaylistByUrl(url))
|
||||
} else {
|
||||
const track = await fetchTrackByUrl(url)
|
||||
const filename = `${track.user.username}-${track.title}`
|
||||
console.log('downloading track:', filename)
|
||||
await downloadTrack(track, {
|
||||
destination: join('assets/soundcloud-dl', sanitizeFilename(filename)),
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue