updated script
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
*.lnk
|
||||||
|
|||||||
30
deno.lock
generated
30
deno.lock
generated
@@ -1,5 +1,35 @@
|
|||||||
{
|
{
|
||||||
"version": "5",
|
"version": "5",
|
||||||
|
"specifiers": {
|
||||||
|
"jsr:@std/cli@*": "1.0.23",
|
||||||
|
"jsr:@std/fs@*": "1.0.19",
|
||||||
|
"npm:@types/node@^24.5.2": "24.9.1"
|
||||||
|
},
|
||||||
|
"jsr": {
|
||||||
|
"@std/assert@1.0.15": {
|
||||||
|
"integrity": "d64018e951dbdfab9777335ecdb000c0b4e3df036984083be219ce5941e4703b"
|
||||||
|
},
|
||||||
|
"@std/cli@1.0.23": {
|
||||||
|
"integrity": "bf95b7a9425ba2af1ae5a6359daf58c508f2decf711a76ed2993cd352498ccca"
|
||||||
|
},
|
||||||
|
"@std/fs@1.0.19": {
|
||||||
|
"integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06"
|
||||||
|
},
|
||||||
|
"@std/internal@1.0.12": {
|
||||||
|
"integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
|
"@types/node@24.9.1": {
|
||||||
|
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
|
||||||
|
"dependencies": [
|
||||||
|
"undici-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"undici-types@7.16.0": {
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="
|
||||||
|
}
|
||||||
|
},
|
||||||
"redirects": {
|
"redirects": {
|
||||||
"https://esm.sh/@types/boolbase@~1.0.3/index.d.ts": "https://esm.sh/@types/boolbase@1.0.3/index.d.ts",
|
"https://esm.sh/@types/boolbase@~1.0.3/index.d.ts": "https://esm.sh/@types/boolbase@1.0.3/index.d.ts",
|
||||||
"https://esm.sh/@types/safer-buffer@~2.1.3/index.d.ts": "https://esm.sh/@types/safer-buffer@2.1.3/index.d.ts",
|
"https://esm.sh/@types/safer-buffer@~2.1.3/index.d.ts": "https://esm.sh/@types/safer-buffer@2.1.3/index.d.ts",
|
||||||
|
|||||||
91
main.ts
91
main.ts
@@ -1,8 +1,7 @@
|
|||||||
import { existsSync } from 'node:fs'
|
|
||||||
import { mkdir, writeFile } from 'node:fs/promises'
|
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { argv } from 'node:process'
|
|
||||||
import { Buffer } from 'node:buffer'
|
import { Buffer } from 'node:buffer'
|
||||||
|
import { parseArgs } from "jsr:@std/cli/parse-args"
|
||||||
|
import { exists } from "jsr:@std/fs/exists";
|
||||||
import * as cheerio from 'https://esm.sh/cheerio?target=esnext'
|
import * as cheerio from 'https://esm.sh/cheerio?target=esnext'
|
||||||
|
|
||||||
const REGEX_PLAYLISTURL =
|
const REGEX_PLAYLISTURL =
|
||||||
@@ -12,23 +11,65 @@ const REGEX_SONGURL =
|
|||||||
const REGEX_ALBUMTITLE = /^(.*?) MP3 - Download/i
|
const REGEX_ALBUMTITLE = /^(.*?) MP3 - Download/i
|
||||||
const REGEX_UNSAFEFORFILE = /[^a-z0-9\-_=+,.()\[\]{} ]/gi
|
const REGEX_UNSAFEFORFILE = /[^a-z0-9\-_=+,.()\[\]{} ]/gi
|
||||||
|
|
||||||
async function main() {
|
// parse args
|
||||||
// deno main.ts <playlistURL> [<downloadPath>] [sync]
|
const flags = parseArgs(Deno.args, {
|
||||||
const playlistURL = argv[2]
|
alias: {
|
||||||
const downloadPath = argv[3] || '.'
|
"url": ["u"],
|
||||||
const runSync = argv[4]?.toLowerCase() == 'sync'
|
"output": ["o"],
|
||||||
|
"sync": ["s"],
|
||||||
|
"help": ["h"],
|
||||||
|
},
|
||||||
|
string: ["url", "output"],
|
||||||
|
boolean: ["sync", "help"],
|
||||||
|
default: {
|
||||||
|
"url": "",
|
||||||
|
"output": ".",
|
||||||
|
"sync": false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!flags.url && flags._.length > 0) {
|
||||||
|
flags.url = flags._[0].toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function printHelp() {
|
||||||
|
console.log(`deno
|
||||||
|
--allow-net --allow-write --allow-read main.ts
|
||||||
|
[--url] <url>
|
||||||
|
[--output <downloadPath>]
|
||||||
|
[--sync]
|
||||||
|
[--help]
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
--url -u (default)
|
||||||
|
url to download
|
||||||
|
|
||||||
|
--output -o
|
||||||
|
default: "."
|
||||||
|
output path
|
||||||
|
|
||||||
|
--sync -s
|
||||||
|
download files one at a time
|
||||||
|
|
||||||
|
--help -h
|
||||||
|
print help message
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
// load all song details
|
// load all song details
|
||||||
const playlist = await fetchPlaylist(playlistURL)
|
const playlist = await fetchPlaylist(flags.url)
|
||||||
|
|
||||||
// if sync, download one at a time
|
// if sync, download one at a time
|
||||||
if (runSync) {
|
if (flags.sync) {
|
||||||
|
console.log('downloading one at a time\n')
|
||||||
for (const song of playlist) {
|
for (const song of playlist) {
|
||||||
await downloadSong(song, downloadPath || '.')
|
await downloadSong(song, flags.output || '.')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
console.log('downloading all at once\n')
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
playlist.map((song) => downloadSong(song, downloadPath || '.'))
|
playlist.map((song) => downloadSong(song, flags.output || '.'))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,6 +79,8 @@ async function fetchPlaylist(url: string): Promise<PlaylistSongData[]> {
|
|||||||
throw `unaccepted url ${url}`
|
throw `unaccepted url ${url}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`downloading: ${url}`)
|
||||||
|
|
||||||
// load the playlist page's dom
|
// load the playlist page's dom
|
||||||
const req = await fetch(url)
|
const req = await fetch(url)
|
||||||
const text = await req.text()
|
const text = await req.text()
|
||||||
@@ -53,12 +96,12 @@ async function fetchPlaylist(url: string): Promise<PlaylistSongData[]> {
|
|||||||
throw `unable to grab album name from ${title}`
|
throw `unable to grab album name from ${title}`
|
||||||
}
|
}
|
||||||
const albumName = titleMatch[1]
|
const albumName = titleMatch[1]
|
||||||
|
console.log(` title: ${albumName}`)
|
||||||
|
|
||||||
// parse all rows in playlist
|
// parse all rows in playlist
|
||||||
const columns = getPlaylistTableHeaders($)
|
const columns = getPlaylistTableHeaders($)
|
||||||
const rows = getPlaylistRows($, columns, albumName)
|
const rows = getPlaylistRows($, columns, albumName)
|
||||||
|
console.log(` songs: ${rows.length}\n`)
|
||||||
console.log(`downloading ${rows.length} songs`)
|
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
@@ -203,12 +246,12 @@ async function downloadSong(song: PlaylistSongData, location: string) {
|
|||||||
const { pathname, fullpathname } = pathFor(location, song)
|
const { pathname, fullpathname } = pathFor(location, song)
|
||||||
|
|
||||||
// ensure folder exists
|
// ensure folder exists
|
||||||
if (!existsSync(pathname)) {
|
if (!await exists(pathname)) {
|
||||||
await mkdir(pathname)
|
await Deno.mkdir(pathname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip file if it exists
|
// skip file if it exists
|
||||||
if (existsSync(fullpathname)) {
|
if (await exists(fullpathname)) {
|
||||||
console.log(`skipping file already exists ${fullpathname}`)
|
console.log(`skipping file already exists ${fullpathname}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +261,7 @@ async function downloadSong(song: PlaylistSongData, location: string) {
|
|||||||
// download the file
|
// download the file
|
||||||
const songresp = await fetch(flacUrl)
|
const songresp = await fetch(flacUrl)
|
||||||
const songblob = await songresp.arrayBuffer()
|
const songblob = await songresp.arrayBuffer()
|
||||||
return writeFile(fullpathname, toBuffer(songblob))
|
return Deno.writeFile(fullpathname, toBuffer(songblob))
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SongPath {
|
interface SongPath {
|
||||||
@@ -279,4 +322,12 @@ function toBuffer(arrayBuffer: ArrayBuffer): Buffer<ArrayBuffer> {
|
|||||||
return buffer
|
return buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch((e) => console.error(e))
|
if (!flags.url) {
|
||||||
|
console.log('Missing URL\n\n')
|
||||||
|
printHelp()
|
||||||
|
} else if (flags.help) {
|
||||||
|
printHelp()
|
||||||
|
} else {
|
||||||
|
main().catch((e) => console.error(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user