Compare commits

8 Commits

Author SHA1 Message Date
valere
19b83e9ac1 clean CI
All checks were successful
Deploy App / build (push) Successful in 1m55s
Deploy App / deploy (push) Successful in 21s
2026-02-03 15:12:49 +01:00
9705257178 Update app/components/Platine.vue
All checks were successful
Deploy App / build (push) Successful in 35s
Deploy App / deploy (push) Successful in 28s
2026-01-26 16:20:43 +00:00
2f78442deb Update server/api/tracks/playlist.ts
All checks were successful
Deploy App / build (push) Successful in 3m45s
Deploy App / deploy (push) Successful in 20s
2026-01-26 16:11:11 +00:00
valere
c586cc3932 PLATINE blur bobine
All checks were successful
Deploy App / build (push) Successful in 1m53s
Deploy App / deploy (push) Successful in 15s
2026-01-04 23:01:26 +01:00
valere
11694d36dd CI copy server files
All checks were successful
Deploy App / build (push) Successful in 51s
Deploy App / deploy (push) Successful in 14s
2026-01-04 10:51:27 +01:00
valere
3b05938162 CI install sqlite3
All checks were successful
Deploy App / build (push) Successful in 2m36s
Deploy App / deploy (push) Successful in 17s
2026-01-04 10:34:10 +01:00
valere
f75a1481bd platine mobile size
All checks were successful
Deploy App / build (push) Successful in 20s
Deploy App / deploy (push) Successful in 14s
2026-01-02 22:34:11 +01:00
valere
bb791e35d1 platine transition
All checks were successful
Deploy App / build (push) Successful in 2m4s
Deploy App / deploy (push) Successful in 16s
2026-01-01 20:50:37 +01:00
10 changed files with 157 additions and 108 deletions

3
.env Executable file
View File

@@ -0,0 +1,3 @@
DOMAIN=evilspins.com
PORT=7901
PORT_EXPOSED=3000

View File

@@ -1,35 +0,0 @@
name: Deploy App
on: [push]
jobs:
build:
runs-on: ubuntu-22.04
container:
volumes:
- /var/docker-web:/var/docker-web
steps:
- uses: actions/checkout@v4
- name: Prepare and build app
run: |
REPO_NAME="${GITHUB_REPOSITORY##*/}"
APP_DIR="/var/docker-web/apps/${REPO_NAME}"
bash /var/docker-web/src/cli.sh down "${REPO_NAME}"
rm -rf "$APP_DIR"
mkdir "$APP_DIR"
cp -a $(find . -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'node_modules') "$APP_DIR/"
export COMPOSE_BAKE=false
docker rmi "local/${REPO_NAME}" 2>/dev/null || true
bash /var/docker-web/src/cli.sh build "${REPO_NAME}"
deploy:
runs-on: ubuntu-22.04
needs: build
container:
volumes:
- /var/docker-web:/var/docker-web
steps:
- uses: actions/checkout@v4
- name: Deploy with docker-web
run: |
REPO_NAME="${GITHUB_REPOSITORY##*/}"
bash /var/docker-web/src/cli.sh up "${REPO_NAME}"

23
.github/workflows/merge.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Delete Merged Branches
on:
pull_request:
types: [closed]
jobs:
delete-branch:
runs-on: ubuntu-22.04
container:
volumes:
- /var/docker-web:/var/docker-web
if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
steps:
- uses: actions/checkout@v4
- name: Delete merged branch
run: |
BRANCH_NAME="${{ github.event.pull_request.head.ref || github.event.inputs.branch_name }}"
APP_NAME="${BRANCH_NAME}_${GITHUB_REPOSITORY##*/}"
if [ "$BRANCH_NAME" != "main" ] && [ "$BRANCH_NAME" != "develop" ] && [ -n "$APP_NAME" ]; then
bash /var/docker-web/src/cli.sh rm -y "${APP_NAME}"
else
echo "Cannot delete protected branch: $BRANCH_NAME"
fi

41
.github/workflows/push.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Deploy App
on: [push]
jobs:
build:
runs-on: ubuntu-22.04
container:
volumes:
- /var/docker-web:/var/docker-web
steps:
- uses: actions/checkout@v4
- name: Build app
run: |
bash ./.github/workflows/setup-env.sh
set -a && source .env && set +a
if [ -n "$APP_NAME" ]; then
set -a && source .env && set +a
bash /var/docker-web/src/cli.sh down "${APP_NAME}"
rm -rf "$APP_DIR"
mkdir "$APP_DIR"
cp -a $(find . -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'node_modules') "$APP_DIR/"
export COMPOSE_BAKE=false
docker rmi "local/${APP_NAME}" 2>/dev/null || true
bash /var/docker-web/src/cli.sh build "${APP_NAME}"
fi
deploy:
runs-on: ubuntu-22.04
needs: build
container:
volumes:
- /var/docker-web:/var/docker-web
steps:
- uses: actions/checkout@v4
- name: Deploy
run: |
bash ./.github/workflows/setup-env.sh
set -a && source .env && set +a
if [ -n "$APP_NAME" ]; then
bash /var/docker-web/src/cli.sh up "${APP_NAME}"
fi

34
.github/workflows/setup-env.sh vendored Normal file
View File

@@ -0,0 +1,34 @@
#!/bin/bash
set -a && source .env && set +a
changeEnvVar() {
local var_name="$1"
local new_value="$2"
local env_file=".env"
if grep -q "^${var_name}=" "$env_file"; then
sed -i "s|${var_name}=.*|${var_name}=${new_value}|" "$env_file"
else
echo "${var_name}=${new_value}" >> "$env_file"
fi
}
# Variables GitHub
APP_NAME="${GITHUB_REPOSITORY##*/}"
BRANCH_NAME=$GITHUB_REF_NAME
# Configuration pour les branches non-principales
if [ "$BRANCH_NAME" != "main" ] && [ "$BRANCH_NAME" != "master" ]; then
DOMAIN="$BRANCH_NAME.$DOMAIN"
APP_NAME="${BRANCH_NAME}_${APP_NAME}"
PORT=$(bash /var/docker-web/src/cli.sh ALLOCATE_PORT)
sed -i "s|${GITHUB_REPOSITORY##*/}|$APP_NAME|g" docker-compose.yml
fi
APP_DIR="/var/docker-web/apps/$APP_NAME"
changeEnvVar "DOMAIN" $DOMAIN
changeEnvVar "APP_NAME" $APP_NAME
changeEnvVar "PORT" $PORT
changeEnvVar "APP_DIR" $APP_DIR

View File

@@ -1,5 +1,6 @@
# Builder
FROM node:20-bookworm AS builder
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install --legacy-peer-deps
@@ -8,9 +9,12 @@ COPY . .
RUN npm run build
# Runtime
FROM node:20-slim AS runner
FROM node:20-alpine AS runner
RUN apk add --no-cache python3 make g++ sqlite
WORKDIR /app
COPY --from=builder /app/.output ./.output
COPY package*.json ./
COPY ./server/database ./server/database
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]

View File

@@ -1,16 +1,14 @@
<template>
<div class="platine pointer-events-none" :class="{ 'drag-over': isDragOver }" @dragenter.prevent="onDragEnter"
@dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop">
<div class="disc pointer-events-auto fixed" ref="discRef" :style="'background-image: url(/card-dock.svg)'"
id="disc">
<div
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"
<div class="platine pointer-events-none" :class="{ 'loading': platineStore.isLoadingTrack, 'mounted': isMounted }"
ref="platine">
<img class="cover" :src="platineStore.currentTrack?.coverId" />
<div class="disc pointer-events-auto fixed bg-transparent" ref="discRef" id="disc">
<div class="bobine"
: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">
<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 class="spinner"></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>
</div>
<div class="w-full h-1/5 text-base">
<!-- <div class="w-full h-1/5 text-base">
{{ platineStore.currentTrack?.title }}
<br>
{{ platineStore.currentTrack?.artist?.name }}
</div>
</div> -->
</div>
</template>
@@ -34,41 +32,12 @@ import type { Track } from '~~/types/types'
const props = defineProps<{ track?: Track }>()
const platineStore = usePlatineStore()
const discRef = ref<HTMLElement>()
const isDragOver = 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)
}
}
}
const platine = ref<HTMLElement>()
const isMounted = ref(false)
// Initialisation du lecteur
onMounted(() => {
isMounted.value = true
if (discRef.value) {
platineStore.initPlatine(discRef.value)
}
@@ -76,6 +45,7 @@ onMounted(() => {
// Nettoyage
onUnmounted(() => {
isMounted.value = false
platineStore.cleanup()
})
@@ -94,21 +64,34 @@ watch(() => props.track, (newTrack) => {
position: relative;
width: 100%;
height: 100%;
padding: 20px;
.card {
position: absolute !important;
z-index: 99;
top: -20%;
left: 50%;
bottom: 0;
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 {
position: relative;
background-color: white;
aspect-ratio: 1;
width: 100%;
overflow: hidden;
@@ -116,10 +99,10 @@ watch(() => props.track, (newTrack) => {
cursor: grab;
background-position: center;
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 & {
background-color: #4CAF50;
.loading & {
box-shadow: none;
}
}
@@ -192,7 +175,6 @@ watch(() => props.track, (newTrack) => {
height: 100%;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
z-index: 10;
}
.spinner {
@@ -211,15 +193,6 @@ watch(() => props.track, (newTrack) => {
}
.bobine {
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
opacity: 0.7;
}
@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;
}
</style>

View File

@@ -1,12 +1,14 @@
<template>
<slot />
<Bucket />
<Platine />
<Platine v-if="playerStore.currentTrack" />
</template>
<script setup lang="ts">
import type { Track } from '~~/types/types'
import { usePlayerStore } from '~/store/player'
const playerStore = usePlayerStore()
const onCardDropped = (card: Track) => {
console.log('Carte déposée dans le bucket:', card)
}
@@ -16,16 +18,17 @@ const onCardDropped = (card: Track) => {
.bucket,
.platine {
position: fixed;
bottom: 0;
bottom: -100%;
right: 0;
height: auto;
}
.bucket {
z-index: 70;
bottom: -260px;
transition: bottom 0.3s ease;
width: 100%;
overflow-x: scroll;
transition: bottom .3s ease;
&:hover,
.card-dragging & {
@@ -38,9 +41,15 @@ const onCardDropped = (card: Track) => {
}
.platine {
z-index: 60;
bottom: -70%;
transition: bottom 0.3s ease;
/* width: 25%; */
bottom: -100%;
transition: bottom 2s ease;
&.mounted {
z-index: 80;
bottom: 0;
width: 100%;
max-width: 450px;
}
}
</style>

3
env.sh
View File

@@ -1,3 +0,0 @@
export DOMAIN="evilspins.com"
export PORT="7901"
export PORT_EXPOSED="3000"

View File

@@ -58,7 +58,7 @@ export default eventHandler(async (event) => {
const date = new Date(year, month - 1, day, hour)
const card = getCardFromDate(date)
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 {
id: Number(`${year}${index + 1}`),