From 108b37dfe3fcac663eb663ca4ece6b9144f0341d Mon Sep 17 00:00:00 2001 From: Jorge Vargas Date: Mon, 10 Mar 2025 18:56:09 -0600 Subject: [PATCH 1/3] Add search and album loaders --- src/components/Album.tsx | 36 ++++++++++++++++++++++++++---------- src/pages/index.astro | 20 +++++++++++--------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/components/Album.tsx b/src/components/Album.tsx index 58104ea..d943d7a 100644 --- a/src/components/Album.tsx +++ b/src/components/Album.tsx @@ -2,14 +2,17 @@ import { useEffect, useState } from 'react' import { getTracks } from 'utils/search' import type { AlbumRow, TrackRow } from 'utils/types' +import Loader from './Loader' export default function Album(props: { album: AlbumRow; lastReqRef: React.RefObject }) { const { album, lastReqRef } = props const [open, setOpen] = useState(false) return ( -
setOpen((o) => !o)}> -

{album.name}

+
+
setOpen((o) => !o)}> +

{album.name}

+
) @@ -18,10 +21,16 @@ export default function Album(props: { album: AlbumRow; lastReqRef: React.RefObj function TrackList(props: { url: string; open: boolean; lastReqRef: React.RefObject }) { const { url, open, lastReqRef } = props const [tracks, setTracks] = useState(null) + const [loading, setLoading] = useState(false) async function fetchTracks() { - const tracks = await getTracks(url, lastReqRef) - setTracks(tracks) + try { + setLoading(true) + const tracks = await getTracks(url, lastReqRef) + setTracks(tracks) + } finally { + setLoading(false) + } } useEffect(() => { @@ -31,19 +40,26 @@ function TrackList(props: { url: string; open: boolean; lastReqRef: React.RefObj if (!open) return null return ( -
- {tracks - ? tracks.map((t, i) => ( +
+ {loading ? ( +
+ +
+ ) : null} + {tracks && open ? ( +
+ {tracks.map((t, i) => (
- {(i + 1).toString().padStart(2, '0').slice(-2)}. + {(i + 1).toString().padStart(2, '0').slice(-2)}. {t.name} FLAC/MP3
- )) - : null} + ))} +
+ ) : null}
) } diff --git a/src/pages/index.astro b/src/pages/index.astro index bd07ed1..c7ad26f 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -57,7 +57,7 @@ import Downloader from '../components/Downloader' width: 30px; height: 30px; animation: spin 1s linear infinite; - margin: 20px auto; + margin: auto; } @keyframes spin { 0% { @@ -81,13 +81,13 @@ import Downloader from '../components/Downloader' border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); } - .album { + .album-title { padding: 15px; border-bottom: 1px solid #333; cursor: pointer; transition: background 0.2s; } - .album:hover { + .album-title:hover { background-color: #333; } .track { @@ -136,10 +136,7 @@ import Downloader from '../components/Downloader' text-align: center; color: #858585; } - .album-title { - font-weight: 500; - margin-bottom: 4px; - color: #e0e0e0; + .album { } .album-meta { display: flex; @@ -163,10 +160,15 @@ import Downloader from '../components/Downloader' background-color: #563d7c; } .results h3 { - padding: 0 15px; - margin: 0 0 15px 0; + margin: 0; font-size: 1.1em; } + .album-open { + background-color: #4a4a4a; + } + .album-open .loader-container { + padding: 6px; + } From bab630224772414663db41e796e5e2d9215bad1d Mon Sep 17 00:00:00 2001 From: Jorge Vargas Date: Mon, 10 Mar 2025 19:35:55 -0600 Subject: [PATCH 2/3] Implement individual song download --- src/components/Album.tsx | 30 ++++++++++++++++++++++++++---- src/components/Downloader.tsx | 4 ++-- src/pages/index.astro | 9 +++++++++ src/utils/search.ts | 28 ++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 6 deletions(-) diff --git a/src/components/Album.tsx b/src/components/Album.tsx index d943d7a..a5ada66 100644 --- a/src/components/Album.tsx +++ b/src/components/Album.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react' -import { getTracks } from 'utils/search' +import { getDownloadUrl, getTracks } from 'utils/search' import type { AlbumRow, TrackRow } from 'utils/types' import Loader from './Loader' @@ -49,13 +49,13 @@ function TrackList(props: { url: string; open: boolean; lastReqRef: React.RefObj {tracks && open ? (
{tracks.map((t, i) => ( -
+
{(i + 1).toString().padStart(2, '0').slice(-2)}. {t.name} FLAC/MP3 - +
))}
@@ -65,5 +65,27 @@ function TrackList(props: { url: string; open: boolean; lastReqRef: React.RefObj } function TrackDownload(props: { url: string; lastReqRef: React.RefObject }) { - return + const { url, lastReqRef } = props + const [downloadUrl, setDownloadUrl] = useState() + const [loading, setLoading] = useState(false) + + async function fetchUrl() { + try { + setLoading(true) + const dlUrl = await getDownloadUrl(url, lastReqRef) + setDownloadUrl(dlUrl) + } finally { + setLoading(false) + } + } + + return downloadUrl ? ( + + Download + + ) : ( + + ) } diff --git a/src/components/Downloader.tsx b/src/components/Downloader.tsx index 13feb1c..9f2aaaa 100644 --- a/src/components/Downloader.tsx +++ b/src/components/Downloader.tsx @@ -53,8 +53,8 @@ export default function Downloader() {
) : null}
- {results.map((r) => ( - + {results.map((r, i) => ( + ))}
diff --git a/src/pages/index.astro b/src/pages/index.astro index c7ad26f..a4a7a43 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -117,6 +117,7 @@ import Downloader from '../components/Downloader' border-radius: 4px; cursor: pointer; transition: background 0.2s; + font-size: 13px; } .download-btn:hover { background-color: #218838; @@ -153,12 +154,20 @@ import Downloader from '../components/Downloader' display: flex; gap: 8px; } + a.download-btn { + text-decoration: none; + } .download-btn.flac { background-color: #6f42c1; } .download-btn.flac:hover { background-color: #563d7c; } + .download-btn .loader { + height: 8px; + width: 8px; + margin: 2px 8px; + } .results h3 { margin: 0; font-size: 1.1em; diff --git a/src/utils/search.ts b/src/utils/search.ts index 4679e0a..0c87b44 100644 --- a/src/utils/search.ts +++ b/src/utils/search.ts @@ -75,3 +75,31 @@ export async function getTracks(url: string, lastReqRef: React.RefObject return tracks } + +export async function getDownloadUrl(trackPageUrl: string, lastReqRef: React.RefObject) { + const trackPageHtml = await (await politeFetch(trackPageUrl, lastReqRef)).text() + const $ = cheerio.load(trackPageHtml) + + const audioLinks: string[] = [] + + $('a[href$=".mp3"], a[href$=".flac"]').each((index, link) => { + let href = $(link).attr('href') + if (!href) return + + href = decodeURIComponent(href) + if (!href.startsWith('http')) { + href = `https://downloads.khinsider.com${href}` + } + audioLinks.push(href) + }) + + const flacLinks = audioLinks.filter((link) => link.endsWith('.flac')) + if (flacLinks.length > 0) { + return flacLinks[0] + } + + const mp3Links = audioLinks.filter((link) => link.endsWith('.mp3')) + if (mp3Links.length > 0) { + return mp3Links[0] + } +} From 98d8442b01809a4b3d3ddaee154cc31bf438bb00 Mon Sep 17 00:00:00 2001 From: Jorge Vargas Date: Mon, 10 Mar 2025 19:41:59 -0600 Subject: [PATCH 3/3] Fix track names --- src/utils/search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/search.ts b/src/utils/search.ts index 0c87b44..1b0eaef 100644 --- a/src/utils/search.ts +++ b/src/utils/search.ts @@ -67,7 +67,7 @@ export async function getTracks(url: string, lastReqRef: React.RefObject : `https://downloads.khinsider.com${link.attr('href')}` tracks.push({ - name: link.text().trim(), + name: link.first().text().trim(), url: trackUrl, format: link.attr('href')!.toLowerCase().endsWith('.flac') ? 'flac' : 'mp3' })