Rework individual song download
This commit is contained in:
parent
4ece9041a6
commit
991d69ed96
4 changed files with 42 additions and 15 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { getDownloadUrl, getTracks } from 'utils/search'
|
||||
import { getCors, getDownloadUrl, getTracks } from 'utils/search'
|
||||
import type { AlbumRow, TrackRow } from 'utils/types'
|
||||
import Loader from './Loader'
|
||||
import ClipboardBtn from './ClipboardBtn'
|
||||
|
|
@ -15,6 +15,7 @@ export default function Album(props: { album: AlbumRow; lastReqRef: React.RefObj
|
|||
<h3>{album.name}</h3>
|
||||
<div className='btn-container'>
|
||||
<ClipboardBtn albumUrl={album.url} lastReqRef={lastReqRef} />
|
||||
<button className='download-btn'>Download album</button>
|
||||
</div>
|
||||
</div>
|
||||
<TrackList url={album.url} open={open} lastReqRef={lastReqRef} />
|
||||
|
|
@ -59,7 +60,7 @@ function TrackList(props: { url: string; open: boolean; lastReqRef: React.RefObj
|
|||
<span>{t.name}</span>
|
||||
</span>
|
||||
<span className='format-tag'>FLAC/MP3</span>
|
||||
<TrackDownload url={t.url} lastReqRef={lastReqRef} />
|
||||
<TrackDownload track={t} lastReqRef={lastReqRef} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -68,27 +69,43 @@ function TrackList(props: { url: string; open: boolean; lastReqRef: React.RefObj
|
|||
)
|
||||
}
|
||||
|
||||
function TrackDownload(props: { url: string; lastReqRef: React.RefObject<number> }) {
|
||||
const { url, lastReqRef } = props
|
||||
function TrackDownload(props: { track: TrackRow; lastReqRef: React.RefObject<number> }) {
|
||||
const { track, lastReqRef } = props
|
||||
const [downloadUrl, setDownloadUrl] = useState<string>()
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
async function fetchUrl() {
|
||||
try {
|
||||
setLoading(true)
|
||||
const dlUrl = await getDownloadUrl(url, lastReqRef)
|
||||
const dlUrl = getCors(await getDownloadUrl(track.url, lastReqRef))
|
||||
|
||||
setDownloadUrl(dlUrl)
|
||||
downloadTrack(dlUrl)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return downloadUrl ? (
|
||||
<a className='download-btn' href={downloadUrl} target='_blank' rel='noopener noreferrer' download>
|
||||
Download
|
||||
</a>
|
||||
) : (
|
||||
<button className='download-btn' disabled={loading} onClick={fetchUrl}>
|
||||
async function downloadTrack(dlUrl: string) {
|
||||
if (!dlUrl) return
|
||||
|
||||
const dlBlob = await (await fetch(dlUrl)).blob()
|
||||
const fileURL = window.URL.createObjectURL(dlBlob)
|
||||
|
||||
const fileLink = document.createElement('a')
|
||||
fileLink.href = fileURL
|
||||
fileLink.setAttribute('download', track.fileName)
|
||||
document.body.appendChild(fileLink)
|
||||
fileLink.click()
|
||||
fileLink.remove()
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className='download-btn'
|
||||
disabled={loading}
|
||||
onClick={downloadUrl ? () => downloadTrack(downloadUrl) : fetchUrl}
|
||||
>
|
||||
{loading ? <Loader show /> : 'Download'}
|
||||
</button>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ import Downloader from '../components/Downloader'
|
|||
flex-shrink: 0;
|
||||
display: flex;
|
||||
column-gap: 4px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.track {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
import * as cheerio from 'cheerio'
|
||||
// import JSZipUtils from 'jszip-utils'
|
||||
import JSZip from 'jszip'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
import { USER_AGENTS } from './consts'
|
||||
import type { AlbumRow } from './types'
|
||||
import type { AlbumRow, TrackRow } from './types'
|
||||
|
||||
function getRandom(list: any[]) {
|
||||
return list[Math.floor(Math.random() * list.length)]
|
||||
}
|
||||
|
||||
export const getCors = (url: string) => `https://cors.squid.wtf/${url}`
|
||||
|
||||
export function politeFetch(url: string, lastReqRef: React.RefObject<number>) {
|
||||
const elapsed = Date.now() - lastReqRef.current
|
||||
const corsUrl = `https://cors.squid.wtf/${url}`
|
||||
const corsUrl = getCors(url)
|
||||
|
||||
const headers = {
|
||||
'User-Agent': getRandom(USER_AGENTS),
|
||||
|
|
@ -57,7 +61,7 @@ export async function searchAlbums(query: string, lastReqRef: React.RefObject<nu
|
|||
export async function getTracks(url: string, lastReqRef: React.RefObject<number>) {
|
||||
const albumHtml = await (await politeFetch(url, lastReqRef)).text()
|
||||
const $ = cheerio.load(albumHtml)
|
||||
const tracks: { name: string; url: string; format: string }[] = []
|
||||
const tracks: TrackRow[] = []
|
||||
|
||||
$('table#songlist tr:has(a[href$=".mp3"]), table#songlist tr:has(a[href$=".flac"])').each((index, row) => {
|
||||
const link = $(row).find('a[href$=".mp3"], a[href$=".flac"]')
|
||||
|
|
@ -66,11 +70,13 @@ export async function getTracks(url: string, lastReqRef: React.RefObject<number>
|
|||
const trackUrl = link.attr('href')!.startsWith('http')
|
||||
? link.attr('href')!
|
||||
: `https://downloads.khinsider.com${link.attr('href')}`
|
||||
const trackPath = trackUrl.split('/')
|
||||
|
||||
tracks.push({
|
||||
name: link.first().text().trim(),
|
||||
url: trackUrl,
|
||||
format: link.attr('href')!.toLowerCase().endsWith('.flac') ? 'flac' : 'mp3'
|
||||
format: link.attr('href')!.toLowerCase().endsWith('.flac') ? 'flac' : 'mp3',
|
||||
fileName: decodeURI(decodeURI(trackPath[trackPath.length - 1]))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -103,6 +109,8 @@ export async function getDownloadUrl(trackPageUrl: string, lastReqRef: React.Ref
|
|||
if (mp3Links.length > 0) {
|
||||
return mp3Links[0]
|
||||
}
|
||||
|
||||
throw new Error('Failed to fetch download url')
|
||||
}
|
||||
|
||||
export async function copyDownloadUrls(albumUrl: string, lastReqRef: React.RefObject<number>) {
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@ export interface TrackRow {
|
|||
name: string
|
||||
url: string
|
||||
format: string
|
||||
fileName: string
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue