Last active
October 31, 2023 22:58
-
-
Save Nezteb/5ba0025d89bc743581e9fceccdde9dc1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Download songs | |
// @description Download songs from a specific website | |
// @namespace Violentmonkey Scripts | |
// @match REDACTED | |
// @version 0.1 | |
// @author Noah Betzen | |
// @grant none | |
// ==/UserScript== | |
const STORAGE_KEY = `seen` | |
(function() { | |
`use strict`; | |
const seenString = localStorage.getItem(STORAGE_KEY); | |
let seen = seenString ? JSON.parse(seenString) : {}; | |
const count = Object.keys(seen).length; | |
if (count > 0) { | |
console.log(`Seen URLs: ${count}`, seen); | |
} | |
const originalFetch = fetch; | |
const queue = new DownloadQueue(originalFetch, 5); | |
window.fetch = async function() { | |
let response = await originalFetch.apply(this, arguments); | |
// This is async but we don't await it on purpose so that the response returns quickly | |
processNewData(response.clone(), seen, queue); | |
return response; | |
}; | |
})(); | |
async function processNewData(resp, seen, queue) { | |
const responseData = await resp.json(); | |
responseData?.result?.servings?.forEach(async (serving) => { | |
let originalDownloadUrl = serving.trackVariation?.tokenedUrl; | |
if (originalDownloadUrl) { | |
// Key url is stripped of query/hash params | |
const url = new URL(originalDownloadUrl); | |
const keyUrl = keyUrl.origin + keyUrl.pathname; | |
if (!seen[keyUrl]) { | |
console.log(`Unseen URL: ${keyUrl}`) | |
const proxyDownloadUrl = `https://corsproxy.io/?${encodeURIComponent(originalDownloadUrl)}`; | |
await queue.enqueue(originalDownloadUrl, proxyDownloadUrl, keyUrl); | |
seen[keyUrl] = true; | |
localStorage.setItem(STORAGE_KEY, JSON.stringify(seen)); | |
} | |
} | |
}); | |
} | |
class DownloadQueue { | |
constructor(originalFetch, concurrency) { | |
this.queue = []; | |
this.running = 0; | |
this.originalFetch = originalFetch; | |
this.concurrency = concurrency || 5; | |
} | |
// Add a file to the queue | |
async enqueue(originalDownloadUrl, proxyDownloadUrl, keyUrl) { | |
this.queue.push({ | |
originalDownloadUrl, | |
proxyDownloadUrl, | |
keyUrl | |
}); | |
await this.run(); | |
} | |
// Process the queue | |
async run() { | |
if (this.running >= this.concurrency || this.queue.length === 0) { | |
return; | |
} | |
const { | |
originalDownloadUrl, | |
proxyDownloadUrl, | |
keyUrl | |
} = this.queue.shift(); | |
this.running++; | |
try { | |
await downloadFile(this.originalFetch, originalDownloadUrl, proxyDownloadUrl, keyUrl) | |
this.running--; | |
this.run(); | |
} catch (error) { | |
console.error(`Error occurred in download queue`, { | |
originalDownloadUrl, | |
proxyDownloadUrl, | |
keyUrl, | |
error | |
}); | |
this.running--; | |
this.run(); | |
} | |
} | |
} | |
async function downloadFile(originalFetch, originalDownloadUrl, proxyDownloadUrl, keyUrl) { | |
const filename = new URL(keyUrl).pathname.split(`/`).pop(); | |
if (filename) { | |
const response = await originalFetch(proxyDownloadUrl); | |
if (!response.ok) { | |
console.error(`Failed to fetch (response not ok) ${keyUrl}`, response); | |
return; | |
} | |
const blob = await response.blob(); | |
// Create an object URL from the Blob | |
const blobURL = URL.createObjectURL(blob); | |
// Use an anchor element to trigger the download | |
const a = document.createElement(`a`); | |
a.style.display = `none`; | |
document.body.appendChild(a); | |
a.href = blobURL; | |
a.download = filename; | |
a.click(); | |
// Cleanup | |
document.body.removeChild(a); | |
URL.revokeObjectURL(blobURL); | |
console.log(`Successfully downloaded file`, { | |
filename, | |
proxyDownloadUrl | |
}); | |
} else { | |
console.error(`Couldn not get filename for url ${keyUrl}`); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment