Compare commits
7 Commits
a5fe876e3f
...
A-new-Comp
| Author | SHA1 | Date | |
|---|---|---|---|
| 9705257178 | |||
| 2f78442deb | |||
|
|
c586cc3932 | ||
|
|
11694d36dd | ||
|
|
3b05938162 | ||
|
|
f75a1481bd | ||
|
|
bb791e35d1 |
@@ -1,5 +1,6 @@
|
|||||||
# Builder
|
# Builder
|
||||||
FROM node:20-bookworm AS builder
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
RUN npm install --legacy-peer-deps
|
RUN npm install --legacy-peer-deps
|
||||||
@@ -8,9 +9,12 @@ COPY . .
|
|||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# Runtime
|
# Runtime
|
||||||
FROM node:20-slim AS runner
|
FROM node:20-alpine AS runner
|
||||||
|
RUN apk add --no-cache python3 make g++ sqlite
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/.output ./.output
|
COPY --from=builder /app/.output ./.output
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
COPY ./server/database ./server/database
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
CMD ["node", ".output/server/index.mjs"]
|
CMD ["node", ".output/server/index.mjs"]
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="platine pointer-events-none" :class="{ 'drag-over': isDragOver }" @dragenter.prevent="onDragEnter"
|
<div class="platine pointer-events-none" :class="{ 'loading': platineStore.isLoadingTrack, 'mounted': isMounted }"
|
||||||
@dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop">
|
ref="platine">
|
||||||
<div class="disc pointer-events-auto fixed" ref="discRef" :style="'background-image: url(/card-dock.svg)'"
|
<img class="cover" :src="platineStore.currentTrack?.coverId" />
|
||||||
id="disc">
|
<div class="disc pointer-events-auto fixed bg-transparent" ref="discRef" id="disc">
|
||||||
<div
|
<div class="bobine"
|
||||||
class="bobine bg-slate-900 bg-opacity-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 rounded-full"
|
|
||||||
:style="{ height: platineStore.progressPercentage + '%', width: platineStore.progressPercentage + '%' }"></div>
|
:style="{ height: platineStore.progressPercentage + '%', width: platineStore.progressPercentage + '%' }"></div>
|
||||||
<img class="absolute size-full rounded-full" :src="platineStore.currentTrack?.coverId"
|
|
||||||
:alt="platineStore.currentTrack?.title">
|
|
||||||
|
|
||||||
<div class="disc-label rounded-full bg-cover bg-center">
|
<div class="disc-label rounded-full bg-cover bg-center">
|
||||||
<img src="/favicon.svg" class="size-1/3">
|
<img src="/favicon.svg" class="size-1/2 bg-black rounded-full p-5">
|
||||||
<div v-if="platineStore.isLoadingTrack" class="loading-indicator">
|
<div v-if="platineStore.isLoadingTrack" class="loading-indicator">
|
||||||
<div class="spinner"></div>
|
<div class="spinner"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -18,11 +16,11 @@
|
|||||||
<div v-if="!platineStore.isLoadingTrack" class="absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow">
|
<div v-if="!platineStore.isLoadingTrack" class="absolute top-1/2 right-8 size-1/12 rounded-full bg-esyellow">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full h-1/5 text-base">
|
<!-- <div class="w-full h-1/5 text-base">
|
||||||
{{ platineStore.currentTrack?.title }}
|
{{ platineStore.currentTrack?.title }}
|
||||||
<br>
|
<br>
|
||||||
{{ platineStore.currentTrack?.artist?.name }}
|
{{ platineStore.currentTrack?.artist?.name }}
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -34,41 +32,12 @@ import type { Track } from '~~/types/types'
|
|||||||
const props = defineProps<{ track?: Track }>()
|
const props = defineProps<{ track?: Track }>()
|
||||||
const platineStore = usePlatineStore()
|
const platineStore = usePlatineStore()
|
||||||
const discRef = ref<HTMLElement>()
|
const discRef = ref<HTMLElement>()
|
||||||
const isDragOver = ref(false)
|
const platine = ref<HTMLElement>()
|
||||||
|
const isMounted = ref(false)
|
||||||
// Gestion du drag and drop
|
|
||||||
const onDragEnter = (e: DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
isDragOver.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDragOver = (e: DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
isDragOver.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDragLeave = () => {
|
|
||||||
isDragOver.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDrop = (e: DragEvent) => {
|
|
||||||
isDragOver.value = false
|
|
||||||
const cardData = e.dataTransfer?.getData('application/json')
|
|
||||||
|
|
||||||
if (cardData) {
|
|
||||||
try {
|
|
||||||
const newTrack = JSON.parse(cardData)
|
|
||||||
if (newTrack && newTrack.url) {
|
|
||||||
platineStore.loadTrack(newTrack)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors du traitement de la carte déposée', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialisation du lecteur
|
// Initialisation du lecteur
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
isMounted.value = true
|
||||||
if (discRef.value) {
|
if (discRef.value) {
|
||||||
platineStore.initPlatine(discRef.value)
|
platineStore.initPlatine(discRef.value)
|
||||||
}
|
}
|
||||||
@@ -76,6 +45,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
// Nettoyage
|
// Nettoyage
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
isMounted.value = false
|
||||||
platineStore.cleanup()
|
platineStore.cleanup()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,21 +64,34 @@ watch(() => props.track, (newTrack) => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
z-index: 99;
|
|
||||||
top: -20%;
|
top: -20%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
transform: translate(-50%, 50%);
|
transform: translate(-50%, 50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-radius: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: opacity 3s ease;
|
||||||
|
|
||||||
|
.loading & {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.disc {
|
.disc {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: white;
|
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -116,10 +99,10 @@ watch(() => props.track, (newTrack) => {
|
|||||||
cursor: grab;
|
cursor: grab;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.4);
|
||||||
|
|
||||||
.dragoOver & {
|
.loading & {
|
||||||
background-color: #4CAF50;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +175,6 @@ watch(() => props.track, (newTrack) => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
@@ -211,15 +193,6 @@ watch(() => props.track, (newTrack) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bobine {
|
.bobine {
|
||||||
&::before {
|
@apply bg-slate-900 bg-opacity-50 backdrop-blur absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 rounded-full;
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-size: cover;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<slot />
|
<slot />
|
||||||
<Bucket />
|
<Bucket />
|
||||||
<Platine />
|
<Platine v-if="playerStore.currentTrack" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track } from '~~/types/types'
|
import type { Track } from '~~/types/types'
|
||||||
|
import { usePlayerStore } from '~/store/player'
|
||||||
|
|
||||||
|
const playerStore = usePlayerStore()
|
||||||
const onCardDropped = (card: Track) => {
|
const onCardDropped = (card: Track) => {
|
||||||
console.log('Carte déposée dans le bucket:', card)
|
console.log('Carte déposée dans le bucket:', card)
|
||||||
}
|
}
|
||||||
@@ -16,16 +18,17 @@ const onCardDropped = (card: Track) => {
|
|||||||
.bucket,
|
.bucket,
|
||||||
.platine {
|
.platine {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: -100%;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bucket {
|
.bucket {
|
||||||
z-index: 70;
|
z-index: 70;
|
||||||
bottom: -260px;
|
bottom: -260px;
|
||||||
transition: bottom 0.3s ease;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
|
transition: bottom .3s ease;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
.card-dragging & {
|
.card-dragging & {
|
||||||
@@ -38,9 +41,15 @@ const onCardDropped = (card: Track) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.platine {
|
.platine {
|
||||||
z-index: 60;
|
bottom: -100%;
|
||||||
bottom: -70%;
|
transition: bottom 2s ease;
|
||||||
transition: bottom 0.3s ease;
|
|
||||||
/* width: 25%; */
|
&.mounted {
|
||||||
|
z-index: 80;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
max-width: 450px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
3
env.sh
3
env.sh
@@ -1,3 +0,0 @@
|
|||||||
export DOMAIN="evilspins.com"
|
|
||||||
export PORT="7901"
|
|
||||||
export PORT_EXPOSED="3000"
|
|
||||||
@@ -58,7 +58,7 @@ export default eventHandler(async (event) => {
|
|||||||
const date = new Date(year, month - 1, day, hour)
|
const date = new Date(year, month - 1, day, hour)
|
||||||
const card = getCardFromDate(date)
|
const card = getCardFromDate(date)
|
||||||
const url = `${urlPrefix}/${encodeURIComponent(file)}`
|
const url = `${urlPrefix}/${encodeURIComponent(file)}`
|
||||||
const coverId = `${urlPrefix}/cover/${encodeURIComponent(file).replace(EXT_RE, '.jpg')}`
|
const coverId = `${urlPrefix}/${encodeURIComponent(file).replace(EXT_RE, '.jpg')}`
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: Number(`${year}${index + 1}`),
|
id: Number(`${year}${index + 1}`),
|
||||||
|
|||||||
Reference in New Issue
Block a user