pages: add background, import page with layout
This commit is contained in:
parent
6d9c826b1d
commit
2ffc9d9ab3
16
doc/concepts/unique-key.md
Normal file
16
doc/concepts/unique-key.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# AquaVox 中的唯一歌曲 ID
|
||||||
|
|
||||||
|
AquaVox 由于支持多种歌曲源,因此在代码层面标识时需要以不同的唯一 ID 区分。
|
||||||
|
|
||||||
|
- 对于仅在本地添加的歌曲,我们使用 UUIDv1 (由 [uuid](https://www.npmjs.com/package/uuid) 库提供)
|
||||||
|
- 对于在本地添加并在此之后跨设备同步的歌曲,我们仍然使用 UUIDv1,
|
||||||
|
因此代码逻辑上不会对任何新添加的歌曲主动去重。但在前端会通过歌曲名等元信息帮助用户排除重复歌曲。
|
||||||
|
- 对于本地添加,开启跨设备同步后在云端音乐库匹配的歌曲,我们会用云端的歌曲唯一 ID 覆盖本地 ID。
|
||||||
|
- 对于通过哔哩哔哩收藏夹导入,在云端音乐库中不存在的音乐,我们会以 BV 号作为唯一 ID。
|
||||||
|
- 对于云端音乐库中的歌曲,我们以 BV 号(首选)或 `md5(歌曲名+作者[主发布人])` 作为唯一 ID。
|
||||||
|
|
||||||
|
但是,AquaVox 的云端音乐库由于其特殊性质,不显式公开其存在。
|
||||||
|
我们未来可能允许基于社区的歌曲分享及交流,但不会像传统音乐平台一样直接公开音乐库。
|
||||||
|
|
||||||
|
因此,AquaVox 的所有歌曲都是本地优先的,如果在用户未手动导入并添加到本地音乐数据库或跨设备同步了数据库的情况下,
|
||||||
|
即使输入云端音乐库匹配的 ID 作为 URL 或搜索框等位置的参数,也不会主动向云端音乐库发送请求进行匹配。
|
@ -13,23 +13,28 @@
|
|||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@iconify/svelte": "^4.0.1",
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||||
"@types/eslint": "^8.56.0",
|
"@types/eslint": "^8.56.0",
|
||||||
|
"autoprefixer": "^10.4.19",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.35.1",
|
"eslint-plugin-svelte": "^2.35.1",
|
||||||
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"prettier-plugin-svelte": "^3.1.2",
|
"prettier-plugin-svelte": "^3.1.2",
|
||||||
"svelte": "^4.2.7",
|
"svelte": "^4.2.7",
|
||||||
"svelte-check": "^3.6.0",
|
"svelte-check": "^3.6.0",
|
||||||
|
"tailwindcss": "^3.4.3",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^5.0.3",
|
"vite": "^5.0.3",
|
||||||
"vitest": "^1.2.0"
|
"vitest": "^1.2.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"localforage": "^1.10.0"
|
"localforage": "^1.10.0",
|
||||||
|
"uuid": "^9.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3429
pnpm-lock.yaml
3429
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
7
src/app.css
Normal file
7
src/app.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@apply text-4xl font-bold leading-[4rem];
|
||||||
|
}
|
@ -6,21 +6,23 @@
|
|||||||
export let coverId: string;
|
export let coverId: string;
|
||||||
let canvas: HTMLCanvasElement;
|
let canvas: HTMLCanvasElement;
|
||||||
|
|
||||||
let flag = false;
|
|
||||||
localforage.getItem(`${coverId}-cover-cache`, function (err, file) {
|
localforage.getItem(`${coverId}-cover-cache`, function (err, file) {
|
||||||
if (file) {
|
if (file) {
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
blobToImageData(file as Blob).then((imageData) => {
|
blobToImageData(file as Blob).then((imageData) => {
|
||||||
console.log(imageData);
|
ctx?.putImageData(imageData, 0, 0);
|
||||||
ctx?.putImageData(imageData,0,0);
|
canvas.style.opacity = '1';
|
||||||
})
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
localforage.getItem(`${coverId}-cover`, function (err, file) {
|
localforage.getItem(`${coverId}-cover`, function (err, file) {
|
||||||
if (file) {
|
if (file) {
|
||||||
const path = URL.createObjectURL(file as File);
|
const path = URL.createObjectURL(file as File);
|
||||||
processImage(16, 3, 96, path, canvas, (resultImageData: ImageData) => {
|
processImage(16, 3, 96, path, canvas, (resultImageData: ImageData) => {
|
||||||
localforage.setItem(`${coverId}-cover-cache`, imageDataToBlob(resultImageData));
|
localforage.setItem(
|
||||||
|
`${coverId}-cover-cache`,
|
||||||
|
imageDataToBlob(resultImageData)
|
||||||
|
);
|
||||||
|
canvas.style.opacity = '1';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -49,5 +51,7 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.6s;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
3
src/components/base.svelte
Normal file
3
src/components/base.svelte
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
17
src/components/import/sourceCard.svelte
Normal file
17
src/components/import/sourceCard.svelte
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let title: string;
|
||||||
|
export let icon: string;
|
||||||
|
export let details: string;
|
||||||
|
import Icon from '@iconify/svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex relative h-20 w-full m-4 border-2 border-zinc-400 dark:border-neutral-700 rounded-lg">
|
||||||
|
<div class="ml-4 flex flex-col justify-center text-4xl">
|
||||||
|
<Icon icon={icon} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ml-4 flex flex-col justify-center">
|
||||||
|
<h3 class="text-lg font-semibold">{title}</h3>
|
||||||
|
<p>{details}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
10
src/routes/+layout.svelte
Normal file
10
src/routes/+layout.svelte
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script>
|
||||||
|
import '../app.css';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="h-fit min-h-screen w-screen max-w-[100vw] overflow-x-hidden overflow-y-auto
|
||||||
|
bg-slate-50 dark:bg-[#1f1f1f] text-black dark:text-white"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
3
src/routes/import/+layout.svelte
Normal file
3
src/routes/import/+layout.svelte
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div class="absolute w-screen md:w-2/3 lg:w-1/2 left-0 md:left-[16.6667%] lg:left-1/4 px-[3%] md:px-0 top-16">
|
||||||
|
<slot />
|
||||||
|
</div>
|
9
src/routes/import/+page.svelte
Normal file
9
src/routes/import/+page.svelte
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import SourceCard from "../../components/import/sourceCard.svelte";
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>导入</h1>
|
||||||
|
<p>希望从哪里导入你的歌曲?</p>
|
||||||
|
<SourceCard title="本地" details="" icon="uil:import" />
|
||||||
|
<SourceCard title="哔哩哔哩" details="" icon="ri:bilibili-fill" />
|
1
src/routes/play/[id]/+page.server.js
Normal file
1
src/routes/play/[id]/+page.server.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const ssr = false;
|
9
src/routes/play/[id]/+page.svelte
Normal file
9
src/routes/play/[id]/+page.svelte
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import Background from '../../../components/background.svelte';
|
||||||
|
|
||||||
|
const audioId = $page.params.id;
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Background coverId={audioId} />
|
8
tailwind.config.js
Normal file
8
tailwind.config.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user