refactor frontend; switch to svelte
|
@ -1 +1,24 @@
|
||||||
/public/build/
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["svelte.svelte-vscode"]
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Svelte + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Svelte in Vite.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
|
||||||
|
|
||||||
|
## Need an official Svelte framework?
|
||||||
|
|
||||||
|
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
|
||||||
|
|
||||||
|
## Technical considerations
|
||||||
|
|
||||||
|
**Why use this over SvelteKit?**
|
||||||
|
|
||||||
|
- It brings its own routing solution which might not be preferable for some users.
|
||||||
|
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
|
||||||
|
`vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example.
|
||||||
|
|
||||||
|
This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
|
||||||
|
|
||||||
|
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
|
||||||
|
|
||||||
|
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
|
||||||
|
|
||||||
|
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
|
||||||
|
|
||||||
|
**Why include `.vscode/extensions.json`?**
|
||||||
|
|
||||||
|
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
|
||||||
|
|
||||||
|
**Why enable `checkJs` in the JS template?**
|
||||||
|
|
||||||
|
It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration.
|
||||||
|
|
||||||
|
**Why is HMR not preserving my local component state?**
|
||||||
|
|
||||||
|
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
|
||||||
|
|
||||||
|
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// store.js
|
||||||
|
// An extremely simple external store
|
||||||
|
import { writable } from 'svelte/store'
|
||||||
|
export default writable(0)
|
||||||
|
```
|
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="/ico/apple-icon-57x57.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="/ico/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/ico/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/ico/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/ico/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/ico/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/ico/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/ico/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/ico/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="/ico/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/ico/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="/ico/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/ico/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/ico/manifest.json">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
/**
|
||||||
|
* svelte-preprocess cannot figure out whether you have
|
||||||
|
* a value or a type, so tell TypeScript to enforce using
|
||||||
|
* `import type` instead of `import` for Types.
|
||||||
|
*/
|
||||||
|
"importsNotUsedAsValues": "error",
|
||||||
|
"isolatedModules": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
/**
|
||||||
|
* To have warnings / errors of the Svelte compiler at the
|
||||||
|
* correct position, enable source maps by default.
|
||||||
|
*/
|
||||||
|
"sourceMap": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
/**
|
||||||
|
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||||
|
* Disable this if you'd like to use dynamic types.
|
||||||
|
*/
|
||||||
|
"checkJs": true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Use global.d.ts instead of compilerOptions.types
|
||||||
|
* to avoid limiting type declarations.
|
||||||
|
*/
|
||||||
|
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "app",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||||
|
"svelte": "^3.49.0",
|
||||||
|
"vite": "^3.0.7"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,424 @@
|
||||||
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
|
specifiers:
|
||||||
|
'@sveltejs/vite-plugin-svelte': ^1.0.1
|
||||||
|
svelte: ^3.49.0
|
||||||
|
vite: ^3.0.7
|
||||||
|
|
||||||
|
devDependencies:
|
||||||
|
'@sveltejs/vite-plugin-svelte': 1.0.1_svelte@3.49.0+vite@3.0.8
|
||||||
|
svelte: 3.49.0
|
||||||
|
vite: 3.0.8
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
/@esbuild/linux-loong64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@rollup/pluginutils/4.2.1:
|
||||||
|
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
|
||||||
|
engines: {node: '>= 8.0.0'}
|
||||||
|
dependencies:
|
||||||
|
estree-walker: 2.0.2
|
||||||
|
picomatch: 2.3.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@sveltejs/vite-plugin-svelte/1.0.1_svelte@3.49.0+vite@3.0.8:
|
||||||
|
resolution: {integrity: sha512-PorCgUounn0VXcpeJu+hOweZODKmGuLHsLomwqSj+p26IwjjGffmYQfVHtiTWq+NqaUuuHWWG7vPge6UFw4Aeg==}
|
||||||
|
engines: {node: ^14.18.0 || >= 16}
|
||||||
|
peerDependencies:
|
||||||
|
diff-match-patch: ^1.0.5
|
||||||
|
svelte: ^3.44.0
|
||||||
|
vite: ^3.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
diff-match-patch:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@rollup/pluginutils': 4.2.1
|
||||||
|
debug: 4.3.4
|
||||||
|
deepmerge: 4.2.2
|
||||||
|
kleur: 4.1.5
|
||||||
|
magic-string: 0.26.2
|
||||||
|
svelte: 3.49.0
|
||||||
|
svelte-hmr: 0.14.12_svelte@3.49.0
|
||||||
|
vite: 3.0.8
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/debug/4.3.4:
|
||||||
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
ms: 2.1.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/deepmerge/4.2.2:
|
||||||
|
resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/esbuild-android-64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-android-arm64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-darwin-64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-darwin-arm64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-freebsd-64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-freebsd-arm64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-linux-32/0.14.54:
|
||||||
|
resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-linux-64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-linux-arm/0.14.54:
|
||||||
|
resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-linux-arm64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-linux-mips64le/0.14.54:
|
||||||
|
resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-linux-ppc64le/0.14.54:
|
||||||
|
resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-linux-riscv64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-linux-s390x/0.14.54:
|
||||||
|
resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-netbsd-64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-openbsd-64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-sunos-64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-windows-32/0.14.54:
|
||||||
|
resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-windows-64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild-windows-arm64/0.14.54:
|
||||||
|
resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/esbuild/0.14.54:
|
||||||
|
resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/linux-loong64': 0.14.54
|
||||||
|
esbuild-android-64: 0.14.54
|
||||||
|
esbuild-android-arm64: 0.14.54
|
||||||
|
esbuild-darwin-64: 0.14.54
|
||||||
|
esbuild-darwin-arm64: 0.14.54
|
||||||
|
esbuild-freebsd-64: 0.14.54
|
||||||
|
esbuild-freebsd-arm64: 0.14.54
|
||||||
|
esbuild-linux-32: 0.14.54
|
||||||
|
esbuild-linux-64: 0.14.54
|
||||||
|
esbuild-linux-arm: 0.14.54
|
||||||
|
esbuild-linux-arm64: 0.14.54
|
||||||
|
esbuild-linux-mips64le: 0.14.54
|
||||||
|
esbuild-linux-ppc64le: 0.14.54
|
||||||
|
esbuild-linux-riscv64: 0.14.54
|
||||||
|
esbuild-linux-s390x: 0.14.54
|
||||||
|
esbuild-netbsd-64: 0.14.54
|
||||||
|
esbuild-openbsd-64: 0.14.54
|
||||||
|
esbuild-sunos-64: 0.14.54
|
||||||
|
esbuild-windows-32: 0.14.54
|
||||||
|
esbuild-windows-64: 0.14.54
|
||||||
|
esbuild-windows-arm64: 0.14.54
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/estree-walker/2.0.2:
|
||||||
|
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/fsevents/2.3.2:
|
||||||
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: true
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/function-bind/1.1.1:
|
||||||
|
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/has/1.0.3:
|
||||||
|
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
||||||
|
engines: {node: '>= 0.4.0'}
|
||||||
|
dependencies:
|
||||||
|
function-bind: 1.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/is-core-module/2.10.0:
|
||||||
|
resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==}
|
||||||
|
dependencies:
|
||||||
|
has: 1.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/kleur/4.1.5:
|
||||||
|
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/magic-string/0.26.2:
|
||||||
|
resolution: {integrity: sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
sourcemap-codec: 1.4.8
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/ms/2.1.2:
|
||||||
|
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/nanoid/3.3.4:
|
||||||
|
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
|
||||||
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
|
hasBin: true
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/path-parse/1.0.7:
|
||||||
|
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/picocolors/1.0.0:
|
||||||
|
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/picomatch/2.3.1:
|
||||||
|
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||||
|
engines: {node: '>=8.6'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/postcss/8.4.16:
|
||||||
|
resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==}
|
||||||
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
dependencies:
|
||||||
|
nanoid: 3.3.4
|
||||||
|
picocolors: 1.0.0
|
||||||
|
source-map-js: 1.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/resolve/1.22.1:
|
||||||
|
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
is-core-module: 2.10.0
|
||||||
|
path-parse: 1.0.7
|
||||||
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/rollup/2.77.3:
|
||||||
|
resolution: {integrity: sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==}
|
||||||
|
engines: {node: '>=10.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/source-map-js/1.0.2:
|
||||||
|
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/sourcemap-codec/1.4.8:
|
||||||
|
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/supports-preserve-symlinks-flag/1.0.0:
|
||||||
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/svelte-hmr/0.14.12_svelte@3.49.0:
|
||||||
|
resolution: {integrity: sha512-4QSW/VvXuqVcFZ+RhxiR8/newmwOCTlbYIezvkeN6302YFRE8cXy0naamHcjz8Y9Ce3ITTZtrHrIL0AGfyo61w==}
|
||||||
|
engines: {node: ^12.20 || ^14.13.1 || >= 16}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: '>=3.19.0'
|
||||||
|
dependencies:
|
||||||
|
svelte: 3.49.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/svelte/3.49.0:
|
||||||
|
resolution: {integrity: sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/vite/3.0.8:
|
||||||
|
resolution: {integrity: sha512-AOZ4eN7mrkJiOLuw8IA7piS4IdOQyQCA81GxGsAQvAZzMRi9ZwGB3TOaYsj4uLAWK46T5L4AfQ6InNGlxX30IQ==}
|
||||||
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
less: '*'
|
||||||
|
sass: '*'
|
||||||
|
stylus: '*'
|
||||||
|
terser: ^5.4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
less:
|
||||||
|
optional: true
|
||||||
|
sass:
|
||||||
|
optional: true
|
||||||
|
stylus:
|
||||||
|
optional: true
|
||||||
|
terser:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.14.54
|
||||||
|
postcss: 8.4.16
|
||||||
|
resolve: 1.22.1
|
||||||
|
rollup: 2.77.3
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
dev: true
|
|
@ -1,49 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
version="1.1"
|
|
||||||
viewBox="0 0 512 512"
|
|
||||||
enable-background="new 0 0 512 512"
|
|
||||||
id="svg10"
|
|
||||||
sodipodi:docname="download.svg"
|
|
||||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs14" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview12"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="0.56761894"
|
|
||||||
inkscape:cx="367.3239"
|
|
||||||
inkscape:cy="27.307052"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1025"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="25"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg10" />
|
|
||||||
<g
|
|
||||||
id="g8"
|
|
||||||
style="fill:#000000">
|
|
||||||
<g
|
|
||||||
id="g6"
|
|
||||||
style="fill:#000000">
|
|
||||||
<path
|
|
||||||
d="M480.6,341.4c-11.3,0-20.4,9.1-20.4,20.4v98.4H51.8v-98.4c0-11.3-9.1-20.4-20.4-20.4c-11.3,0-20.4,9.1-20.4,20.4v118.8 c0,11.3,9.1,20.4,20.4,20.4h449.2c11.3,0,20.4-9.1,20.4-20.4V361.8C501,350.5,491.9,341.4,480.6,341.4z"
|
|
||||||
id="path2"
|
|
||||||
style="fill:#000000" />
|
|
||||||
<path
|
|
||||||
d="m241,365.6c11.5,11.6 25.6,5.2 29.9,0l117.3-126.2c7.7-8.3 7.2-21.2-1.1-28.9-8.3-7.7-21.2-7.2-28.8,1.1l-81.9,88.1v-265.2c0-11.3-9.1-20.4-20.4-20.4-11.3,0-20.4,9.1-20.4,20.4v265.3l-81.9-88.1c-7.7-8.3-20.6-8.7-28.9-1.1-8.3,7.7-8.7,20.6-1.1,28.9l117.3,126.1z"
|
|
||||||
id="path4"
|
|
||||||
style="fill:#000000" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.1 KiB |
|
@ -1,124 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
@ -1,18 +0,0 @@
|
||||||
<!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>
|
|
|
@ -1,2 +0,0 @@
|
||||||
User-agent: *
|
|
||||||
Disallow: /
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -1,32 +1,123 @@
|
||||||
<script>
|
<script>
|
||||||
import Search from './Search.svelte';
|
import Album from './lib/Album.svelte';
|
||||||
import Album from './Album.svelte';
|
import Header from './lib/Header.svelte';
|
||||||
import Download from './Download.svelte';
|
import Loading from './lib/Loading.svelte';
|
||||||
|
import Search from './lib/Search.svelte';
|
||||||
|
import ThemeSwitcher from './lib/ThemeSwitcher.svelte';
|
||||||
|
import { queue } from './lib/stores';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
import ProgressBar from './lib/ProgressBar.svelte';
|
||||||
|
|
||||||
import {albums, display, displays, downloading} from './stores';
|
let loading = false;
|
||||||
|
|
||||||
|
async function search(event) {
|
||||||
|
const query = event.target.value;
|
||||||
|
|
||||||
|
searchAlbums = [];
|
||||||
|
loading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let url = new URL('/api/search', window.location.origin);
|
||||||
|
url.searchParams.set('search', query);
|
||||||
|
const response = await fetch(url);
|
||||||
|
const data = await response.json();
|
||||||
|
loading = false;
|
||||||
|
searchAlbums = data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchAlbums = [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<app>
|
||||||
content {
|
<main>
|
||||||
display: flex;
|
<span class="main">
|
||||||
justify-content: center;
|
<Header/>
|
||||||
align-items: center;
|
<Search onChange={search}/>
|
||||||
flex-direction: column;
|
<i class="small">ps. sorry for shitty mobile support on rewrite i'll fix it soon i promiseeeee -oat</i>
|
||||||
}
|
{#if loading}
|
||||||
</style>
|
<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}
|
||||||
|
</span>
|
||||||
|
</main>
|
||||||
|
<sidebar class:open={$queue.length > 0}>
|
||||||
|
<h1>Download Queue</h1>
|
||||||
|
<div class="queue">
|
||||||
|
{#each $queue as dl}
|
||||||
|
<div>
|
||||||
|
<Album log={dl.log} short={true} hideDownload={true} butShowThisDownloadLinkInstead={dl.downloadLink} title={dl.title} artist={dl.artist} cover={dl.cover} id={dl.id} subtitle={!dl.isAlbum && `from ${dl.album}`} />
|
||||||
|
<ProgressBar progress={dl.progress} success={dl.success}/>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</sidebar>
|
||||||
|
</app>
|
||||||
|
|
||||||
<main>
|
<style>
|
||||||
<content>
|
app {
|
||||||
{#if $display === displays.AlbumSearch}
|
display: flex;
|
||||||
<Search/>
|
flex-direction: row;
|
||||||
<div id="albums">
|
}
|
||||||
{#each $albums as album}
|
|
||||||
<Album id={album.id} title={album.title} cover={album.cover} artist={album.artist.name}/><br>
|
.main {
|
||||||
{/each}
|
display: flex;
|
||||||
</div>
|
align-items: center;
|
||||||
{/if}
|
flex-direction: column;
|
||||||
{#if $display === displays.Download && $downloading}
|
}
|
||||||
<Download id={$downloading.id} isAlbum={$downloading.isAlbum}/>
|
main {
|
||||||
{/if}
|
flex: 1 1 0px;
|
||||||
</content>
|
}
|
||||||
</main>
|
|
||||||
|
sidebar {
|
||||||
|
width: 0px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 0em;
|
||||||
|
height: calc(100vh - 2em);
|
||||||
|
overflow: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
transition: width 0.2s ease-in-out, padding 0.2s ease-in-out, right 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
sidebar.open {
|
||||||
|
width: 450px;
|
||||||
|
padding: 1em;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
sidebar {
|
||||||
|
background-color: #161627;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
sidebar {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.albums {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 600px;
|
||||||
|
max-width: 98%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
<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}
|
|
|
@ -1,51 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,55 +0,0 @@
|
||||||
<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}>
|
|
|
@ -1,98 +0,0 @@
|
||||||
<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>
|
|
|
@ -1,9 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
* {
|
||||||
|
transition: 0.1s background-color ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
background-color: #0a0a0f;
|
||||||
|
color: #fff;
|
||||||
|
accent-color: rgb(131, 131, 243);
|
||||||
|
}
|
||||||
|
.small {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
.link {
|
||||||
|
color:rgb(131, 131, 243);
|
||||||
|
}
|
||||||
|
.link:hover {
|
||||||
|
color: rgb(151, 151, 255);
|
||||||
|
filter: drop-shadow( 0px 0px 2px #8383F3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #1e1e2d;
|
||||||
|
accent-color: #ea74ac;
|
||||||
|
}
|
||||||
|
.small {
|
||||||
|
color: #c8c8d2;
|
||||||
|
}
|
||||||
|
.link {
|
||||||
|
color:#ea74ac;
|
||||||
|
}
|
||||||
|
.link:hover {
|
||||||
|
color: #f484b6;
|
||||||
|
filter: drop-shadow( 0px 0px 2px #f484b6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
font-size: medium;
|
||||||
|
}
|
||||||
|
.big {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.1s color ease-out;
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
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});
|
|
||||||
}
|
|
|
@ -1,3 +1,94 @@
|
||||||
|
<script>
|
||||||
|
export let id;
|
||||||
|
export let title;
|
||||||
|
export let artist;
|
||||||
|
export let subtitle;
|
||||||
|
export let cover;
|
||||||
|
|
||||||
|
export let short;
|
||||||
|
export let hideDownload;
|
||||||
|
export let log;
|
||||||
|
export let butShowThisDownloadLinkInstead;
|
||||||
|
|
||||||
|
import { Icon } from 'svelte-fontawesome';
|
||||||
|
import { faDownload } from '@fortawesome/free-solid-svg-icons/faDownload';
|
||||||
|
|
||||||
|
import { inview } from 'svelte-inview';
|
||||||
|
import Loading from './Loading.svelte';
|
||||||
|
import Track from './Track.svelte';
|
||||||
|
|
||||||
|
import { startDownload } from './download';
|
||||||
|
|
||||||
|
const options = {};
|
||||||
|
|
||||||
|
let album;
|
||||||
|
let loadingTracks = false;
|
||||||
|
async function loadTracks() {
|
||||||
|
if (loadingTracks || album || short) return;
|
||||||
|
loadingTracks = true;
|
||||||
|
try {
|
||||||
|
let url = new URL(`/api/album`, window.location.origin);
|
||||||
|
url.searchParams.set('id', id);
|
||||||
|
const response = await fetch(url);
|
||||||
|
album = await response.json();
|
||||||
|
loadingTracks = false;
|
||||||
|
} catch(err) {
|
||||||
|
loadingTracks = false;
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="album" class:short={short} id="album-{id}"
|
||||||
|
use:inview={options}
|
||||||
|
on:enter={loadTracks}
|
||||||
|
>
|
||||||
|
<div class="album-metadata">
|
||||||
|
<span class="metadata">
|
||||||
|
<span class="big">{title}</span>
|
||||||
|
{#if subtitle}
|
||||||
|
<span class="small">{subtitle}</span>
|
||||||
|
{/if}
|
||||||
|
<br>
|
||||||
|
<span class="small">{artist.name}</span>
|
||||||
|
</span>
|
||||||
|
{#if log}
|
||||||
|
<div class="progress-state">
|
||||||
|
{#each $log as line, i}
|
||||||
|
<span style="order: {-i}">{line}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#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>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</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}'">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="album-bottom" id="album-bottom-{id}">
|
||||||
|
{#if loadingTracks}
|
||||||
|
<Loading/>
|
||||||
|
{/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}/>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.album {
|
.album {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
@ -8,8 +99,11 @@
|
||||||
min-height: 96px;
|
min-height: 96px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
.album.short {
|
||||||
|
border-radius: 10px 10px 10px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-image {
|
.album-image {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -24,17 +118,16 @@
|
||||||
.album-metadata {
|
.album-metadata {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
flex: 1 1 0px;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
.metadata {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-download {
|
.album-download {
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.1s filter ease-out;
|
transition: 0.1s filter ease-out, 0.1s color ease-out;
|
||||||
|
}
|
||||||
|
.album-download {
|
||||||
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-bottom {
|
.album-bottom {
|
||||||
|
@ -43,13 +136,36 @@
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
border-radius: 0px 0px 10px 10px;
|
border-radius: 0px 0px 10px 10px;
|
||||||
transition: 0.1s border-left ease-out;
|
transition: 0.1s border-left ease-out;
|
||||||
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-download {
|
.metadata {
|
||||||
vertical-align: top;
|
flex: 1 1 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-state {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 80%;
|
||||||
|
padding: 6px;
|
||||||
|
height: 5.5em;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.album-download {
|
||||||
|
color: #fff;
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
.album-download:hover {
|
||||||
|
color: rgb(131, 131, 243);
|
||||||
|
filter: drop-shadow( 0px 0px 6px #8383F3);
|
||||||
|
}
|
||||||
.album {
|
.album {
|
||||||
background-color: #161627;
|
background-color: #161627;
|
||||||
box-shadow: 0px 0px 12px #000;
|
box-shadow: 0px 0px 12px #000;
|
||||||
|
@ -73,18 +189,24 @@
|
||||||
.album:hover .album-image-wrapper {
|
.album:hover .album-image-wrapper {
|
||||||
border: 0px solid rgba(0, 0, 0, 0);
|
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 {
|
.album-bottom {
|
||||||
background-color: #112;
|
background-color: #112;
|
||||||
border-left: 0rem solid rgb(131, 131, 243);
|
border-left: 0rem solid rgb(131, 131, 243);
|
||||||
}
|
}
|
||||||
|
.progress-state {
|
||||||
|
background-color: #0a0a0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
|
.album-download {
|
||||||
|
color: #1e1e2d;
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
.album-download:hover {
|
||||||
|
color: #ea74ac;
|
||||||
|
filter: drop-shadow( 0px 0px 6px #f484b6);
|
||||||
|
}
|
||||||
.album {
|
.album {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.2);
|
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.2);
|
||||||
|
@ -108,59 +230,12 @@
|
||||||
.album:hover .album-image-wrapper {
|
.album:hover .album-image-wrapper {
|
||||||
border: 0px solid rgba(0, 0, 0, 0);
|
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 {
|
.album-bottom {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-left: 0rem solid #ea74ac;
|
border-left: 0rem solid #ea74ac;
|
||||||
}
|
}
|
||||||
|
.progress-state {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
<div class="header">
|
||||||
|
<div class="header-left">
|
||||||
|
<a href="https://deemix.app" target="_blank" class="big link"><img class="deemix" width="26" height="auto" src="https://deemix.app/img/logo.png" alt=""> deemix</a>
|
||||||
|
<a href="https://git.oat.zone/oat/deemix-web-frontend" target="_blank" class="big link"><img class="git" width="26" height="auto" src="https://git-scm.com/images/logos/downloads/Git-Icon-White.png" alt=""> git</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="header-right">
|
||||||
|
<!--<ThemeSwitcher/>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header > * {
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
.header .link {
|
||||||
|
font-size: x-large;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.header img {
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
flex: 1 1 0px;
|
||||||
|
}
|
||||||
|
.header-left > * {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.git {
|
||||||
|
filter: invert(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,50 @@
|
||||||
|
<div class="lds-ring"><div></div><div></div><div></div><div></div></div>
|
||||||
|
|
||||||
|
<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>
|
|
@ -0,0 +1,41 @@
|
||||||
|
<script>
|
||||||
|
export let progress;
|
||||||
|
export let success;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="progress-bar"><div class="progress-bar-inner" style="height:100%;width:{$success === false ? 100 : $progress}%" class:done={$success === true} class:error={$success === false}></div></div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.progress-bar {
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 100%;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
.progress-bar-inner {
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: 0.2s background-color ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar-inner.done {
|
||||||
|
background-color: rgb(131, 243, 131);
|
||||||
|
}
|
||||||
|
.progress-bar-inner.error {
|
||||||
|
background-color: rgb(243, 131, 131);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.progress-bar {
|
||||||
|
background-color: #0a0a0f;
|
||||||
|
}
|
||||||
|
.progress-bar-inner {
|
||||||
|
background-color: rgb(131, 131, 243);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.progress-bar {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
.progress-bar-inner {
|
||||||
|
background-color: #ea74ac;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,89 @@
|
||||||
|
<script>
|
||||||
|
export let onChange;
|
||||||
|
|
||||||
|
// list of based music
|
||||||
|
const placeholders = [
|
||||||
|
'xilent - we are dust',
|
||||||
|
'joyryde - brave',
|
||||||
|
'phaseone - the risen ep',
|
||||||
|
// 'blanke & godlands - hellraiser', // deemix search sucks and this doesnt actually work
|
||||||
|
'camellia - blackmagik blazing',
|
||||||
|
't+pazolite - without permission',
|
||||||
|
'the brig - vindicate',
|
||||||
|
'bossfight - next wave',
|
||||||
|
'matt doe - g.a.s.',
|
||||||
|
'virtual riot - save yourself ep',
|
||||||
|
'panda eyes - hold on',
|
||||||
|
'clockvice - disgrace',
|
||||||
|
'scheme - quake',
|
||||||
|
'eliminate - belly of the beast ep',
|
||||||
|
'apriskah - mistakes ep',
|
||||||
|
'dog blood - turn off the lights',
|
||||||
|
'silentroom - memory waves',
|
||||||
|
'metaroom - metadata',
|
||||||
|
'prismo - nightmare',
|
||||||
|
'ray volpe - rise of the volpetron',
|
||||||
|
'eliminate - mula',
|
||||||
|
'vorso - full tilt',
|
||||||
|
'billiummoto - link(init)',
|
||||||
|
'voltra - radar dx',
|
||||||
|
'zomboy - end game',
|
||||||
|
'dr. ozi - host ep',
|
||||||
|
'nitepunk - absolute zero',
|
||||||
|
'2 mello - sounds of tokyo-to future',
|
||||||
|
'must die - crisis vision redux',
|
||||||
|
'moe shop - notice',
|
||||||
|
'cysmix - escapism',
|
||||||
|
'rogue - rattlesnake',
|
||||||
|
'all nighter, vol. 7',
|
||||||
|
'six impala - rubber',
|
||||||
|
'tryple - inner experience',
|
||||||
|
'ekcle - deshoda ep',
|
||||||
|
'leroy - dariacore',
|
||||||
|
'voljum - dayscapes'
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input type="search" class="album-search" name="q" on:change={onChange} placeholder={placeholders[Math.floor(Math.random() * placeholders.length)]}>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input {
|
||||||
|
margin: 5px;
|
||||||
|
width: 550px;
|
||||||
|
max-width: 98%;
|
||||||
|
padding: 15px;
|
||||||
|
font-size: x-large;
|
||||||
|
border: none;
|
||||||
|
border-radius: 7px;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
transition: 0.1s border-bottom ease-out, 0.1s background-color ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus, input:hover {
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-bottom-width: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@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 {
|
||||||
|
background-color: #161626;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,94 @@
|
||||||
|
<script>
|
||||||
|
let override;
|
||||||
|
|
||||||
|
function getTheme() {
|
||||||
|
return override || (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is literally a find replace in the css
|
||||||
|
function setTheme(theme) {
|
||||||
|
override = theme;
|
||||||
|
Array.from(document.styleSheets).map(e => {
|
||||||
|
try {
|
||||||
|
return Array.from(e.cssRules)
|
||||||
|
} catch (e) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}).flat().map(e => {
|
||||||
|
if (e.constructor != CSSMediaRule) return;
|
||||||
|
if (e.originalConditionText) e.conditionText = e.originalConditionText;
|
||||||
|
else e.originalConditionText = e.conditionText
|
||||||
|
if (theme === 'system') return
|
||||||
|
let match = e.conditionText.match(/prefers-color-scheme:\s*(light|dark)/i)
|
||||||
|
if (!match) return;
|
||||||
|
e.conditionText = e.conditionText.replace(match[0], (match[1].toLowerCase() == theme ? 'min' : 'max') + '-width: 0')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function swapTheme() {
|
||||||
|
let theme = getTheme();
|
||||||
|
console.log(`current theme ${theme}`)
|
||||||
|
setTheme(theme === 'light' ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" class="theme-switch" on:click={swapTheme}>
|
||||||
|
<span class="slider round"></span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 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%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,99 @@
|
||||||
|
<script>
|
||||||
|
export let id;
|
||||||
|
export let artist;
|
||||||
|
export let title;
|
||||||
|
export let cover;
|
||||||
|
export let duration;
|
||||||
|
export let album;
|
||||||
|
|
||||||
|
import { formatTime } from './format';
|
||||||
|
|
||||||
|
import { Icon } from 'svelte-fontawesome';
|
||||||
|
import { faDownload } from '@fortawesome/free-solid-svg-icons/faDownload';
|
||||||
|
|
||||||
|
import { startDownload } from './download';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="track" id="track-{id}">
|
||||||
|
<span class="track-left">{artist} - {title}</span>
|
||||||
|
<span class="track-right">
|
||||||
|
<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}/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.track {
|
||||||
|
padding: 10px;
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
margin: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
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-left {
|
||||||
|
flex: 1 1 0px;
|
||||||
|
}
|
||||||
|
.track-right {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-download {
|
||||||
|
vertical-align: top;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.1s filter ease-out, 0.1s color ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.track-download {
|
||||||
|
color: #fff;
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
.track-download:hover {
|
||||||
|
color: rgb(131, 131, 243);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.track-download {
|
||||||
|
color: #1e1e2d;
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
.track-download:hover {
|
||||||
|
color: #ea74ac;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { get, writable } from "svelte/store";
|
||||||
|
import { queue } from "./stores";
|
||||||
|
|
||||||
|
function getWebsocketLocation() {
|
||||||
|
return window.window.location.toString().replace('https://', 'wss://').replace('http://', 'ws://');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startDownload(id, metadata, isAlbum) {
|
||||||
|
let log = writable(['Connecting to WebSocket...']);
|
||||||
|
let logLocal = ['Connecting to WebSocket...'];
|
||||||
|
let progress = writable(0);
|
||||||
|
let success = writable(null);
|
||||||
|
let downloadLink = writable(null);
|
||||||
|
|
||||||
|
let coverArt;
|
||||||
|
let title;
|
||||||
|
let artist;
|
||||||
|
|
||||||
|
let queueItem = {
|
||||||
|
id: id,
|
||||||
|
...metadata,
|
||||||
|
log,
|
||||||
|
progress,
|
||||||
|
isAlbum,
|
||||||
|
success,
|
||||||
|
downloadLink
|
||||||
|
};
|
||||||
|
|
||||||
|
queue.set([...get(queue), queueItem]);
|
||||||
|
|
||||||
|
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') {
|
||||||
|
logLocal.push(`[${d.data.data.title}] ${d.data.state}`);
|
||||||
|
log.set(logLocal);
|
||||||
|
} else if (d.key === 'updateQueue') {
|
||||||
|
if (d.data.progress) {
|
||||||
|
progress.set(d.data.progress);
|
||||||
|
}
|
||||||
|
} else if (d.key === 'download') {
|
||||||
|
downloadLink.set(d.data);
|
||||||
|
} else if (d.key === 'finishDownload') {
|
||||||
|
logLocal.push('Download finished');
|
||||||
|
log.set(logLocal);
|
||||||
|
success.set(true);
|
||||||
|
} else if (d.key === 'zipping') {
|
||||||
|
logLocal.push('Creating zip archive');
|
||||||
|
log.set(logLocal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ws.onerror = (e) => {
|
||||||
|
console.error(e);
|
||||||
|
logLocal.push(`${e}`);
|
||||||
|
log.set(logLocal);
|
||||||
|
success.set(false);
|
||||||
|
}
|
||||||
|
ws.onclose = (e) => {
|
||||||
|
if (e.code !== 1000) {
|
||||||
|
console.error(`websocket closed unexpectedly with code ${e.code}\n${e.reason}`);
|
||||||
|
logLocal.push(`websocket closed unexpectedly with code ${e.code}`, `${e.reason}`);
|
||||||
|
log.set(logLocal);
|
||||||
|
success.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function formatTime(s) {
|
||||||
|
return Math.floor(s / 60).toString().padStart(2, '0') + ':' + (s % 60).toString().padStart(2, '0');
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { writable } from "svelte/store";
|
||||||
|
|
||||||
|
export let queue = writable([]);
|
|
@ -1,9 +1,8 @@
|
||||||
import App from './App.svelte';
|
import './app.css'
|
||||||
|
import App from './App.svelte'
|
||||||
|
|
||||||
const app = new App({
|
const app = new App({
|
||||||
target: document.body,
|
target: document.body
|
||||||
props: {
|
})
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app;
|
export default app
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
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);
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
/// <reference types="svelte" />
|
||||||
|
/// <reference types="vite/client" />
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [svelte()]
|
||||||
|
})
|
|
@ -23,12 +23,15 @@
|
||||||
"author": "oatmealine",
|
"author": "oatmealine",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"deemix": "git+https://git.freezerapp.xyz/RemixDev/deemix-js",
|
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
||||||
|
"deemix": "git+https://gitlab.com/RemixDev/deemix-js",
|
||||||
"deezer-js": "^1.3.5",
|
"deezer-js": "^1.3.5",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-ws": "^5.0.2",
|
"express-ws": "^5.0.2",
|
||||||
"sirv-cli": "^2.0.2",
|
"sirv-cli": "^2.0.2",
|
||||||
|
"svelte-fontawesome": "^0.0.3",
|
||||||
|
"svelte-inview": "^3.0.1",
|
||||||
"timeago.js": "^4.0.2",
|
"timeago.js": "^4.0.2",
|
||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"winston": "^3.6.0",
|
"winston": "^3.6.0",
|
||||||
|
|
119
pnpm-lock.yaml
|
@ -1,13 +1,14 @@
|
||||||
lockfileVersion: 5.3
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
|
'@fortawesome/free-solid-svg-icons': ^6.1.2
|
||||||
'@rollup/plugin-commonjs': ^17.1.0
|
'@rollup/plugin-commonjs': ^17.1.0
|
||||||
'@rollup/plugin-node-resolve': ^11.2.1
|
'@rollup/plugin-node-resolve': ^11.2.1
|
||||||
'@types/express': ^4.17.13
|
'@types/express': ^4.17.13
|
||||||
'@types/express-ws': ^3.0.1
|
'@types/express-ws': ^3.0.1
|
||||||
'@types/ws': ^8.2.3
|
'@types/ws': ^8.2.3
|
||||||
bufferutil: ^4.0.6
|
bufferutil: ^4.0.6
|
||||||
deemix: git+https://git.freezerapp.xyz/RemixDev/deemix-js
|
deemix: git+https://gitlab.com/RemixDev/deemix-js
|
||||||
deezer-js: ^1.3.5
|
deezer-js: ^1.3.5
|
||||||
dotenv: ^10.0.0
|
dotenv: ^10.0.0
|
||||||
express: ^4.17.3
|
express: ^4.17.3
|
||||||
|
@ -19,6 +20,8 @@ specifiers:
|
||||||
rollup-plugin-terser: ^7.0.2
|
rollup-plugin-terser: ^7.0.2
|
||||||
sirv-cli: ^2.0.2
|
sirv-cli: ^2.0.2
|
||||||
svelte: ^3.46.4
|
svelte: ^3.46.4
|
||||||
|
svelte-fontawesome: ^0.0.3
|
||||||
|
svelte-inview: ^3.0.1
|
||||||
timeago.js: ^4.0.2
|
timeago.js: ^4.0.2
|
||||||
toml: ^3.0.0
|
toml: ^3.0.0
|
||||||
typescript: ^4.5.5
|
typescript: ^4.5.5
|
||||||
|
@ -27,16 +30,19 @@ specifiers:
|
||||||
ws: ^8.5.0
|
ws: ^8.5.0
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
deemix: git.freezerapp.xyz/RemixDev/deemix-js/58bd92bc303a338aafc56ccf51940713812756ec
|
'@fortawesome/free-solid-svg-icons': 6.1.2
|
||||||
|
deemix: gitlab.com/RemixDev/deemix-js/a105b03beb93cc2efa5d723d7f2e3cb2237d4f08
|
||||||
deezer-js: 1.3.5
|
deezer-js: 1.3.5
|
||||||
dotenv: 10.0.0
|
dotenv: 10.0.0
|
||||||
express: 4.17.3
|
express: 4.17.3
|
||||||
express-ws: 5.0.2_9938c0c959a5f2b516218250f750cbca
|
express-ws: 5.0.2_te4mbskzuxzlkfrbqjipouglzi
|
||||||
sirv-cli: 2.0.2
|
sirv-cli: 2.0.2
|
||||||
|
svelte-fontawesome: 0.0.3
|
||||||
|
svelte-inview: 3.0.1_svelte@3.46.4
|
||||||
timeago.js: 4.0.2
|
timeago.js: 4.0.2
|
||||||
toml: 3.0.0
|
toml: 3.0.0
|
||||||
winston: 3.6.0
|
winston: 3.6.0
|
||||||
ws: 8.5.0_bffff4271b89d628e8333ead80d3d8e8
|
ws: 8.5.0_x777ijy3rhlcr2bth2wybu6y5a
|
||||||
|
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
bufferutil: 4.0.6
|
bufferutil: 4.0.6
|
||||||
|
@ -50,8 +56,8 @@ devDependencies:
|
||||||
'@types/ws': 8.2.3
|
'@types/ws': 8.2.3
|
||||||
rollup: 2.68.0
|
rollup: 2.68.0
|
||||||
rollup-plugin-css-only: 3.1.0_rollup@2.68.0
|
rollup-plugin-css-only: 3.1.0_rollup@2.68.0
|
||||||
rollup-plugin-livereload: 2.0.5_bffff4271b89d628e8333ead80d3d8e8
|
rollup-plugin-livereload: 2.0.5_x777ijy3rhlcr2bth2wybu6y5a
|
||||||
rollup-plugin-svelte: 7.1.0_rollup@2.68.0+svelte@3.46.4
|
rollup-plugin-svelte: 7.1.0_dni462kqdqwcnfey6ikyaliv44
|
||||||
rollup-plugin-terser: 7.0.2_rollup@2.68.0
|
rollup-plugin-terser: 7.0.2_rollup@2.68.0
|
||||||
svelte: 3.46.4
|
svelte: 3.46.4
|
||||||
typescript: 4.5.5
|
typescript: 4.5.5
|
||||||
|
@ -92,6 +98,20 @@ packages:
|
||||||
kuler: 2.0.0
|
kuler: 2.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@fortawesome/fontawesome-common-types/6.1.2:
|
||||||
|
resolution: {integrity: sha512-wBaAPGz1Awxg05e0PBRkDRuTsy4B3dpBm+zreTTyd9TH4uUM27cAL4xWyWR0rLJCrRwzVsQ4hF3FvM6rqydKPA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@fortawesome/free-solid-svg-icons/6.1.2:
|
||||||
|
resolution: {integrity: sha512-lTgZz+cMpzjkHmCwOG3E1ilUZrnINYdqMmrkv30EC3XbRsGlbIOL8H9LaNp5SV4g0pNJDfQ4EdTWWaMvdwyLiQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
requiresBuild: true
|
||||||
|
dependencies:
|
||||||
|
'@fortawesome/fontawesome-common-types': 6.1.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@polka/url/1.0.0-next.21:
|
/@polka/url/1.0.0-next.21:
|
||||||
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -302,7 +322,7 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/asynckit/0.4.0:
|
/asynckit/0.4.0:
|
||||||
resolution: {integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k=}
|
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/balanced-match/1.0.2:
|
/balanced-match/1.0.2:
|
||||||
|
@ -328,6 +348,8 @@ packages:
|
||||||
qs: 6.9.7
|
qs: 6.9.7
|
||||||
raw-body: 2.4.3
|
raw-body: 2.4.3
|
||||||
type-is: 1.6.18
|
type-is: 1.6.18
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/brace-expansion/1.1.11:
|
/brace-expansion/1.1.11:
|
||||||
|
@ -354,8 +376,6 @@ packages:
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build: 4.3.0
|
node-gyp-build: 4.3.0
|
||||||
dev: false
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/builtin-modules/3.2.0:
|
/builtin-modules/3.2.0:
|
||||||
resolution: {integrity: sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==}
|
resolution: {integrity: sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==}
|
||||||
|
@ -417,7 +437,7 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/clone-response/1.0.2:
|
/clone-response/1.0.2:
|
||||||
resolution: {integrity: sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=}
|
resolution: {integrity: sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
mimic-response: 1.0.1
|
mimic-response: 1.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -515,12 +535,22 @@ packages:
|
||||||
|
|
||||||
/debug/2.6.9:
|
/debug/2.6.9:
|
||||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.0.0
|
ms: 2.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/debug/3.2.7:
|
/debug/3.2.7:
|
||||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -562,7 +592,7 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/delayed-stream/1.0.0:
|
/delayed-stream/1.0.0:
|
||||||
resolution: {integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk=}
|
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
@ -625,14 +655,14 @@ packages:
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/express-ws/5.0.2_9938c0c959a5f2b516218250f750cbca:
|
/express-ws/5.0.2_te4mbskzuxzlkfrbqjipouglzi:
|
||||||
resolution: {integrity: sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==}
|
resolution: {integrity: sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ==}
|
||||||
engines: {node: '>=4.5.0'}
|
engines: {node: '>=4.5.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
express: ^4.0.0 || ^5.0.0-alpha.1
|
express: ^4.0.0 || ^5.0.0-alpha.1
|
||||||
dependencies:
|
dependencies:
|
||||||
express: 4.17.3
|
express: 4.17.3
|
||||||
ws: 7.5.7_bffff4271b89d628e8333ead80d3d8e8
|
ws: 7.5.7_x777ijy3rhlcr2bth2wybu6y5a
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
@ -672,6 +702,8 @@ packages:
|
||||||
type-is: 1.6.18
|
type-is: 1.6.18
|
||||||
utils-merge: 1.0.1
|
utils-merge: 1.0.1
|
||||||
vary: 1.1.2
|
vary: 1.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/fast-safe-stringify/2.1.1:
|
/fast-safe-stringify/2.1.1:
|
||||||
|
@ -700,6 +732,8 @@ packages:
|
||||||
parseurl: 1.3.3
|
parseurl: 1.3.3
|
||||||
statuses: 1.5.0
|
statuses: 1.5.0
|
||||||
unpipe: 1.0.0
|
unpipe: 1.0.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/fn.name/1.1.0:
|
/fn.name/1.1.0:
|
||||||
|
@ -951,7 +985,7 @@ packages:
|
||||||
resolution: {integrity: sha512-a7Jipme3XIBIryJluWP5LQrEAvhobDPyScBe+q+MYwxBiMT2Ck7msy4tAdF8TAa33FMdJqX4guP81Yhiu6BkmQ==}
|
resolution: {integrity: sha512-a7Jipme3XIBIryJluWP5LQrEAvhobDPyScBe+q+MYwxBiMT2Ck7msy4tAdF8TAa33FMdJqX4guP81Yhiu6BkmQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/livereload/0.9.3_bffff4271b89d628e8333ead80d3d8e8:
|
/livereload/0.9.3_x777ijy3rhlcr2bth2wybu6y5a:
|
||||||
resolution: {integrity: sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==}
|
resolution: {integrity: sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -959,7 +993,7 @@ packages:
|
||||||
chokidar: 3.5.3
|
chokidar: 3.5.3
|
||||||
livereload-js: 3.3.3
|
livereload-js: 3.3.3
|
||||||
opts: 2.0.2
|
opts: 2.0.2
|
||||||
ws: 7.5.7_bffff4271b89d628e8333ead80d3d8e8
|
ws: 7.5.7_x777ijy3rhlcr2bth2wybu6y5a
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
@ -1021,6 +1055,8 @@ packages:
|
||||||
dependencies:
|
dependencies:
|
||||||
commander: 8.3.0
|
commander: 8.3.0
|
||||||
probe-image-size: 7.2.3
|
probe-image-size: 7.2.3
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/methods/1.1.2:
|
/methods/1.1.2:
|
||||||
|
@ -1079,7 +1115,7 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/ms/2.0.0:
|
/ms/2.0.0:
|
||||||
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
|
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/ms/2.1.2:
|
/ms/2.1.2:
|
||||||
|
@ -1098,6 +1134,8 @@ packages:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
iconv-lite: 0.4.24
|
iconv-lite: 0.4.24
|
||||||
sax: 1.2.4
|
sax: 1.2.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/negotiator/0.6.3:
|
/negotiator/0.6.3:
|
||||||
|
@ -1108,8 +1146,6 @@ packages:
|
||||||
/node-gyp-build/4.3.0:
|
/node-gyp-build/4.3.0:
|
||||||
resolution: {integrity: sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==}
|
resolution: {integrity: sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/normalize-path/3.0.0:
|
/normalize-path/3.0.0:
|
||||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
|
@ -1133,7 +1169,7 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/once/1.4.0:
|
/once/1.4.0:
|
||||||
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
|
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
wrappy: 1.0.2
|
wrappy: 1.0.2
|
||||||
|
|
||||||
|
@ -1181,6 +1217,8 @@ packages:
|
||||||
lodash.merge: 4.6.2
|
lodash.merge: 4.6.2
|
||||||
needle: 2.9.1
|
needle: 2.9.1
|
||||||
stream-parser: 0.3.1
|
stream-parser: 0.3.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/proxy-addr/2.0.7:
|
/proxy-addr/2.0.7:
|
||||||
|
@ -1294,17 +1332,17 @@ packages:
|
||||||
rollup: 2.68.0
|
rollup: 2.68.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rollup-plugin-livereload/2.0.5_bffff4271b89d628e8333ead80d3d8e8:
|
/rollup-plugin-livereload/2.0.5_x777ijy3rhlcr2bth2wybu6y5a:
|
||||||
resolution: {integrity: sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA==}
|
resolution: {integrity: sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA==}
|
||||||
engines: {node: '>=8.3'}
|
engines: {node: '>=8.3'}
|
||||||
dependencies:
|
dependencies:
|
||||||
livereload: 0.9.3_bffff4271b89d628e8333ead80d3d8e8
|
livereload: 0.9.3_x777ijy3rhlcr2bth2wybu6y5a
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rollup-plugin-svelte/7.1.0_rollup@2.68.0+svelte@3.46.4:
|
/rollup-plugin-svelte/7.1.0_dni462kqdqwcnfey6ikyaliv44:
|
||||||
resolution: {integrity: sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==}
|
resolution: {integrity: sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1396,6 +1434,8 @@ packages:
|
||||||
on-finished: 2.3.0
|
on-finished: 2.3.0
|
||||||
range-parser: 1.2.1
|
range-parser: 1.2.1
|
||||||
statuses: 1.5.0
|
statuses: 1.5.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/serialize-javascript/4.0.0:
|
/serialize-javascript/4.0.0:
|
||||||
|
@ -1412,6 +1452,8 @@ packages:
|
||||||
escape-html: 1.0.3
|
escape-html: 1.0.3
|
||||||
parseurl: 1.3.3
|
parseurl: 1.3.3
|
||||||
send: 0.17.2
|
send: 0.17.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/setprototypeof/1.2.0:
|
/setprototypeof/1.2.0:
|
||||||
|
@ -1495,9 +1537,11 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/stream-parser/0.3.1:
|
/stream-parser/0.3.1:
|
||||||
resolution: {integrity: sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M=}
|
resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 2.6.9
|
debug: 2.6.9
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/string_decoder/1.3.0:
|
/string_decoder/1.3.0:
|
||||||
|
@ -1545,10 +1589,21 @@ packages:
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/svelte-fontawesome/0.0.3:
|
||||||
|
resolution: {integrity: sha512-iC3aqOiaQ2yBJ5kxDIlQNhLQ1rRxxb6yxTzpzUTkLfFJSVGRCFZPNuS+Yu5xy9pg5c0dDz/E3f6KcmpeUlk3Hw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/svelte-inview/3.0.1_svelte@3.46.4:
|
||||||
|
resolution: {integrity: sha512-kmlD4l/z5tqMqGuWLOSz6Ys2ISJ2WXg01jFVygugma0XA45UeYNGjoRazp+gMvaWoHqCQPx6D8cH9YQBS1pZxA==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^3.0.0
|
||||||
|
dependencies:
|
||||||
|
svelte: 3.46.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/svelte/3.46.4:
|
/svelte/3.46.4:
|
||||||
resolution: {integrity: sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==}
|
resolution: {integrity: sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/terser/5.11.0:
|
/terser/5.11.0:
|
||||||
resolution: {integrity: sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==}
|
resolution: {integrity: sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==}
|
||||||
|
@ -1638,8 +1693,6 @@ packages:
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dependencies:
|
dependencies:
|
||||||
node-gyp-build: 4.3.0
|
node-gyp-build: 4.3.0
|
||||||
dev: false
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
/util-deprecate/1.0.2:
|
/util-deprecate/1.0.2:
|
||||||
resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=}
|
resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=}
|
||||||
|
@ -1681,9 +1734,9 @@ packages:
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/wrappy/1.0.2:
|
/wrappy/1.0.2:
|
||||||
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
/ws/7.5.7_bffff4271b89d628e8333ead80d3d8e8:
|
/ws/7.5.7_x777ijy3rhlcr2bth2wybu6y5a:
|
||||||
resolution: {integrity: sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==}
|
resolution: {integrity: sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==}
|
||||||
engines: {node: '>=8.3.0'}
|
engines: {node: '>=8.3.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1698,7 +1751,7 @@ packages:
|
||||||
bufferutil: 4.0.6
|
bufferutil: 4.0.6
|
||||||
utf-8-validate: 5.0.8
|
utf-8-validate: 5.0.8
|
||||||
|
|
||||||
/ws/8.5.0_bffff4271b89d628e8333ead80d3d8e8:
|
/ws/8.5.0_x777ijy3rhlcr2bth2wybu6y5a:
|
||||||
resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==}
|
resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1718,10 +1771,10 @@ packages:
|
||||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
git.freezerapp.xyz/RemixDev/deemix-js/58bd92bc303a338aafc56ccf51940713812756ec:
|
gitlab.com/RemixDev/deemix-js/a105b03beb93cc2efa5d723d7f2e3cb2237d4f08:
|
||||||
resolution: {commit: 58bd92bc303a338aafc56ccf51940713812756ec, repo: https://git.freezerapp.xyz/RemixDev/deemix-js, type: git}
|
resolution: {tarball: https://gitlab.com/api/v4/projects/RemixDev%2Fdeemix-js/repository/archive.tar.gz?ref=a105b03beb93cc2efa5d723d7f2e3cb2237d4f08}
|
||||||
name: deemix
|
name: deemix
|
||||||
version: 3.6.7
|
version: 3.6.10
|
||||||
dependencies:
|
dependencies:
|
||||||
async: 3.2.3
|
async: 3.2.3
|
||||||
deezer-js: 1.3.5
|
deezer-js: 1.3.5
|
||||||
|
|
|
@ -1,163 +0,0 @@
|
||||||
@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;
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/axios@0.20.0/dist/axios.min.js"></script>
|
|
||||||
<script src="index.js"></script>
|
|
||||||
<link href="index.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="/apple-icon-72x72.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="/apple-icon-76x76.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="/apple-icon-114x114.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="/apple-icon-120x120.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="/apple-icon-144x144.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="/apple-icon-152x152.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="/android-icon-192x192.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
|
||||||
<link rel="manifest" href="/manifest.json">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="header">
|
|
||||||
<div id="header-left">
|
|
||||||
<a href="https://deemix.app" target="_blank" class="big link"><img id="deemix" width="26" height="auto" src="https://deemix.app/img/logo.png"> deemix</a>
|
|
||||||
<a href="https://git.oat.zone/oat/deemix-web-frontend" target="_blank" class="big link"><img id="git" width="26" height="auto" src="https://git-scm.com/images/logos/downloads/Git-Icon-White.png"> git</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="header-right">
|
|
||||||
<label class="switch">
|
|
||||||
<input type="checkbox" id="theme-switch">
|
|
||||||
<span class="slider round"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<span id="main">
|
|
||||||
<div class="error" id="error"></div>
|
|
||||||
<input type="search" id="album-search" name="q">
|
|
||||||
<div id="progress"><div id="progress-album"></div><div id="progress-bar-wrapper"></div></div>
|
|
||||||
<div id="albums"></div>
|
|
||||||
</span>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,257 +0,0 @@
|
||||||
const h = window.window.location.hostname;
|
|
||||||
if (!h || h.includes('localhost')) {
|
|
||||||
window.document.title = 'deemix-web-frontend';
|
|
||||||
} else {
|
|
||||||
window.document.title = window.window.location.hostname
|
|
||||||
}
|
|
||||||
|
|
||||||
function download(url) {
|
|
||||||
console.log(url);
|
|
||||||
fetch(url)
|
|
||||||
.then(response => response.blob())
|
|
||||||
.then(blob => {
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = URL.createObjectURL(blob);
|
|
||||||
link.download = url.split('/').pop();
|
|
||||||
link.target = '_blank';
|
|
||||||
link.click();
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatTime(s) {
|
|
||||||
return Math.floor(s / 60).toString().padStart(2, '0') + ':' + (s % 60).toString().padStart(2, '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is literally a find replace in the css
|
|
||||||
function setTheme(theme) {
|
|
||||||
Array.from(document.styleSheets).map(e => {
|
|
||||||
try {
|
|
||||||
return Array.from(e.cssRules)
|
|
||||||
} catch (e) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}).flat().map(e => {
|
|
||||||
if (e.constructor != CSSMediaRule) return;
|
|
||||||
if (e.originalConditionText) e.conditionText = e.originalConditionText;
|
|
||||||
else e.originalConditionText = e.conditionText
|
|
||||||
if (theme === 'system') return
|
|
||||||
let match = e.conditionText.match(/prefers-color-scheme:\s*(light|dark)/i)
|
|
||||||
if (!match) return;
|
|
||||||
e.conditionText = e.conditionText.replace(match[0], (match[1].toLowerCase() == theme ? 'min' : 'max') + '-width: 0')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWebsocketLocation() {
|
|
||||||
return window.window.location.toString().replace('https://', 'wss://').replace('http://', 'ws://');
|
|
||||||
}
|
|
||||||
|
|
||||||
function addlog(log, text) {
|
|
||||||
log += `<br>${text}`;
|
|
||||||
log = log.split('<br>').slice(-5).join('<br>');
|
|
||||||
if (log.startsWith('<br>')) log = log.replace('<br>', '');
|
|
||||||
return log;
|
|
||||||
}
|
|
||||||
|
|
||||||
function startDownload(id, isAlbum) {
|
|
||||||
let log = '';
|
|
||||||
|
|
||||||
let coverArt;
|
|
||||||
let title;
|
|
||||||
let artist;
|
|
||||||
|
|
||||||
let type = isAlbum ? 'album' : 'track'
|
|
||||||
document.getElementById('albums').innerHTML = '';
|
|
||||||
document.getElementById('progress-album').innerHTML = '<div class="lds-ring"><div></div><div></div><div></div><div></div></div>';
|
|
||||||
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 = addlog(log, `[${d.data.data.title}] ${d.data.state}`);
|
|
||||||
} else if (d.key === 'updateQueue') {
|
|
||||||
if (d.data.progress) {
|
|
||||||
document.getElementById('progress-bar-wrapper').innerHTML = `<br><div id="progress-bar"><div id="progress-bar-inner" style="height:100%;width:${d.data.progress}%"></div></div>`
|
|
||||||
}
|
|
||||||
} else if (d.key === 'coverArt') {
|
|
||||||
log = addlog(log, 'Fetched cover art');
|
|
||||||
coverArt = d.data;
|
|
||||||
} else if (d.key === 'metadata') {
|
|
||||||
log = addlog(log, 'Fetched metadata');
|
|
||||||
title = d.data.title;
|
|
||||||
artist = d.data.artist;
|
|
||||||
} else if (d.key === 'download') {
|
|
||||||
download(d.data);
|
|
||||||
} else if (d.key === 'finishDownload') {
|
|
||||||
log = addlog(log, 'Download finished');
|
|
||||||
} else if (d.key === 'zipping') {
|
|
||||||
log = addlog(log, 'Zipping up files');
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('progress-album').innerHTML = `
|
|
||||||
<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">${log || ''}</div>
|
|
||||||
</div>
|
|
||||||
<div class="album-image-wrapper">
|
|
||||||
<img class="album-image" width="128" height="128" src="${coverArt}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
ws.onerror = (e) => {
|
|
||||||
console.log('error: ' + e);
|
|
||||||
error(e.toString());
|
|
||||||
}
|
|
||||||
ws.onclose = (e) => {
|
|
||||||
change();
|
|
||||||
if (e.code !== 1000) error(`websocket closed unexpectedly with code ${e.code}\n${e.reason}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function error(e) {
|
|
||||||
document.getElementById('error').innerHTML = `<div class="big">error!</div>${e.split('\n').join('<br>')}`;
|
|
||||||
document.getElementById('error').style.display = 'block';
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
function clearError() {
|
|
||||||
document.getElementById('error').innerHTML = '';
|
|
||||||
document.getElementById('error').style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
let change; // fuck off js
|
|
||||||
|
|
||||||
window.onload = () => {
|
|
||||||
clearError();
|
|
||||||
|
|
||||||
// dirty theme hacks :tm:
|
|
||||||
|
|
||||||
const color = window.getComputedStyle(document.querySelector('body')).getPropertyValue('color');
|
|
||||||
const rgbRegex = /rgb\((\d+), ?(\d+), ?(\d+)\)/;
|
|
||||||
const r = rgbRegex.exec(color);
|
|
||||||
|
|
||||||
let brightness = (Number(r[1]) + Number(r[2]) + Number(r[3])) / (255 * 3);
|
|
||||||
|
|
||||||
if (brightness > 0.5) { // light text, dark theme
|
|
||||||
document.getElementById('theme-switch').checked = true
|
|
||||||
} else { // dark text, light theme
|
|
||||||
document.getElementById('theme-switch').checked = false
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('theme-switch').addEventListener('click', () => {
|
|
||||||
setTheme(document.getElementById('theme-switch').checked ? 'dark' : 'light');
|
|
||||||
});
|
|
||||||
|
|
||||||
// list of based music
|
|
||||||
const placeholders = [
|
|
||||||
'xilent - we are dust',
|
|
||||||
'joyryde - brave',
|
|
||||||
'phaseone - the risen ep',
|
|
||||||
// 'blanke & godlands - hellraiser', // deemix search sucks and this doesnt actually work
|
|
||||||
'camellia - blackmagik blazing',
|
|
||||||
't+pazolite - without permission',
|
|
||||||
'the brig - vindicate',
|
|
||||||
'bossfight - next wave',
|
|
||||||
'matt doe - g.a.s.',
|
|
||||||
'virtual riot - save yourself ep',
|
|
||||||
'panda eyes - hold on',
|
|
||||||
'clockvice - disgrace',
|
|
||||||
'scheme - quake',
|
|
||||||
'eliminate - belly of the beast ep',
|
|
||||||
'apriskah - mistakes ep',
|
|
||||||
'dog blood - turn off the lights',
|
|
||||||
'silentroom - memory waves',
|
|
||||||
'metaroom - metadata',
|
|
||||||
'prismo - nightmare',
|
|
||||||
'ray volpe - rise of the volpetron',
|
|
||||||
'eliminate - mula',
|
|
||||||
'vorso - full tilt',
|
|
||||||
'billiummoto - link(init)',
|
|
||||||
'voltra - radar dx',
|
|
||||||
'zomboy - end game',
|
|
||||||
'dr. ozi - host ep',
|
|
||||||
'nitepunk - absolute zero',
|
|
||||||
'2 mello - sounds of tokyo-to future',
|
|
||||||
'must die - crisis vision redux',
|
|
||||||
'moe shop - notice',
|
|
||||||
'cysmix - escapism',
|
|
||||||
'rogue - rattlesnake'
|
|
||||||
];
|
|
||||||
|
|
||||||
const search = document.getElementById('album-search');
|
|
||||||
search.setAttribute('placeholder', placeholders[Math.floor(Math.random() * placeholders.length)]);
|
|
||||||
|
|
||||||
change = async () => {
|
|
||||||
clearError();
|
|
||||||
const value = document.getElementById('album-search').value;
|
|
||||||
if (value === '') return document.getElementById('albums').innerHTML = '';
|
|
||||||
document.getElementById('progress-album').innerHTML = '';
|
|
||||||
document.getElementById('progress-bar-wrapper').innerHTML = '';
|
|
||||||
document.getElementById('albums').innerHTML = '<div class="lds-ring"><div></div><div></div><div></div><div></div></div>';
|
|
||||||
let d;
|
|
||||||
try {
|
|
||||||
d = await axios.get('/api/search', {params: {search: value}});
|
|
||||||
} catch(err) {
|
|
||||||
error(err.toString());
|
|
||||||
document.getElementById('albums').innerHTML = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.getElementById('albums').innerHTML = d.data.map(d =>
|
|
||||||
`
|
|
||||||
<div class="album" id="album-${d.id}">
|
|
||||||
<div class="album-metadata">
|
|
||||||
<span class="metadata">
|
|
||||||
<span class="big">${d.title}</span>
|
|
||||||
<br>
|
|
||||||
<span class="small">by ${d.artist.name}</span>
|
|
||||||
</span>
|
|
||||||
<img class="album-download" onclick="clearError(); startDownload(${d.id}, true)" width="48" height="48" src="assets/download.svg">
|
|
||||||
</div>
|
|
||||||
<div class="album-image-wrapper">
|
|
||||||
<img class="album-image" width="128" height="128" src="https://e-cdns-images.dzcdn.net/images/cover/${d.cover}/128x128-000000-80-0-0.jpg">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="album-bottom" id="album-bottom-${d.id}"></div>
|
|
||||||
`
|
|
||||||
).join('<br>');
|
|
||||||
|
|
||||||
if (d.data.length === 0) return document.getElementById('albums').innerHTML = '<span class="small">Not found!</span>';
|
|
||||||
|
|
||||||
for (c of document.getElementById('albums').children) {
|
|
||||||
let id = c.id.split('-')[1];
|
|
||||||
if (document.getElementById('album-bottom-' + id)) {
|
|
||||||
document.getElementById('album-bottom-' + id).innerHTML = '<div class="lds-ring"><div></div><div></div><div></div><div></div></div>';
|
|
||||||
let album;
|
|
||||||
try {
|
|
||||||
album = await axios.get('/api/album', {params: {id: id}});
|
|
||||||
} catch(err) {
|
|
||||||
error(err.toString());
|
|
||||||
document.getElementById('album-bottom-' + id).innerHTML = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('album-bottom-' + id).innerHTML = album.data.tracks.map(d =>
|
|
||||||
`
|
|
||||||
<div class="track" id="track-${d.id}">
|
|
||||||
<span>${d.artist} - ${d.title}</span>
|
|
||||||
<span>
|
|
||||||
<span class="track-download-wrapper" onclick="clearError(); startDownload(${d.id}, false)">
|
|
||||||
<img class="album-download" width="32" height="32" src="assets/download.svg">
|
|
||||||
</span>
|
|
||||||
${formatTime(d.duration)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
).join('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
search.addEventListener('change', change);
|
|
||||||
};
|
|
|
@ -22,13 +22,14 @@ if (config.server.proxy) {
|
||||||
logger.info('enabled express.js reverse proxy settings');
|
logger.info('enabled express.js reverse proxy settings');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.use(express.static('app/dist'));
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
logger.http(`${(config.server.proxy && req.headers['x-forwarded-for']) || req.connection.remoteAddress} ${req.method} ${req.originalUrl} `);
|
logger.http(`${(config.server.proxy && req.headers['x-forwarded-for']) || req.connection.remoteAddress} ${req.method} ${req.originalUrl} `);
|
||||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(express.static('public'));
|
|
||||||
app.use('/data', express.static('data', {extensions: ['flac', 'mp3']}));
|
app.use('/data', express.static('data', {extensions: ['flac', 'mp3']}));
|
||||||
|
|
||||||
import get from './get';
|
import get from './get';
|
||||||
|
|