khinsider 批量下載器
khinsider 是一個提供許多遊戲 OST 下載與試聽的網站,也提供許多免費單曲下載 不過下載整張專輯的功能是需要 donate 的,因此就弄了一個簡單的腳本來批量下載 下載連結: Greasy Fork
我之所以知道這個網站是為了下載整張永恆的賽妮亞的 OST
這遊戲也是不錯的遊戲,有興趣可以玩玩看
用法
安裝完上面的腳本之後到 khinsider 網站上找到你喜歡的遊戲這篇文章以永恆的賽妮亞作為範例
按下圖片中的 click to download 然後右邊會出現一個進度條建議瀏覽器留在這個分頁,因為同時瀏覽其他分頁時可能會卡卡的(Firefox)
等進度條跑到滿之後瀏覽器應該會自動觸發下載,檔案名=[遊戲名稱].zip 把 zip 用 7zip,winrar 之類的程式打開裡面就會有你想要的音樂了
原理
直接點擊下方的任何一首音樂就會進入到音樂的下載頁面,而下載頁面上有連結然後用ajax
的方式去把檔案存在記憶體成Blob
的格式最後用 JSZip 把檔案包裝成 zip 檔
程式碼: Greasy Fork
downloadblob
因為網站本身在https://downloads.khinsider.com/
上,屬於 https 而存放音樂的伺服器在http://66.90.93.122/
上,屬於 http 因為 protocol 不同的關係不符合同源政策所以沒辦法直接用一般的XMLHttpRequest
和fetch
需要使用腳本管理器提供的GM_xmlhttpRequest
來做請求
函數也很簡單,就是給 url 然後回傳一個Promise<Blob>
// @grant GM_xmlhttpRequest
//something...
function downloadblob(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url,
responseType: 'blob',
onload: res => resolve(res.response)
})
})
}
主程式
在原本就有的 click to download
連結上綁click
事件並取消它,然後顯示進度條接下來取得遊戲名稱和各首歌下載頁面的網址用fetch
去抓下載頁面,並且用jquery
在上面找出mp3
的真實連結再來把把真實連結轉成{blob: Promise<Blob>,name: String}
的物件,用reduce
去加入JSZip
(JSZip
支援Promise
) 而JSZip
有支援進度,在有進度時就更新前面的<progress>
最後在下載完成時把整個zip
的Blob
弄成 url 並觸發下載就完成了
$('a:contains("click to download")').on('click', e => {
e.preventDefault()
$('.albumMassDownload').append(`
<div>
<span>Download progress:</span>
<progress min="0" max="100" id="dp" value="0"></progress>
</div>
`)
const title = $('h2')[0].textContent
const urls = $('tr>td.clickable-row:not([align])')
.toArray()
.map(el =>
$(el)
.find('a')
.attr('href')
)
const requests = urls.map(e => fetch(e).then(r => r.text()))
Promise.all(requests).then(ar =>
ar
.map(ht => {
const url = $(ht)
.find('a:contains("Click here to download as MP3")')
.attr('href')
return {
blob: downloadblob(url),
name: decodeURIComponent(url.split('/').pop())
}
})
.reduce((zip, file) => {
zip.file(file.name, file.blob)
return zip
}, new JSZip())
.generateAsync({ type: 'blob' }, meta => {
$('#dp').attr('value', parseInt(meta.percent))
})
.then(blob => {
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.download = title + '.zip'
a.href = url
document.body.appendChild(a)
a.click()
a.remove()
URL.revokeObjectURL(url)
})
)
})