Compare commits
3 Commits
79eb457248
...
b8680d13f4
Author | SHA1 | Date |
---|---|---|
Jill | b8680d13f4 | |
Jill | f316b8542e | |
Jill | f5a413cd99 |
|
@ -12,8 +12,13 @@
|
|||
|
||||
let loading = false;
|
||||
|
||||
let searchAlbums = [];
|
||||
let total;
|
||||
let next;
|
||||
let query;
|
||||
|
||||
async function search(event) {
|
||||
const query = event.target.value;
|
||||
query = event.target.value;
|
||||
|
||||
searchAlbums = [];
|
||||
loading = true;
|
||||
|
@ -24,14 +29,33 @@
|
|||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
loading = false;
|
||||
searchAlbums = data;
|
||||
searchAlbums = data.data;
|
||||
total = data.total;
|
||||
next = data.next;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
let searchAlbums = [];
|
||||
async function searchMore() {
|
||||
loading = true;
|
||||
|
||||
try {
|
||||
let url = dev ? (new URL('http://localhost:4500/api/search')) : (new URL('/api/search', window.location.origin));
|
||||
url.searchParams.set('search', query);
|
||||
url.searchParams.set('index', next);
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
loading = false;
|
||||
searchAlbums = [...searchAlbums, ...data.data];
|
||||
total = data.total;
|
||||
next = data.next;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<SvelteToast options={{
|
||||
|
@ -45,15 +69,21 @@
|
|||
<span class="main">
|
||||
<Header/>
|
||||
<Search onChange={search}/>
|
||||
{#if loading}
|
||||
<Loading/>
|
||||
{/if}
|
||||
{#if searchAlbums.length > 0}
|
||||
<div class="albums">
|
||||
{#each searchAlbums as album, i}
|
||||
<Album title={album.title} id={album.id} cover={album.cover} artist={album.artist}/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if total > searchAlbums.length}
|
||||
<div style="padding: 1.5em">
|
||||
<button on:click={searchMore}>Load more</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if loading}
|
||||
<Loading/>
|
||||
{/if}
|
||||
</span>
|
||||
</main>
|
||||
|
|
|
@ -23,6 +23,14 @@ body {
|
|||
color: rgb(151, 151, 255);
|
||||
filter: drop-shadow( 0px 0px 2px #8383F3);
|
||||
}
|
||||
button {
|
||||
background-color: #161627;
|
||||
color: #fff;
|
||||
border-color: #161627;
|
||||
}
|
||||
button:hover {
|
||||
border-color: rgb(131, 131, 243);
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
body {
|
||||
|
@ -40,6 +48,14 @@ body {
|
|||
color: #f484b6;
|
||||
filter: drop-shadow( 0px 0px 2px #f484b6);
|
||||
}
|
||||
button {
|
||||
background-color: #ffffff;
|
||||
color: #1e1e2d;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #ea74ac;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -57,4 +73,14 @@ body {
|
|||
.link {
|
||||
cursor: pointer;
|
||||
transition: 0.1s color ease-out;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
border-width: 2px;
|
||||
border-radius: 8px;
|
||||
border-style: solid;
|
||||
font-size: large;
|
||||
padding: 0.25em;
|
||||
transition: 0.1s border-color ease-out;
|
||||
}
|
|
@ -55,22 +55,24 @@
|
|||
</div>
|
||||
<div class="small">{artist.name}</div>
|
||||
</span>
|
||||
{#if !hideDownload || $butShowThisDownloadLinkInstead}
|
||||
{#if $butShowThisDownloadLinkInstead}
|
||||
<a href={$butShowThisDownloadLinkInstead} target="_blank" rel="noopener" download="{$butShowThisDownloadLinkInstead.split('/').slice(-1)}">
|
||||
<div class="album-download" title="Download">
|
||||
<div class="album-inner-inner-bottom">
|
||||
{#if !hideDownload || $butShowThisDownloadLinkInstead}
|
||||
{#if $butShowThisDownloadLinkInstead}
|
||||
<a href={$butShowThisDownloadLinkInstead} target="_blank" rel="noopener" download="{$butShowThisDownloadLinkInstead.split('/').slice(-1)}">
|
||||
<div class="album-download" title="Download">
|
||||
<Icon icon={faDownload}/>
|
||||
</div>
|
||||
</a>
|
||||
{:else}
|
||||
<div class="album-download" title="Download" on:click={() => startDownload(id, {title, artist, cover}, true)}>
|
||||
<Icon icon={faDownload}/>
|
||||
</div>
|
||||
</a>
|
||||
{:else}
|
||||
<div class="album-download" title="Download" on:click={() => startDownload(id, {title, artist, cover}, true)}>
|
||||
<Icon icon={faDownload}/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="album-image-wrapper">
|
||||
<img class="album-image" width="128" height="128" src="https://e-cdns-images.dzcdn.net/images/cover/{cover}/128x128-000000-80-0-0.jpg" alt="Cover for '{title}'">
|
||||
<img class="album-image" class:explicit={album && album.explicitCover === 1} width="128" height="128" src="https://e-cdns-images.dzcdn.net/images/cover/{cover}/128x128-000000-80-0-0.jpg" alt="Cover for '{title}'">
|
||||
</div>
|
||||
</div>
|
||||
<div class="album-inner-bottom">
|
||||
|
@ -90,7 +92,7 @@
|
|||
{/if}
|
||||
{#if album && !short}
|
||||
{#each album.tracks as track}
|
||||
<Track id={track.id} title={track.title} duration={track.duration} artist={track.artist} cover={cover} album={title} albumArtist={artist.name}/>
|
||||
<Track id={track.id} title={track.title} duration={track.duration} artist={track.contributors.map(a => a && a.name).join(', ')} cover={cover} album={title} albumArtist={artist.name} explicit={track.explicit === 1}/>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -113,13 +115,20 @@
|
|||
.album-inner-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.5em;
|
||||
gap: 1em;
|
||||
}
|
||||
.album-inner-bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
.album-inner-inner-bottom {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.album.short {
|
||||
border-radius: 10px 10px 10px 10px;
|
||||
}
|
||||
|
@ -182,6 +191,14 @@
|
|||
align-self: stretch;
|
||||
}
|
||||
|
||||
.explicit {
|
||||
transition: 0.2s filter ease-out;
|
||||
filter: blur(8px);
|
||||
}
|
||||
.explicit:hover {
|
||||
filter: blur(0px);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.album-download {
|
||||
color: #fff;
|
||||
|
@ -214,10 +231,6 @@
|
|||
.album:hover .album-image-wrapper {
|
||||
border: 0px solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
.album-bottom {
|
||||
background-color: #112;
|
||||
border-left: 0rem solid rgb(131, 131, 243);
|
||||
}
|
||||
.progress-state {
|
||||
background-color: #0a0a0f;
|
||||
}
|
||||
|
@ -255,10 +268,6 @@
|
|||
.album:hover .album-image-wrapper {
|
||||
border: 0px solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
.album-bottom {
|
||||
background-color: #ffffff;
|
||||
border-left: 0rem solid #ea74ac;
|
||||
}
|
||||
.progress-state {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
export let duration;
|
||||
export let album;
|
||||
export let albumArtist;
|
||||
export let explicit;
|
||||
|
||||
import { formatTime } from './format';
|
||||
|
||||
|
@ -24,6 +25,9 @@
|
|||
{/if}
|
||||
</span>
|
||||
<span class="track-right">
|
||||
{#if explicit}
|
||||
<div class="tag">EXPLICIT</div>
|
||||
{/if}
|
||||
<span class="small">{formatTime(duration)}</span>
|
||||
<span class="track-download" title="Download" on:click={() => startDownload(id, {title, artist: {name: artist}, cover, album}, false)}>
|
||||
<Icon icon={faDownload}/>
|
||||
|
@ -43,17 +47,15 @@
|
|||
align-items: center;
|
||||
font-size: large;
|
||||
transition: 0.05s background-color ease-out, 0.1s border-left ease-out;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.track:nth-last-child(1) {
|
||||
border-bottom: none;
|
||||
border-radius: 0px 0px 15px 15px;
|
||||
}
|
||||
|
||||
.track-left {
|
||||
flex: 1 1 0px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.track-right {
|
||||
flex: 0 0 auto;
|
||||
|
@ -69,6 +71,14 @@
|
|||
transition: 0.1s filter ease-out, 0.1s color ease-out;
|
||||
}
|
||||
|
||||
.tag {
|
||||
text-transform: uppercase;
|
||||
border-radius: 10px;
|
||||
padding: 0.2em;
|
||||
font-size: small;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.track-download {
|
||||
color: #fff;
|
||||
|
@ -79,13 +89,19 @@
|
|||
filter: drop-shadow( 0px 0px 6px #8383F3);
|
||||
}
|
||||
.track {
|
||||
border-bottom: 3px solid #0a0a0f;
|
||||
border-left: 0rem solid rgb(131, 131, 243);
|
||||
}
|
||||
.track:hover {
|
||||
background-color: #161627;
|
||||
border-left: 0.25rem solid rgb(131, 131, 243);
|
||||
}
|
||||
.tag {
|
||||
background-color: #f0f0f0;
|
||||
color: #0a0a0f;
|
||||
}
|
||||
.track {
|
||||
background-color: #112;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
|
@ -98,12 +114,18 @@
|
|||
filter: drop-shadow( 0px 0px 6px #f484b6);
|
||||
}
|
||||
.track {
|
||||
border-bottom: 3px solid #f0f0f0;
|
||||
border-left: 0rem solid #ea74ac;
|
||||
}
|
||||
.track:hover {
|
||||
background-color: #fafafa;
|
||||
border-left: 0.25rem solid #ea74ac;
|
||||
}
|
||||
.tag {
|
||||
background-color: #0a0a0f;
|
||||
color: #fff;
|
||||
}
|
||||
.track {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -24,15 +24,20 @@ router.get('/api/album', async (req, res) => {
|
|||
id: album.id,
|
||||
title: album.title,
|
||||
link: album.link,
|
||||
tracks: album.tracks.data.map(t => {
|
||||
releaseDate: album.release_date,
|
||||
explicitCover: album.explicit_content_cover,
|
||||
tracks: await Promise.all(album.tracks.data.map(async ({id}) => {
|
||||
const t = await deezerInstance.api.get_track(id);
|
||||
return {
|
||||
id: t.id,
|
||||
title: t.title,
|
||||
duration: t.duration,
|
||||
link: t.link,
|
||||
artist: t.artist.name,
|
||||
explicit: t.explicit_content_lyrics,
|
||||
contributors: t.contributors
|
||||
};
|
||||
})
|
||||
}))
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -12,19 +12,22 @@ router.get('/api/search', async (req, res) => {
|
|||
if (Array.isArray(req.query.search)) req.query.search = req.query.search.join('');
|
||||
req.query.search = req.query.search as string;
|
||||
|
||||
let s: [Album];
|
||||
let s: DeezerResponse<[Album]>;
|
||||
try {
|
||||
s = searchcache[req.query.search] || (await deezerInstance.api.search_album(req.query.search, {
|
||||
s = searchcache[req.query.search+'/'+req.query.index] || (await deezerInstance.api.search_album(req.query.search, {
|
||||
limit: config.limits.searchLimit || 15,
|
||||
})).data;
|
||||
index: parseInt(req.query.index as string) || undefined
|
||||
}));
|
||||
} catch(err) {
|
||||
logger.error((err as Error).toString());
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
if (!searchcache[req.query.search]) searchcache[req.query.search] = s;
|
||||
searchcache[req.query.search+'/'+req.query.index] = s;
|
||||
|
||||
let format = s.map(s => {
|
||||
return {
|
||||
let format = {
|
||||
next: s.next && s.next.split('=').pop(), // dumb workaround of having to use regexes because i hate regexes
|
||||
total: s.total,
|
||||
data: s.data.map(s => ({
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
cover: s.md5_image,
|
||||
|
@ -32,8 +35,8 @@ router.get('/api/search', async (req, res) => {
|
|||
id: s.artist.id,
|
||||
name: s.artist.name
|
||||
},
|
||||
};
|
||||
});
|
||||
}))
|
||||
};
|
||||
|
||||
res.send(format);
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ import { deezerInstance } from './deemix';
|
|||
|
||||
export const port = config.server.port || 4500;
|
||||
|
||||
export let searchcache: Record<string, [Album]> = {};
|
||||
export let searchcache: Record<string, DeezerResponse<[Album]>> = {};
|
||||
export let albumcache: Record<string, Album> = {};
|
||||
export let trackcache: Record<string, Track> = {};
|
||||
|
||||
|
|
Loading…
Reference in New Issue