WIP blurash & sync card ?
This commit is contained in:
@@ -115,6 +115,7 @@ const isTrackLoaded = ref(false)
|
|||||||
|
|
||||||
.face-up {
|
.face-up {
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
|
border: none;
|
||||||
transform: rotateY(0deg);
|
transform: rotateY(0deg);
|
||||||
transition: box-shadow 0.6s;
|
transition: box-shadow 0.6s;
|
||||||
|
|
||||||
|
|||||||
BIN
data/music.db
BIN
data/music.db
Binary file not shown.
@@ -13,7 +13,7 @@ export default defineNuxtConfig({
|
|||||||
tasks: true
|
tasks: true
|
||||||
},
|
},
|
||||||
scheduledTasks: {
|
scheduledTasks: {
|
||||||
'*/5 * * * *': ['syncTracks']
|
'*/1 * * * *': ['sync-tracks']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
compatibilityDate: '2025-07-15',
|
compatibilityDate: '2025-07-15',
|
||||||
|
|||||||
@@ -23,9 +23,11 @@
|
|||||||
"@nuxtjs/tailwindcss": "6.14.0",
|
"@nuxtjs/tailwindcss": "6.14.0",
|
||||||
"@pinia/nuxt": "0.11.2",
|
"@pinia/nuxt": "0.11.2",
|
||||||
"atropos": "^2.0.2",
|
"atropos": "^2.0.2",
|
||||||
|
"blurhash": "^2.0.5",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"nuxt": "^4.3.0",
|
"nuxt": "^4.3.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
|
"sharp": "^0.34.5",
|
||||||
"vue": "^3.5.18",
|
"vue": "^3.5.18",
|
||||||
"vue-router": "^4.5.1",
|
"vue-router": "^4.5.1",
|
||||||
"vuedraggable": "^4.1.0"
|
"vuedraggable": "^4.1.0"
|
||||||
|
|||||||
279
pnpm-lock.yaml
generated
279
pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
|||||||
atropos:
|
atropos:
|
||||||
specifier: ^2.0.2
|
specifier: ^2.0.2
|
||||||
version: 2.0.2
|
version: 2.0.2
|
||||||
|
blurhash:
|
||||||
|
specifier: ^2.0.5
|
||||||
|
version: 2.0.5
|
||||||
drizzle-orm:
|
drizzle-orm:
|
||||||
specifier: ^0.45.1
|
specifier: ^0.45.1
|
||||||
version: 0.45.1(@libsql/client@0.17.0)
|
version: 0.45.1(@libsql/client@0.17.0)
|
||||||
@@ -32,6 +35,9 @@ importers:
|
|||||||
pinia:
|
pinia:
|
||||||
specifier: ^3.0.3
|
specifier: ^3.0.3
|
||||||
version: 3.0.4(typescript@5.9.3)(vue@3.5.27(typescript@5.9.3))
|
version: 3.0.4(typescript@5.9.3)(vue@3.5.27(typescript@5.9.3))
|
||||||
|
sharp:
|
||||||
|
specifier: ^0.34.5
|
||||||
|
version: 0.34.5
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.18
|
specifier: ^3.5.18
|
||||||
version: 3.5.27(typescript@5.9.3)
|
version: 3.5.27(typescript@5.9.3)
|
||||||
@@ -845,6 +851,143 @@ packages:
|
|||||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||||
engines: {node: '>=18.18'}
|
engines: {node: '>=18.18'}
|
||||||
|
|
||||||
|
'@img/colour@1.0.0':
|
||||||
|
resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@img/sharp-darwin-arm64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-darwin-x64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||||
|
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||||
|
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||||
|
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm@0.34.5':
|
||||||
|
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-ppc64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-riscv64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-s390x@0.34.5':
|
||||||
|
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-x64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-wasm32@0.34.5':
|
||||||
|
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [wasm32]
|
||||||
|
|
||||||
|
'@img/sharp-win32-arm64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@img/sharp-win32-ia32@0.34.5':
|
||||||
|
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@img/sharp-win32-x64@0.34.5':
|
||||||
|
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@ioredis/commands@1.5.0':
|
'@ioredis/commands@1.5.0':
|
||||||
resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==}
|
resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==}
|
||||||
|
|
||||||
@@ -2339,6 +2482,9 @@ packages:
|
|||||||
birpc@2.9.0:
|
birpc@2.9.0:
|
||||||
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
|
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
|
||||||
|
|
||||||
|
blurhash@2.0.5:
|
||||||
|
resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==}
|
||||||
|
|
||||||
boolbase@1.0.0:
|
boolbase@1.0.0:
|
||||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||||
|
|
||||||
@@ -5270,6 +5416,10 @@ packages:
|
|||||||
setprototypeof@1.2.0:
|
setprototypeof@1.2.0:
|
||||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||||
|
|
||||||
|
sharp@0.34.5:
|
||||||
|
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -6706,6 +6856,102 @@ snapshots:
|
|||||||
|
|
||||||
'@humanwhocodes/retry@0.4.3': {}
|
'@humanwhocodes/retry@0.4.3': {}
|
||||||
|
|
||||||
|
'@img/colour@1.0.0': {}
|
||||||
|
|
||||||
|
'@img/sharp-darwin-arm64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-darwin-x64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-ppc64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-ppc64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-riscv64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-riscv64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-s390x@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-s390x': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-x64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-wasm32@0.34.5':
|
||||||
|
dependencies:
|
||||||
|
'@emnapi/runtime': 1.8.1
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-arm64@0.34.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-ia32@0.34.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-x64@0.34.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@ioredis/commands@1.5.0': {}
|
'@ioredis/commands@1.5.0': {}
|
||||||
|
|
||||||
'@isaacs/balanced-match@4.0.1': {}
|
'@isaacs/balanced-match@4.0.1': {}
|
||||||
@@ -8431,6 +8677,8 @@ snapshots:
|
|||||||
|
|
||||||
birpc@2.9.0: {}
|
birpc@2.9.0: {}
|
||||||
|
|
||||||
|
blurhash@2.0.5: {}
|
||||||
|
|
||||||
boolbase@1.0.0: {}
|
boolbase@1.0.0: {}
|
||||||
|
|
||||||
brace-expansion@1.1.12:
|
brace-expansion@1.1.12:
|
||||||
@@ -11798,6 +12046,37 @@ snapshots:
|
|||||||
|
|
||||||
setprototypeof@1.2.0: {}
|
setprototypeof@1.2.0: {}
|
||||||
|
|
||||||
|
sharp@0.34.5:
|
||||||
|
dependencies:
|
||||||
|
'@img/colour': 1.0.0
|
||||||
|
detect-libc: 2.1.2
|
||||||
|
semver: 7.7.3
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-darwin-arm64': 0.34.5
|
||||||
|
'@img/sharp-darwin-x64': 0.34.5
|
||||||
|
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||||
|
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-ppc64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-riscv64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-s390x': 1.2.4
|
||||||
|
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||||
|
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||||
|
'@img/sharp-linux-arm': 0.34.5
|
||||||
|
'@img/sharp-linux-arm64': 0.34.5
|
||||||
|
'@img/sharp-linux-ppc64': 0.34.5
|
||||||
|
'@img/sharp-linux-riscv64': 0.34.5
|
||||||
|
'@img/sharp-linux-s390x': 0.34.5
|
||||||
|
'@img/sharp-linux-x64': 0.34.5
|
||||||
|
'@img/sharp-linuxmusl-arm64': 0.34.5
|
||||||
|
'@img/sharp-linuxmusl-x64': 0.34.5
|
||||||
|
'@img/sharp-wasm32': 0.34.5
|
||||||
|
'@img/sharp-win32-arm64': 0.34.5
|
||||||
|
'@img/sharp-win32-ia32': 0.34.5
|
||||||
|
'@img/sharp-win32-x64': 0.34.5
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
shebang-regex: 3.0.0
|
shebang-regex: 3.0.0
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const cards = sqliteTable('cards', {
|
|||||||
slug: text('slug').notNull(),
|
slug: text('slug').notNull(),
|
||||||
suit: text('suit').notNull(),
|
suit: text('suit').notNull(),
|
||||||
rank: text('rank').notNull(),
|
rank: text('rank').notNull(),
|
||||||
|
blurhash: text('blurhash').notNull(), // blurhash of the image
|
||||||
createdAt: int('created_at', { mode: 'timestamp' })
|
createdAt: int('created_at', { mode: 'timestamp' })
|
||||||
.notNull()
|
.notNull()
|
||||||
.$defaultFn(() => new Date()),
|
.$defaultFn(() => new Date()),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { eq, notInArray } from 'drizzle-orm'
|
import { eq, notInArray } from 'drizzle-orm'
|
||||||
import { useDB, schema } from '../db'
|
import { useDB, schema } from '../db'
|
||||||
import { scanMusicFolder } from '../utils/fileScanner'
|
import { scanMusicFolder } from '../utils/fileScanner'
|
||||||
|
import { generateBlurhash } from '../utils/blurHash'
|
||||||
|
|
||||||
const { cards } = schema
|
const { cards } = schema
|
||||||
|
|
||||||
@@ -21,11 +22,13 @@ export async function syncCardsWithDatabase(folderPath: string) {
|
|||||||
const scannedEsids = new Set(scannedCards.map((t) => t.esid))
|
const scannedEsids = new Set(scannedCards.map((t) => t.esid))
|
||||||
const cardsToDelete = existingCards.filter((t) => !scannedEsids.has(t.esid))
|
const cardsToDelete = existingCards.filter((t) => !scannedEsids.has(t.esid))
|
||||||
|
|
||||||
// 4. Insérer les nouvelles cards
|
// 4. Insérer les nouvelles cartes
|
||||||
if (cardsToInsert.length > 0) {
|
if (cardsToInsert.length > 0) {
|
||||||
// Dans la fonction syncCardsWithDatabase
|
// Générer tous les blurhash en parallèle
|
||||||
await db.insert(cards).values(
|
const cardsWithBlurhash = await Promise.all(
|
||||||
cardsToInsert.map((card) => ({
|
cardsToInsert.map(async (card) => {
|
||||||
|
const blurhash = await generateBlurhash(card.url_image)
|
||||||
|
return {
|
||||||
url_audio: card.url_audio,
|
url_audio: card.url_audio,
|
||||||
url_image: card.url_image,
|
url_image: card.url_image,
|
||||||
year: card.year,
|
year: card.year,
|
||||||
@@ -38,10 +41,15 @@ export async function syncCardsWithDatabase(folderPath: string) {
|
|||||||
slug: card.slug,
|
slug: card.slug,
|
||||||
createdAt: card.createdAt,
|
createdAt: card.createdAt,
|
||||||
suit: card.suit,
|
suit: card.suit,
|
||||||
rank: card.rank
|
rank: card.rank,
|
||||||
}))
|
blurhash: blurhash
|
||||||
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
console.log(`✅ ${cardsToInsert.length} cards ajoutées`)
|
|
||||||
|
// Insérer les cartes avec les blurhash déjà résolus
|
||||||
|
await db.insert(cards).values(cardsWithBlurhash)
|
||||||
|
console.log(`✅ ${cardsToInsert.length} cartes ajoutées`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Supprimer les cards obsolètes avec une requête distincte pour chaque esid
|
// 5. Supprimer les cards obsolètes avec une requête distincte pour chaque esid
|
||||||
|
|||||||
114
server/utils/blurHash.ts
Normal file
114
server/utils/blurHash.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// server/utils/blurHash.ts
|
||||||
|
import sharp from 'sharp'
|
||||||
|
import { encode } from 'blurhash'
|
||||||
|
import { $fetch } from 'ofetch'
|
||||||
|
import { mkdir, writeFile, unlink } from 'node:fs/promises'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
import { tmpdir } from 'node:os'
|
||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
|
||||||
|
// Dossier temporaire pour les images téléchargées
|
||||||
|
const TMP_DIR = join(tmpdir(), 'evilspins-images')
|
||||||
|
|
||||||
|
async function ensureTmpDir() {
|
||||||
|
try {
|
||||||
|
await mkdir(TMP_DIR, { recursive: true })
|
||||||
|
} catch (error) {
|
||||||
|
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadImage(url: string): Promise<string> {
|
||||||
|
await ensureTmpDir()
|
||||||
|
const tmpPath = join(TMP_DIR, `${randomUUID()}.jpg`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await $fetch(url, {
|
||||||
|
responseType: 'arrayBuffer',
|
||||||
|
headers: {
|
||||||
|
'User-Agent':
|
||||||
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
||||||
|
Accept: 'image/*,*/*;q=0.8'
|
||||||
|
},
|
||||||
|
// Timeout plus long
|
||||||
|
timeout: 60000, // 60 secondes
|
||||||
|
// Désactiver la compression pour les images
|
||||||
|
headers: {
|
||||||
|
'Accept-Encoding': 'identity'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await writeFile(tmpPath, Buffer.from(response))
|
||||||
|
return tmpPath
|
||||||
|
} catch (error) {
|
||||||
|
// Nettoyer en cas d'erreur
|
||||||
|
try {
|
||||||
|
await unlink(tmpPath).catch(() => {})
|
||||||
|
} catch {
|
||||||
|
/* Ignorer les erreurs de suppression */
|
||||||
|
}
|
||||||
|
console.error(`Failed to download image from ${url}:`, error.message)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDefaultBlurhash() {
|
||||||
|
// Un blurhash plus discret
|
||||||
|
return 'L5H2EC=H00~q^-=wD6xuxvV@%KxZ'
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateBlurhash(
|
||||||
|
input: Buffer | string,
|
||||||
|
componentX: number = 4,
|
||||||
|
componentY: number = 3
|
||||||
|
): Promise<string> {
|
||||||
|
let tmpPath: string | null = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof input === 'string') {
|
||||||
|
if (input.startsWith('http')) {
|
||||||
|
try {
|
||||||
|
tmpPath = await downloadImage(input)
|
||||||
|
input = tmpPath
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Using default blurhash for ${input} due to download error`)
|
||||||
|
return createDefaultBlurhash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier si le fichier existe
|
||||||
|
try {
|
||||||
|
const image = sharp(input).resize(32, 32, { fit: 'inside' }).ensureAlpha()
|
||||||
|
|
||||||
|
const { data, info } = await image.raw().toBuffer({ resolveWithObject: true })
|
||||||
|
return encode(new Uint8ClampedArray(data), info.width, info.height, componentX, componentY)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Error processing image ${input}:`, error.message)
|
||||||
|
return createDefaultBlurhash()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Si c'est déjà un Buffer
|
||||||
|
try {
|
||||||
|
const image = sharp(input).resize(32, 32, { fit: 'inside' }).ensureAlpha()
|
||||||
|
|
||||||
|
const { data, info } = await image.raw().toBuffer({ resolveWithObject: true })
|
||||||
|
return encode(new Uint8ClampedArray(data), info.width, info.height, componentX, componentY)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Error processing image buffer:', error.message)
|
||||||
|
return createDefaultBlurhash()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Unexpected error in generateBlurhash:', error.message)
|
||||||
|
return createDefaultBlurhash()
|
||||||
|
} finally {
|
||||||
|
// Nettoyer le fichier temporaire s'il existe
|
||||||
|
if (tmpPath) {
|
||||||
|
try {
|
||||||
|
await unlink(tmpPath).catch(() => {})
|
||||||
|
} catch {
|
||||||
|
/* Ignorer les erreurs de suppression */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user