commit 6c9b6c9ec1b8fb9e857a333947a029297cdd7ba2 Author: zomo Date: Fri Sep 26 19:11:03 2025 -0500 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..5b320c2 --- /dev/null +++ b/deno.json @@ -0,0 +1,8 @@ +{ + "tasks": { + "dev": "deno run --watch main.ts" + }, + "imports": { + "@std/assert": "jsr:@std/assert@1" + } +} diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..0e1543a --- /dev/null +++ b/main.ts @@ -0,0 +1,95 @@ +import { existsSync } from 'node:fs' +import { mkdir, writeFile } from 'node:fs/promises' +import path from 'node:path' +import {argv} from 'node:process' + +const REGEX_PLAYLISTURL = /^https:\/\/downloads\.khinsider\.com\/game-soundtracks\/album\/[^\/]+\/?$/i +const REGEX_SONGURL = /^https:\/\/downloads\.khinsider\.com\/game-soundtracks\/album\/[^\/]+\/[^\/]+\/?$/i +const REGEX_FILEPATHPARSE = /^\/soundtracks\/([^\/]+)\/[^\/]+\/([^\/]+)$/i +const REGEX_PLAYLIST_SONGHREF = //gi +const REGEX_SONG_FILEHREF = / downloadSong(song, downloadPath || '.'))) + } +} + +async function fetchPlaylist(url: string): Promise { + if (!REGEX_PLAYLISTURL.test(url)) { + throw `unaccepted url ${url}` + } + + const resp = await fetch(url) + const text = await resp.text() + + const matches = text.matchAll(REGEX_PLAYLIST_SONGHREF) + const matchesArr = [...matches].map(match => match[1]) + + console.log(`downloading ${matchesArr.length} songs`) + + return matchesArr +} + +async function downloadSong(url: string, location: string) { + if (!/^http/i.test(url)) { + url = 'https://downloads.khinsider.com' + url + } + if (!REGEX_SONGURL.test(url)) { + throw `unaccepted url ${url}` + } + + let resp = await fetch(url) + let text = await resp.text() + let match = text.match(REGEX_SONG_FILEHREF) + if (!match) { + throw `can't find download link for ${url}` + } + + + const songurl = new URL(match[1]) + const songurlmatch = REGEX_FILEPATHPARSE.exec(songurl.pathname) + if (!songurlmatch) { + throw `can't find folder and filename for ${songurl}` + } + + const foldername = decodeURIComponent(songurlmatch[1]) + const filename = decodeURIComponent(songurlmatch[2]) + const pathname = path.resolve(location, foldername) + const fullpathname = path.resolve(pathname, filename) + + if (!existsSync(pathname)) { + await mkdir(pathname) + } + if (existsSync(fullpathname)) { + console.log(`skipping file already exists ${fullpathname}`) + } + + console.log(`downloading ${fullpathname}`) + + const songresp = await fetch(songurl) + const songblob = await songresp.arrayBuffer() + + return writeFile(fullpathname, toBuffer(songblob)) +} + +function toBuffer(arrayBuffer: ArrayBuffer) { + const buffer = Buffer.alloc(arrayBuffer.byteLength); + const view = new Uint8Array(arrayBuffer); + for (let i = 0; i < buffer.length; ++i) { + buffer[i] = view[i]; + } + return buffer; +} + +main().catch(e => console.error(e)) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..16b3a78 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,29 @@ +{ + "name": "khinsider downloads", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@types/node": "^24.5.2" + } + }, + "node_modules/@types/node": { + "version": "24.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", + "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.12.0" + } + }, + "node_modules/undici-types": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", + "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..062e7f6 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "@types/node": "^24.5.2" + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..8f7f71a --- /dev/null +++ b/readme.md @@ -0,0 +1,7 @@ +# khinsider-downloads + +## usage + +deno + +`deno --allow-net --allow-write --allow-read main.ts "https://downloads.khinsider.com/game-soundtracks/album/"`