This commit is contained in:
Jill 2022-02-23 20:18:13 +03:00
parent 1c0b40f410
commit 6d76619e75
39 changed files with 1725 additions and 706 deletions

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/public/build/

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
app/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

124
app/public/global.css Normal file
View File

@ -0,0 +1,124 @@
html, body {
position: relative;
width: 100%;
height: 100%;
}
body {
color: #333;
margin: 0;
padding: 8px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
a {
color: rgb(0,100,200);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:visited {
color: rgb(0,80,160);
}
label {
display: block;
}
input, button, select, textarea {
font-family: inherit;
font-size: inherit;
-webkit-padding: 0.4em 0;
padding: 0.4em;
margin: 0 0 0.5em 0;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 2px;
}
input:disabled {
color: #ccc;
}
button {
color: #333;
background-color: #f4f4f4;
outline: none;
}
button:disabled {
color: #999;
}
button:not(:disabled):active {
background-color: #ddd;
}
button:focus {
border-color: #666;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
* {
transition: 0.1s background-color ease-out;
}
.link {
cursor: pointer;
transition: 0.1s color ease-out, 0.1s filter ease-out;
}
.small {
font-size: medium;
}
.big {
font-weight: bold;
}
#albums {
margin-top: 20px;
width: 600px;
max-width: 98%;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #0a0a0f;
color: #fff;
accent-color: rgb(131, 131, 243);
}
.small {
color: #c8c8d2;
}
.link {
color:#ea74ac;
}
.link:hover {
color: #f484b6;
filter: drop-shadow( 0px 0px 2px #f484b6);
}
}
@media (prefers-color-scheme: light) {
body {
background-color: #f0f0f0;
color: #1e1e2d;
accent-color: #ea74ac;
}
.small {
color: #888;
}
.link {
color:rgb(131, 131, 243);
}
.link:hover {
color: rgb(151, 151, 255);
filter: drop-shadow( 0px 0px 2px #8383F3);
}
}

18
app/public/index.html Normal file
View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>

166
app/src/Album.svelte Normal file
View File

@ -0,0 +1,166 @@
<style>
.album {
padding: 15px;
margin: 2px;
font-size: large;
border-radius: 10px 10px 0px 0px;
transition: 0.1s border-left ease-out, 0.1s background-color ease-in-out;
min-height: 96px;
display: flex;
justify-content: space-between;
}
.album-image {
width: auto;
height: 100%;
border-radius: 10px;
transition: 0.1s border ease-out, 0.1s box-shadow ease-out;
width: 96px;
height: 96px;
}
.album-image-wrapper {
transition: 0.1s border ease-out;
}
.album-metadata {
display: flex;
flex-direction: column;
width: 100%;
}
.metadata {
height: 100%;
}
.album-download {
width: 32px;
height: 32px;
cursor: pointer;
transition: 0.1s filter ease-out;
}
.album-bottom {
padding: 0px;
margin-left: 2px;
margin-right: 2px;
border-radius: 0px 0px 10px 10px;
transition: 0.1s border-left ease-out;
}
.album-download {
vertical-align: top;
}
@media (prefers-color-scheme: dark) {
.album {
background-color: #161627;
box-shadow: 0px 0px 12px #000;
border-left: 0rem solid rgb(131, 131, 243);
}
.album:hover {
border-left: 0.25rem solid rgb(131, 131, 243);
background-color: #181829;
}
.album-image {
border: 0px solid rgb(131, 131, 243);
box-shadow: 0px 0px 15px #000;
}
.album:hover .album-image {
border: 2px solid rgb(131, 131, 243);
box-shadow: 0px 0px 30px #000;
}
.album-image-wrapper {
border: 2px solid rgba(0, 0, 0, 0);
}
.album:hover .album-image-wrapper {
border: 0px solid rgba(0, 0, 0, 0);
}
.album-download {
filter: invert(100%);
}
.album-download:hover {
filter: invert(50%) sepia(58%) saturate(893%) hue-rotate(206deg) brightness(99%) contrast(92%) drop-shadow( 0px 0px 5px #8383F3);
}
.album-bottom {
background-color: #112;
border-left: 0rem solid rgb(131, 131, 243);
}
}
@media (prefers-color-scheme: light) {
.album {
background-color: #ffffff;
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.2);
border-left: 0rem solid #ea74ac;
}
.album:hover {
border-left: 0.25rem solid #ea74ac;
background-color: #fafafa;
}
.album-image {
border: 0px solid #ea74ac;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
}
.album:hover .album-image {
border: 2px solid #ea74ac;
box-shadow: 0px 0px 30px rgba(0, 0, 0, 0.2);
}
.album-image-wrapper {
border: 2px solid rgba(0, 0, 0, 0);
}
.album:hover .album-image-wrapper {
border: 0px solid rgba(0, 0, 0, 0);
}
.album-download {
filter: none;
}
.album-download:hover {
filter: invert(65%) sepia(45%) saturate(772%) hue-rotate(295deg) brightness(103%) contrast(91%) drop-shadow( 0px 0px 5px #f484b6);
}
.album-bottom {
background-color: #ffffff;
border-left: 0rem solid #ea74ac;
}
}
</style>
<script>
import {onMount} from 'svelte/internal';
import Track from './Track.svelte';
import {queryTracks} from './api';
import {downloadAlbum} from './download';
import Loading from './Loading.svelte';
export let id;
export let title;
export let cover;
export let artist;
let tracks = [];
onMount(async () => {
let album = await queryTracks(id);
tracks = album.tracks;
});
</script>
<div class="album" id="album-{id}">
<div class="album-metadata">
<span class="metadata">
<span class="big">{title}</span>
<br>
<span class="small">by {artist}</span>
</span>
<img class="album-download" width="48" height="48" src="assets/download.svg" alt="Download" on:click={() => downloadAlbum(id)}>
</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">
</div>
</div>
<div class="album-bottom" id="album-bottom-{id}">
{#if tracks.length === 0}
<Loading/>
{:else}
{#each tracks as track}
<Track id={track.id} title={track.title} artist={track.artist} duration={track.duration}/>
{/each}
{/if}
</div>

32
app/src/App.svelte Normal file
View File

@ -0,0 +1,32 @@
<script>
import Search from './Search.svelte';
import Album from './Album.svelte';
import Download from './Download.svelte';
import {albums, display, displays, downloading} from './stores';
</script>
<style>
content {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
</style>
<main>
<content>
{#if $display === displays.AlbumSearch}
<Search/>
<div id="albums">
{#each $albums as album}
<Album id={album.id} title={album.title} cover={album.cover} artist={album.artist.name}/><br>
{/each}
</div>
{/if}
{#if $display === displays.Download && $downloading}
<Download id={$downloading.id} isAlbum={$downloading.isAlbum}/>
{/if}
</content>
</main>

45
app/src/Download.svelte Normal file
View File

@ -0,0 +1,45 @@
<script>
import { onMount } from 'svelte/internal';
import { writable } from 'svelte/store';
import Loading from './Loading.svelte';
import { startWebsocket } from './websocket'
export let id;
export let isAlbum;
let title;
let artist;
let coverArt;
let log = writable([]);
let progress = writable(0);
onMount(async () => {
startWebsocket(id, isAlbum, s => {log.update(l => {return [...l, s].slice(-5)})}, c => {coverArt = c}, t => {title = t}, a => {artist = a}, p => {progress.set(p)});
});
</script>
{#if title && artist && coverArt}
<div class="album album-downloading" id="album-{id}">
<div class="album-metadata">
<span class="metadata">
<span class="big">{title}</span>
<br>
<span class="small">by {artist}</span>
</span>
<div id="progress-state">
{#each $log as l}
<br>{l}
{/each}
</div>
</div>
<div class="album-image-wrapper">
<img class="album-image" width="128" height="128" src="{coverArt}" alt="Cover">
</div>
</div>
{#if $progress > 0}
<div id="progress-bar"><div id="progress-bar-inner" style="height:100%;width:{$progress}%"></div></div>
{/if}
{:else}
<Loading/>
{/if}

51
app/src/Loading.svelte Normal file
View File

@ -0,0 +1,51 @@
<style>
.lds-ring {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@media (prefers-color-scheme: dark) {
.lds-ring div {
border: 8px solid #fff;
border-color: #fff transparent transparent transparent;
}
}
@media (prefers-color-scheme: light) {
.lds-ring div {
border: 8px solid #1e1e2d;
border-color: #1e1e2d transparent transparent transparent;
}
}
</style>
<div class="lds-ring"><div></div><div></div><div></div><div></div></div>

55
app/src/Search.svelte Normal file
View File

@ -0,0 +1,55 @@
<script>
import {clearAlbums, pushAlbum} from './stores';
import {querySearch} from './api';
let searchbar;
async function search() {
const value = searchbar.value;
clearAlbums();
if (value !== '') {
let albums = await querySearch(value);
albums.forEach(pushAlbum);
}
}
</script>
<style>
@media (prefers-color-scheme: dark) {
input {
background-color: #112;
color: #fff;
box-shadow: 0px 0px 15px #000;
border-bottom: 0rem solid rgb(131, 131, 243);
}
input:focus, input:hover {
border-bottom: 0.25rem solid rgb(131, 131, 243);
background-color: #161626;
}
}
@media (prefers-color-scheme: light) {
input {
background-color: #ffffff;
color: #1e1e2d;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
border-bottom: 0rem solid #ea74ac;
}
input:focus, input:hover {
border-bottom: 0.25rem solid #ea74ac;
background-color: #fafafa;
}
}
input {
margin: 5px;
width: 550px;
max-width: 98%;
padding: 15px;
font-size: x-large;
border: none;
border-radius: 7px;
transition: 0.1s border-bottom ease-out, 0.1s background-color ease-in-out;
}
</style>
<input type="search" id="album-search" name="q" bind:this={searchbar} on:change={search}>

98
app/src/Track.svelte Normal file
View File

@ -0,0 +1,98 @@
<style>
.track .album-download {
position: relative;
top: 20px;
}
.track {
padding: 10px;
padding-top: 6px;
padding-bottom: 6px;
margin: none;
display: flex;
justify-content: space-between;
font-size: large;
transition: 0.05s background-color ease-out, 0.1s border-left ease-out;
}
.track:nth-last-child(1) {
border-bottom: none;
border-radius: 0px 0px 15px 15px;
}
.track .album-download {
vertical-align: top;
}
.track-download-wrapper {
position: relative;
bottom: 20px;
height: 50%;
width: auto;
}
.album-download {
width: 32px;
height: 32px;
cursor: pointer;
transition: 0.1s filter ease-out;
position: relative;
top: 20px;
}
@media (prefers-color-scheme: dark) {
.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);
}
.album-download {
filter: invert(100%);
}
.album-download:hover {
filter: invert(50%) sepia(58%) saturate(893%) hue-rotate(206deg) brightness(99%) contrast(92%) drop-shadow( 0px 0px 5px #8383F3);
}
}
@media (prefers-color-scheme: light) {
.track {
border-bottom: 3px solid #f0f0f0;
border-left: 0rem solid #ea74ac;
}
.track:hover {
background-color: #fafafa;
border-left: 0.25rem solid #ea74ac;
}
.album-download {
filter: none;
}
.album-download:hover {
filter: invert(65%) sepia(45%) saturate(772%) hue-rotate(295deg) brightness(103%) contrast(91%) drop-shadow( 0px 0px 5px #f484b6);
}
}
</style>
<script>
import {downloadTrack} from './download';
export let id;
export let title;
export let artist;
export let duration;
function formatTime(s) {
return Math.floor(s / 60).toString().padStart(2, '0') + ':' + (s % 60).toString().padStart(2, '0');
}
</script>
<div class="track" id="track-{id}">
<span>{artist} - {title}</span>
<span>
<span class="track-download-wrapper" on:click={downloadTrack}>
<img class="album-download" width="32" height="32" src="assets/download.svg" alt="Download">
</span>
{formatTime(duration)}
</span>
</div>

9
app/src/api.js Normal file
View File

@ -0,0 +1,9 @@
export async function querySearch(q) {
let response = await fetch(`http://localhost:4500/api/search?search=${encodeURI(q)}`);
return await response.json();
}
export async function queryTracks(id) {
let response = await fetch(`http://localhost:4500/api/album?id=${id}`);
return await response.json();
}

10
app/src/download.js Normal file
View File

@ -0,0 +1,10 @@
import {downloading, display, displays} from './stores';
export function downloadAlbum(id) {
display.set(displays.Download);
downloading.set({id: id, isAlbum: true});
}
export function downloadTrack(id) {
display.set(displays.Download);
downloading.set({id: id, isAlbum: false});
}

9
app/src/main.js Normal file
View File

@ -0,0 +1,9 @@
import App from './App.svelte';
const app = new App({
target: document.body,
props: {
}
});
export default app;

19
app/src/stores.js Normal file
View File

@ -0,0 +1,19 @@
import {writable} from 'svelte/store';
export const albums = writable([]);
export function clearAlbums() {
albums.set([]);
}
export function pushAlbum(id) {
albums.update((l) => [...l, id]);
}
export const displays = {
AlbumSearch: 0,
Download: 1,
}
export let display = writable(displays.AlbumSearch);
export let downloading = writable(null);

37
app/src/websocket.js Normal file
View File

@ -0,0 +1,37 @@
/*
function getWebsocketLocation() {
return window.window.location.toString().replace('https://', 'wss://').replace('http://', 'ws://');
}
*/
function getWebsocketLocation() {
return 'ws://localhost:4500/';
}
export function startWebsocket(id, isAlbum, log, coverArt, title, artist, progress) {
let type = isAlbum ? 'album' : 'track';
const ws = new WebSocket(`${getWebsocketLocation()}api/${type}?id=${id}`);
ws.onmessage = (m) => {
const d = JSON.parse(m.data);
console.log(d);
if (d.key === 'downloadInfo') {
log(`[${d.data.data.title}] ${d.data.state}`);
} else if (d.key === 'updateQueue') {
progress(d.data.progress);
} else if (d.key === 'coverArt') {
log('Fetched cover art');
coverArt(d.data);
} else if (d.key === 'metadata') {
log('Fetched metadata');
title(d.data.title);
artist(d.data.artist);
} else if (d.key === 'download') {
console.log(d.data);
download(d.data);
} else if (d.key === 'finishDownload') {
log('Download finished');
} else if (d.key === 'zipping') {
log('Zipping up files');
}
};
}

View File

@ -6,7 +6,10 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"quickrun": "tsc && node dist/index.js",
"build": "tsc"
"build": "tsc",
"febuild": "rollup -c",
"fedev": "rollup -c -w",
"start": "sirv app/public --no-clear"
},
"repository": {
"type": "git",
@ -14,29 +17,39 @@
},
"keywords": [
"deemix",
"piracy"
"piracy",
"svelte"
],
"author": "oatmealine",
"license": "AGPL-3.0",
"dependencies": {
"deemix": "git+https://git.freezerapp.xyz/RemixDev/deemix-js",
"deezer-js": "^1.2.4",
"deezer-js": "^1.3.5",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"express": "^4.17.3",
"express-ws": "^5.0.2",
"sirv-cli": "^2.0.2",
"timeago.js": "^4.0.2",
"toml": "^3.0.0",
"winston": "^3.3.3",
"ws": "^8.2.3"
"winston": "^3.6.0",
"ws": "^8.5.0"
},
"optionalDependencies": {
"bufferutil": "^4.0.5",
"utf-8-validate": "^5.0.7"
"bufferutil": "^4.0.6",
"utf-8-validate": "^5.0.8"
},
"devDependencies": {
"typescript": "^4.4.4",
"@rollup/plugin-commonjs": "^17.1.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@types/express": "^4.17.13",
"@types/express-ws": "^3.0.1",
"@types/ws": "^8.2.0"
"@types/ws": "^8.2.3",
"rollup": "^2.68.0",
"rollup-plugin-css-only": "^3.1.0",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-svelte": "^7.1.0",
"rollup-plugin-terser": "^7.0.2",
"svelte": "^3.46.4",
"typescript": "^4.5.5"
}
}

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

163
public-old/index.css Normal file
View File

@ -0,0 +1,163 @@
@media (prefers-color-scheme: dark) {
.lds-ring div {
border: 8px solid #fff;
border-color: #fff transparent transparent transparent;
}
#progress-bar {
background-color: #161627;
}
#progress-bar-inner {
background-color: rgb(131, 131, 243);
}
.slider {
background-color: rgb(131, 131, 243);
}
.slider:hover {
filter: drop-shadow( 0px 0px 5px #8383F3);
}
#progress-state {
background-color: #0a0a0f;
}
}
@media (prefers-color-scheme: light) {
#progress-bar {
background-color: #fafafa;
}
#progress-bar-inner {
background-color: #ea74ac;
}
.slider {
background-color: #ea74ac;
}
.slider:hover {
filter: drop-shadow( 0px 0px 5px #ea74ac);
}
#git {
filter: invert(100%);
}
#progress-state {
background-color: #fafafa;
}
}
#progress {
width: 600px;
max-width: 98%;
}
#progress-bar {
border-radius: 10px;
width: 100%;
height: 12px;
}
#progress-bar-inner {
border-radius: 10px;
}
#header {
display: flex;
align-items: left;
justify-content: left;
flex-direction: row;
}
#header > * {
margin-left: 12px;
margin-right: 12px;
}
#header .link {
font-size: x-large;
text-decoration: none;
}
#header img {
position: relative;
top: 5px;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
-webkit-transition: .2s;
transition: .2s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .2s;
transition: .2s;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
#header-left {
width: 100%;
}
#header-left > * {
margin-right: 16px;
}
#progress-state {
font-family: monospace;
font-size: 12px;
border-radius: 10px;
width: 80%;
padding: 6px;
height: 110px;
}
.album-downloading {
border-radius: 10px;
}
.error {
background-color: rgb(255, 155, 155, 0.3);
padding: 20px;
border-radius: 15px;
border: 3px solid rgb(255, 155, 155, 0.8);
text-align: center;
margin: 15px;
width: 400px;
display: none; /* this is changed by the js */
}
.error .big {
font-size: x-large;
}

View File

@ -1,450 +0,0 @@
@media (prefers-color-scheme: dark) {
* {
transition: 0.1s background-color ease-out;
}
body {
background-color: #0a0a0f;
color: #fff;
accent-color: rgb(131, 131, 243);
}
input {
background-color: #112;
color: #fff;
box-shadow: 0px 0px 15px #000;
border-bottom: 0rem solid rgb(131, 131, 243);
}
input:focus, input:hover {
border-bottom: 0.25rem solid rgb(131, 131, 243);
background-color: #161626;
}
.album {
background-color: #161627;
box-shadow: 0px 0px 12px #000;
border-left: 0rem solid rgb(131, 131, 243);
}
.album:hover {
border-left: 0.25rem solid rgb(131, 131, 243);
background-color: #181829;
}
.small {
color: #888;
}
.album-image {
border: 0px solid rgb(131, 131, 243);
box-shadow: 0px 0px 15px #000;
}
.album:hover .album-image {
border: 2px solid rgb(131, 131, 243);
box-shadow: 0px 0px 30px #000;
}
.album-image-wrapper {
border: 2px solid rgba(0, 0, 0, 0);
}
.album:hover .album-image-wrapper {
border: 0px solid rgba(0, 0, 0, 0);
}
.link {
color:rgb(131, 131, 243);
}
.link:hover {
color: rgb(151, 151, 255);
filter: drop-shadow( 0px 0px 2px #8383F3);
}
.album-download {
filter: invert(100%);
}
.album-download:hover {
filter: invert(50%) sepia(58%) saturate(893%) hue-rotate(206deg) brightness(99%) contrast(92%) drop-shadow( 0px 0px 5px #8383F3);
}
.lds-ring div {
border: 8px solid #fff;
border-color: #fff transparent transparent transparent;
}
#progress-bar {
background-color: #161627;
}
#progress-bar-inner {
background-color: rgb(131, 131, 243);
}
.album-bottom {
background-color: #112;
border-left: 0rem solid rgb(131, 131, 243);
}
.album:hover .album-bottom {
border-left: 0.25rem solid rgb(131, 131, 243);
}
.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);
}
.slider {
background-color: rgb(131, 131, 243);
}
.slider:hover {
filter: drop-shadow( 0px 0px 5px #8383F3);
}
#progress-state {
background-color: #0a0a0f;
}
}
@media (prefers-color-scheme: light) {
body {
background-color: #f0f0f0;
color: #1e1e2d;
accent-color: #ea74ac;
}
input {
background-color: #ffffff;
color: #1e1e2d;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
border-bottom: 0rem solid #ea74ac;
}
input:focus, input:hover {
border-bottom: 0.25rem solid #ea74ac;
background-color: #fafafa;
}
.album {
background-color: #ffffff;
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.2);
border-left: 0rem solid #ea74ac;
}
.album:hover {
border-left: 0.25rem solid #ea74ac;
background-color: #fafafa;
}
.small {
color: #c8c8d2;
}
.album-image {
border: 0px solid #ea74ac;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.2);
}
.album:hover .album-image {
border: 2px solid #ea74ac;
box-shadow: 0px 0px 30px rgba(0, 0, 0, 0.2);
}
.album-image-wrapper {
border: 2px solid rgba(0, 0, 0, 0);
}
.album:hover .album-image-wrapper {
border: 0px solid rgba(0, 0, 0, 0);
}
.link {
color:#ea74ac;
}
.link:hover {
color: #f484b6;
filter: drop-shadow( 0px 0px 2px #f484b6);
}
.album-download {
filter: none;
}
.album-download:hover {
filter: invert(65%) sepia(45%) saturate(772%) hue-rotate(295deg) brightness(103%) contrast(91%) drop-shadow( 0px 0px 5px #f484b6);
}
.lds-ring div {
border: 8px solid #1e1e2d;
border-color: #1e1e2d transparent transparent transparent;
}
#progress-bar {
background-color: #fafafa;
}
#progress-bar-inner {
background-color: #ea74ac;
}
.album-bottom {
background-color: #ffffff;
border-left: 0rem solid #ea74ac;
}
.album:hover .album-bottom {
border-left: 0.25rem solid #ea74ac;
}
.track {
border-bottom: 3px solid #f0f0f0;
border-left: 0rem solid #ea74ac;
}
.track:hover {
background-color: #fafafa;
border-left: 0.25rem solid #ea74ac;
}
.slider {
background-color: #ea74ac;
}
.slider:hover {
filter: drop-shadow( 0px 0px 5px #ea74ac);
}
#git {
filter: invert(100%);
}
#progress-state {
background-color: #fafafa;
}
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#main {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
input {
margin: 5px;
width: 550px;
max-width: 98%;
padding: 15px;
font-size: x-large;
border: none;
border-radius: 7px;
transition: 0.1s border-bottom ease-out, 0.1s background-color ease-in-out;
}
.album {
padding: 15px;
margin: 2px;
font-size: large;
border-radius: 10px 10px 0px 0px;
transition: 0.1s border-left ease-out, 0.1s background-color ease-in-out;
min-height: 96px;
display: flex;
justify-content: space-between;
}
.small {
font-size: medium;
}
.big {
font-weight: bold;
}
#albums {
margin-top: 20px;
width: 600px;
max-width: 98%;
}
#progress {
width: 600px;
max-width: 98%;
}
.album-image {
width: auto;
height: 100%;
border-radius: 10px;
transition: 0.1s border ease-out, 0.1s box-shadow ease-out;
width: 96px;
height: 96px;
}
.album-image-wrapper {
transition: 0.1s border ease-out;
}
.album-metadata {
display: flex;
flex-direction: column;
width: 100%;
}
.metadata {
height: 100%;
}
.link {
cursor: pointer;
transition: 0.1s color ease-out, 0.1s filter ease-out;
}
.album-download {
width: 32px;
height: 32px;
cursor: pointer;
transition: 0.1s filter ease-out;
}
.track .album-download {
position: relative;
top: 20px;
}
.lds-ring {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#progress-bar {
border-radius: 10px;
width: 100%;
height: 12px;
}
#progress-bar-inner {
border-radius: 10px;
}
.album-bottom {
padding: 0px;
margin-left: 2px;
margin-right: 2px;
border-radius: 0px 0px 10px 10px;
transition: 0.1s border-left ease-out;
}
.track {
padding: 10px;
padding-top: 6px;
padding-bottom: 6px;
margin: none;
display: flex;
justify-content: space-between;
font-size: large;
transition: 0.05s background-color ease-out, 0.1s border-left ease-out;
}
.track:nth-last-child(1) {
border-bottom: none;
border-radius: 0px 0px 15px 15px;
}
.track .album-download {
vertical-align: top;
}
.track-download-wrapper {
position: relative;
bottom: 20px;
height: 50%;
width: auto;
}
#header {
display: flex;
align-items: left;
justify-content: left;
flex-direction: row;
}
#header > * {
margin-left: 12px;
margin-right: 12px;
}
#header .link {
font-size: x-large;
text-decoration: none;
}
#header img {
position: relative;
top: 5px;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
-webkit-transition: .2s;
transition: .2s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .2s;
transition: .2s;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
#header-left {
width: 100%;
}
#header-left > * {
margin-right: 16px;
}
#progress-state {
font-family: monospace;
font-size: 12px;
border-radius: 10px;
width: 80%;
padding: 6px;
height: 110px;
}
.album-downloading {
border-radius: 10px;
}
.error {
background-color: rgb(255, 155, 155, 0.3);
padding: 20px;
border-radius: 15px;
border: 3px solid rgb(255, 155, 155, 0.8);
text-align: center;
margin: 15px;
width: 400px;
display: none; /* this is changed by the js */
}
.error .big {
font-size: x-large;
}

76
rollup.config.js Normal file
View File

@ -0,0 +1,76 @@
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
import css from 'rollup-plugin-css-only';
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
export default {
input: 'app/src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'app/public/build/bundle.js'
},
plugins: [
svelte({
compilerOptions: {
// enable run-time checks when not in production
dev: !production
}
}),
// we'll extract any component CSS out into
// a separate file - better for performance
css({ output: 'bundle.css' }),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('app/public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};

View File

@ -24,6 +24,7 @@ if (config.server.proxy) {
app.use((req, res, next) => {
logger.http(`${(config.server.proxy && req.headers['x-forwarded-for']) || req.connection.remoteAddress} ${req.method} ${req.originalUrl} `);
res.setHeader('Access-Control-Allow-Origin', '*');
next();
});