Compare commits
10 Commits
8d3586f4d4
...
7a1cdf2178
Author | SHA1 | Date | |
---|---|---|---|
|
7a1cdf2178 | ||
|
d013e62fcf | ||
|
9c1204b46b | ||
|
8541050011 | ||
|
480e8f008c | ||
|
7f8eb7bbb9 | ||
|
7e9c0d3caf | ||
|
f972137389 | ||
|
7f8ed0e8a0 | ||
|
0ca4cc3bfe |
16
.drone.yml
@@ -1,16 +0,0 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
- name: deploy
|
||||
image: docker:dind
|
||||
commands:
|
||||
- apk add --upgrade npm bash findutils rsync sed
|
||||
- WORKDIR="/var/docker-web/apps/$DRONE_REPO_NAME"
|
||||
- rm -rf $WORKDIR
|
||||
- mkdir $WORKDIR
|
||||
- rsync -av --exclude ./node_modules /drone/src/ $WORKDIR
|
||||
- cd $WORKDIR
|
||||
- npm ci
|
||||
- bash /var/docker-web/src/cli.sh up $DRONE_REPO_NAME
|
21
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Deploy App
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
volumes:
|
||||
- /var/docker-web:/var/docker-web
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install
|
||||
run: |
|
||||
APP_DIR=/var/docker-web/apps/${GITHUB_REPOSITORY##*/}
|
||||
mkdir -p $APP_DIR
|
||||
cp -a $(find . -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'node_modules') "$APP_DIR/"
|
||||
- name: up
|
||||
run: |
|
||||
export COMPOSE_BAKE=false
|
||||
bash /var/docker-web/src/cli.sh up ${GITHUB_REPOSITORY##*/}
|
5
.gitignore
vendored
@@ -17,3 +17,8 @@ logs
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
37
Dockerfile
@@ -1,19 +1,30 @@
|
||||
# INSTALL
|
||||
FROM node:18-alpine as builder
|
||||
# Stage de build
|
||||
FROM node:20-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Installer pnpm
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Copier package.json et lockfile pour cache pnpm
|
||||
COPY package.json pnpm-lock.yaml* ./
|
||||
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copier tout le projet
|
||||
COPY . .
|
||||
RUN npm ci && npm cache clean --force
|
||||
ADD . .
|
||||
|
||||
# BUILD
|
||||
RUN npm run build
|
||||
# Build Nuxt
|
||||
RUN pnpm build
|
||||
|
||||
# Stage production
|
||||
FROM node:20-alpine
|
||||
|
||||
# PROD
|
||||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/.output /app/.output
|
||||
COPY --from=builder /app/.nuxt /app/.nuxt
|
||||
COPY --from=builder /app/.env /app/.env
|
||||
ENV HOST 0.0.0.0
|
||||
|
||||
COPY --from=build /app/.output .output
|
||||
COPY --from=build /app/package.json ./
|
||||
COPY --from=build /app/node_modules ./node_modules
|
||||
|
||||
EXPOSE 3000
|
||||
CMD source .env && node .output/server/index.mjs
|
||||
CMD ["node", ".output/server/index.mjs"]
|
||||
|
76
README.md
@@ -1 +1,75 @@
|
||||
# evilSpins
|
||||
# Nuxt Minimal Starter
|
||||
|
||||
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
|
||||
|
||||
## Setup
|
||||
|
||||
Make sure to install dependencies:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm install
|
||||
|
||||
# pnpm
|
||||
pnpm install
|
||||
|
||||
# yarn
|
||||
yarn install
|
||||
|
||||
# bun
|
||||
bun install
|
||||
```
|
||||
|
||||
## Development Server
|
||||
|
||||
Start the development server on `http://localhost:3000`:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run dev
|
||||
|
||||
# pnpm
|
||||
pnpm dev
|
||||
|
||||
# yarn
|
||||
yarn dev
|
||||
|
||||
# bun
|
||||
bun run dev
|
||||
```
|
||||
|
||||
## Production
|
||||
|
||||
Build the application for production:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run build
|
||||
|
||||
# pnpm
|
||||
pnpm build
|
||||
|
||||
# yarn
|
||||
yarn build
|
||||
|
||||
# bun
|
||||
bun run build
|
||||
```
|
||||
|
||||
Locally preview production build:
|
||||
|
||||
```bash
|
||||
# npm
|
||||
npm run preview
|
||||
|
||||
# pnpm
|
||||
pnpm preview
|
||||
|
||||
# yarn
|
||||
yarn preview
|
||||
|
||||
# bun
|
||||
bun run preview
|
||||
```
|
||||
|
||||
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
|
||||
|
11
app.vue
@@ -1,11 +0,0 @@
|
||||
<template>
|
||||
<NuxtPage />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// @todo : laod datas as plugin/middleware (cant load pinia in plugin/middleware) ?
|
||||
onMounted(async ()=>{
|
||||
const dataStore = await useDataStore()
|
||||
await dataStore.loadData()
|
||||
})
|
||||
</script>
|
6
app/app.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
<NuxtRouteAnnouncer />
|
||||
</NuxtLayout>
|
||||
</template>
|
8
app/components/atropos.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="flex items-center justify-center min-h-screen">
|
||||
<atropos-component class="h-72 w-60" active-offset="80" shadow-scale="1.05">
|
||||
<img src="/logo.svg">
|
||||
<img src="/ES01A/object.png">
|
||||
</atropos-component>
|
||||
</div>
|
||||
</template>
|
96
app/pages/index.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="background fixed w-full h-full">
|
||||
<video class="animation screen" loop autoplay muted ref="animation">
|
||||
<source src="https://files.erudi.fr/evilspins/sloughi-run-loop-big.webm" type="video/webm">
|
||||
<source src="https://files.erudi.fr/evilspins/sloughi-run-loop-small.webm" type="video/webm"
|
||||
media="all and (max-width: 640px)">
|
||||
</video>
|
||||
</div>
|
||||
<section class="splash-screen flex items-center flex-col">
|
||||
<figure class="ui">
|
||||
<img class="logo" src="/logo.svg">
|
||||
</figure>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.logo,
|
||||
.button,
|
||||
.shadow,
|
||||
.animation,
|
||||
.mix {
|
||||
transition: .7s opacity;
|
||||
}
|
||||
|
||||
.screen {
|
||||
position: absolute;
|
||||
height: 100vh;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.splash-screen {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
box-shadow: inset black 0px 1px 800px 200px;
|
||||
}
|
||||
|
||||
.animation {
|
||||
z-index: 1;
|
||||
object-fit: cover;
|
||||
opacity: .8;
|
||||
/* opacity: 0; */
|
||||
}
|
||||
|
||||
.mix {
|
||||
z-index: 4;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
z-index: 3;
|
||||
box-shadow: rgb(0, 0, 0) 0px 0px 170px 70px inset;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
.ui {
|
||||
z-index: 4;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
max-width: 80%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
filter: drop-shadow(8px 8px 0 rgb(0 0 0 / 0.8));
|
||||
}
|
||||
|
||||
.mixPlayer {
|
||||
background: black;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.hide {
|
||||
opacity: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.text-shadow {
|
||||
text-shadow: 3px 2px 8px black;
|
||||
}
|
||||
</style>
|
3
app/pages/wait.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<atropos />
|
||||
</template>
|
@@ -1,45 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
@apply bg-black;
|
||||
}
|
||||
|
||||
.button {
|
||||
text-decoration: none;
|
||||
box-shadow: 0 8px 0 0 black;
|
||||
transition: all .3s;
|
||||
border: 8px black solid;
|
||||
line-height: 100%;
|
||||
border-width: 2px;
|
||||
border-radius: 100px;
|
||||
cursor: pointer;
|
||||
color: black;
|
||||
font-size: 26px;
|
||||
background-color: #ffffff59;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
|
||||
@media (min-width: 780px) {
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #fdec50ff;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
box-shadow: 0 0 0 0 black;
|
||||
}
|
||||
|
||||
.button--screened {
|
||||
top: 74px;
|
||||
}
|
||||
|
||||
.compilation {
|
||||
cursor: pointer;
|
||||
max-width: 420px;
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<NuxtLink :to="'/compilations/' + props.data.id" class="compilation mx-auto p-4 inline-flex">
|
||||
<atropos-component class="my-atropos" active-offset="80" shadow-scale="1.05">
|
||||
<img :src="props.data.id + '/bkg.jpg'" data-atropos-offset="-8" />
|
||||
<img :src="props.data.id + '/object.png'" data-atropos-offset="-3" class="absolute inset-0 object-cover" />
|
||||
<img :src="props.data.id + '/name.png'" data-atropos-offset="0" class="absolute inset-0 object-cover" />
|
||||
<img src="/logo.svg" data-atropos-offset="0" width="70%" class="logo absolute inset-0" />
|
||||
</atropos-component>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps(['data', 'template'])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.logo {
|
||||
filter: drop-shadow(4px 4px 0 rgb(0 0 0 / 0.5));
|
||||
left: 14%;
|
||||
top: 10%;
|
||||
}
|
||||
</style>
|
@@ -1,11 +0,0 @@
|
||||
<template>
|
||||
<section>
|
||||
<div v-for="compilation in store.getAllCompilations" class="text-white">
|
||||
<compilationObject :data="compilation" template="full" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const store = useDataStore()
|
||||
</script>
|
@@ -1,31 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
const isLoaded = ref(false)
|
||||
const isPlaying = ref(false)
|
||||
const video = ref()
|
||||
async function play() {
|
||||
await video.value.player.playVideo()
|
||||
}
|
||||
function stateChange(event) {
|
||||
isPlaying.value = event.data === 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-center p-5">
|
||||
<ScriptYouTubePlayer ref="video" video-id="iyPiiZly864" class="group" @ready="isLoaded = true" @state-change="stateChange">
|
||||
<template #awaitingLoad>
|
||||
<div class="absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 h-[48px] w-[68px]">
|
||||
<svg height="100%" version="1.1" viewBox="0 0 68 48" width="100%"><path d="M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z" fill="#f00" /><path d="M 45,24 27,14 27,34" fill="#fff" /></svg>
|
||||
</div>
|
||||
</template>
|
||||
</ScriptYouTubePlayer>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div v-if="!isLoaded" class="mb-5" size="sm" color="blue" variant="soft" title="Click to load" description="Clicking the video will load the Vimeo iframe and start the video." />
|
||||
<button v-if="isLoaded && !isPlaying" @click="play">
|
||||
Play Video
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<div class="compilation mx-auto p-4 inline-flex">
|
||||
<atropos-component ref="atropos" class="my-atropos" active-offset="80" shadow-scale="1.05">
|
||||
<img src="/zero/sky-b.jpg" data-atropos-offset="-8" />
|
||||
<img src="/zero/propeller-b.png" data-atropos-offset="-3" class="absolute inset-0 object-cover" />
|
||||
<img src="/zero/zero-b.png" data-atropos-offset="0" class="absolute inset-0 object-cover" />
|
||||
<img src="/logo.svg" data-atropos-offset="0" width="70%" class="logo absolute inset-0" />
|
||||
</atropos-component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
const atropos = ref(null)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* .my-atropos {
|
||||
width: 320px;
|
||||
height: 160px;
|
||||
} */
|
||||
.logo {
|
||||
filter: drop-shadow(4px 4px 0 rgb(0 0 0 / 0.5));
|
||||
left: 14%;
|
||||
top: 10%;
|
||||
}
|
||||
</style>
|
5
config.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
export REPO_NAME="evilspins"
|
||||
export DOMAIN="evilspins.$MAIN_DOMAIN"
|
||||
export PORT="7901"
|
||||
export PORT_EXPOSED="3000"
|
||||
export REDIRECTIONS="" # example.$MAIN_DOMAIN->/route $MAIN_DOMAIN->url /route->/another-route /route->url
|
@@ -1,20 +1,23 @@
|
||||
services:
|
||||
|
||||
evilspins:
|
||||
build: .
|
||||
image: local/evilspins
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: evilspins
|
||||
restart: unless-stopped
|
||||
working_dir: /app
|
||||
ports:
|
||||
- $PORT:3000
|
||||
- "${PORT}:${PORT_EXPOSED}"
|
||||
volumes:
|
||||
- "${MEDIA_DIR}:/mnt/media"
|
||||
environment:
|
||||
VIRTUAL_HOST: "${DOMAIN}"
|
||||
LETSENCRYPT_HOST: "${DOMAIN}"
|
||||
PUID: "${PUID}"
|
||||
PGID: "${PGID}"
|
||||
volumes:
|
||||
- "${MEDIA_DIR}:/app/media"
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: dockerweb
|
||||
external: true
|
||||
external: true
|
||||
|
6
eslint.config.mjs
Normal file
@@ -0,0 +1,6 @@
|
||||
// @ts-check
|
||||
import withNuxt from './.nuxt/eslint.config.mjs'
|
||||
|
||||
export default withNuxt(
|
||||
// Your custom configs here
|
||||
)
|
125
logo.svg
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -1,24 +1,11 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
ssr: true,
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: { enabled: true },
|
||||
css: ['~/assets/css/main.css'],
|
||||
|
||||
postcss: {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
app: {
|
||||
head: {
|
||||
charset: 'utf-8',
|
||||
viewport: 'width=device-width, initial-scale=1',
|
||||
modules: ['@nuxt/eslint', '@nuxtjs/tailwindcss', '@pinia/nuxt'],
|
||||
vue: {
|
||||
compilerOptions: {
|
||||
isCustomElement: (tag) => ['atropos-component'].includes(tag)
|
||||
}
|
||||
},
|
||||
|
||||
compatibilityDate: '2024-07-10',
|
||||
modules: ['@pinia/nuxt']
|
||||
}
|
||||
})
|
8186
package-lock.json
generated
25
package.json
@@ -1,27 +1,28 @@
|
||||
{
|
||||
"name": "nuxt-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev --host",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pinia/nuxt": "^0.5.5",
|
||||
"@nuxt/eslint": "1.9.0",
|
||||
"@nuxtjs/tailwindcss": "6.14.0",
|
||||
"atropos": "^2.0.2",
|
||||
"nuxt": "^3.12.3",
|
||||
"pinia": "^2.2.4",
|
||||
"unhead": "^1.9.15",
|
||||
"vue": "^3.4.31",
|
||||
"vue-router": "^4.4.0"
|
||||
"eslint": "^9.33.0",
|
||||
"nuxt": "^4.0.3",
|
||||
"vue": "^3.5.18",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": ">=10 <11"
|
||||
},
|
||||
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748",
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.19",
|
||||
"postcss": "^8.4.39",
|
||||
"sass": "^1.77.6",
|
||||
"tailwindcss": "^3.4.4"
|
||||
"@pinia/nuxt": "^0.11.2"
|
||||
}
|
||||
}
|
||||
|
137
pages/about.vue
@@ -1,137 +0,0 @@
|
||||
<template>
|
||||
<div class="text-white p-12 text-lg flex flex-col items-center">
|
||||
<a href="/" class="mb-12">
|
||||
<img class="logo" src="/logo.svg">
|
||||
</a>
|
||||
<div class="max-w-4xl">
|
||||
<h1 class="text-esyellow text-6xl mb-8">About evilSpins ...</h1>
|
||||
<pre>
|
||||
Rather than explaining the "artistic" approach of evilspins in too formal a manner, I prefer to freely share the notes and sketches as a testament to the project's long genesis (2019 -> 2024) :
|
||||
</pre>
|
||||
<pre>
|
||||
"The idea of the name comes from Anton Newcombe, who defines music as something that must remain independent,
|
||||
hence his quote "keep music evil", from which 'evil' and 'spins' refer to music as an object that turns (cassette,
|
||||
vinyl, CD...), music is a living object in perpetual motion and it's 'evil' / independent / unmanageable because
|
||||
it makes you dance, it takes the listener without even realizing it.
|
||||
</pre>
|
||||
<img src="https://files.erudi.fr/evilspins/ABOUT-1.jpg">
|
||||
<pre>
|
||||
Each compilation is the original soundtrack of a non-existent film. It must leave room for imagination and
|
||||
reflection.
|
||||
A compilation tries to produce the effect of a film, to provoke imagination.
|
||||
It's an opportunity to decontextualize forgotten, outdated, or unconsciously caricatured musical genres
|
||||
(bluegrass,
|
||||
punk, folk, hip-hop...) or simply to discover new genres.
|
||||
Approach music without thinking about the stylistic references of the piece, like in a film.
|
||||
</pre>
|
||||
<img src="https://files.erudi.fr/evilspins/ABOUT-2.jpg">
|
||||
<pre>
|
||||
A compilation can therefore offer several readings,
|
||||
- a panel of characters,
|
||||
- several versions (short, long like a director's cut, side A/B (side B could be a parallel version with the same
|
||||
artists in the same order but with different titles very close))
|
||||
- several possible reading modes (mode 1: only the mixed sound and artworks (only play/pause is available),
|
||||
mode 2: portraits and names of the protagonists, anecdotes, quotes, and web links (possibility to go to the
|
||||
next/previous track and to 'seek'))
|
||||
- an artwork (square format (vinyl) or 16/9 + black band (cinema)) - vocal samples to make characters speak?
|
||||
(samples of artists in interviews, for example)
|
||||
- a mix must follow this order:
|
||||
List of quintessence sound :
|
||||
1. accessible but instrumental
|
||||
2. more researched and instrumental
|
||||
3. more rhythmic instrumental
|
||||
4->7. rhythmic sung
|
||||
8. rhythmic instrumental
|
||||
9. Instrumental
|
||||
10. bluegrass final Evils spins from the idea that music needs the imagination of its listener.
|
||||
11. hidden track
|
||||
</pre>
|
||||
<img src="https://files.erudi.fr/evilspins/ABOUT-3.jpg">
|
||||
<pre>
|
||||
Design: timeline of the player like the itinerary of a journey on a map OR like a display board at a train
|
||||
station/airport "A problem created cannot be solved by thinking in the same way it was created." "God only depends
|
||||
on the imagination of men" Drawing situations where you meet someone you think you know well but who doesn't have
|
||||
the same vision of things as you. Example: discussion about music and completely divergent points of view, (Nick
|
||||
Waterhouse = completely inaccessible jazz) Going to see local labels inventing a religion and therefore a god, is
|
||||
only a way to personify one's conscience. The projects converge on the concept of a "cyber residence of artists"
|
||||
drawing a face to one's morality. Dreams are nightmares, nightmares are dreams. Starting from reflections on the
|
||||
use
|
||||
of new technologies; questioning one's use of the internet on one's computer, phone, etc., allows one to wonder
|
||||
what? when? why do I need the internet, to watch a movie, to listen to music... In my case, the answers to these
|
||||
questions propose a use that mixes tools and methods from different eras. TOOLS & METHODS FROM DIFFERENT
|
||||
ERAS/CONTEXTS Just like in the film 'La fille du 14 juillet', it can be very interesting to play on the contrasts
|
||||
between different eras by mixing them in the same visual and sound realization. Idea of realization graphic novel
|
||||
or
|
||||
comic book: Observation: with the revolution of the internet and new technologies, cinema has become a major art
|
||||
that everyone masters more or less. The public's gaze is sharpened and everyone knows how to apprehend
|
||||
cinematographic realization. The plans, camera movements, sound effects are a language that everyone knows how to
|
||||
read (cinema is carried by all through downloading & streaming) ... and even write (I can make a film with my
|
||||
phone,
|
||||
reflex cameras have become affordable). How to draw cinema? Try to take key scenes from cinema, draw/paint them
|
||||
and
|
||||
animate them. fixed plan? traveling? zoom? panoramic? plunge? counter-plunge? wide-angle? long focal? work on the
|
||||
Wikipedia of 'film noir' re-watch film noir link with the polar noir? film noir has a pretentious approach at
|
||||
first
|
||||
glance. so we need to find a technique (visual? sound?) to disarm this bad image. to do this we need to start by
|
||||
isolating and understanding the possible pretentious aspect of film noir.
|
||||
</pre>
|
||||
<img src="https://files.erudi.fr/evilspins/ABOUT-4.jpg">
|
||||
<pre>
|
||||
The solitude of the character? The
|
||||
voice-overs that narrate the thoughts? The appearance of the protagonist? The cliché (visual & script) of the
|
||||
polar
|
||||
noir too worn out? (the detective alone in his thoughts, in his apartment smoking a cigarette on the balcony) the
|
||||
cigarette can still be a more powerful visual element than a cliché transition with animation of the cigarette?
|
||||
cigarette = huge filmographic symbol == we find it in most films especially in the era of film noir == commercial
|
||||
influence tool == influence of American culture (cowboy), == symbol of the ephemeral, like life, like the
|
||||
characters
|
||||
of a film, like a film. the 2 mega clichés: Nicholson/Polansky in Chinatown & Ridley Scott in Blade Runner How to
|
||||
integrate the cigarette? = 3D animation? (often ugly, technically very difficult, aesthetically difficult) =
|
||||
hand-drawn animation? (long... very long to do but good pretext to train) = vector-drawn animation? (technically
|
||||
moderately difficult but aesthetically difficult) = film excerpts? (technically easy & aesthetically very easy BUT
|
||||
not really creation (montage), and illegal... to check) = animation of the spin (film zoom in Cassette, CD, Vinyl,
|
||||
the plates of a hard drive (joke -> why not make a montage effect), find an equivalent for digital (server bays,
|
||||
server fan? (joke))) why not call the box to meet at Slift to turn it properly and start a collaboration?
|
||||
otherwise,
|
||||
a lot of material to Bellecours photo + ... Fnac? no, much more interesting to go through a collaboration. Take
|
||||
out
|
||||
the rushes of films.
|
||||
</pre>
|
||||
<img src="https://files.erudi.fr/evilspins/ABOUT-5.jpg">
|
||||
<pre>
|
||||
|
||||
The thinking of Jack Nicholson in Chinatown No visual drawing during the reading of the piece,
|
||||
only why not the log / cover / artwork of the album the reading should not look like a PowerPoint, the
|
||||
counter-example is everywhere on YouTube. On the other hand, a drawn and animated artwork in parallax why not. See
|
||||
3D js cover -> at the mouse movement: preview with animation -> At the click fade of the box zoom on the cover the
|
||||
animation is no longer manual but automatic (linear movement? random?) after the cigarette... the smoke... too
|
||||
cliché? possible but the smoke can make a transition between 2 listens / animations. the listening ends the smoke
|
||||
arrives brings us to the cigarette which brings us to a protagonist who brings us to a film excerpt. why
|
||||
evilspins?
|
||||
The spin obviously refers to the movement of music both by its format (cassette, vinyl, CD) and by its effect
|
||||
(membrane, air movement, dance) possible sound effect: end of the piece Purple Mercy - Purple -> the end of the
|
||||
piece ends with an abrupt snare with a subtle reverb -> effect felt: conclusion on an impression of grand space ->
|
||||
effect analyzed / symbolic: grandeur of the proposal but above all empty room -> no public -> therefore call to
|
||||
the
|
||||
public -> idea expressible: maybe too pretentious again. film noir has a pretentious image at first glance. we
|
||||
need
|
||||
to find a technique (visual? sound?) to disarm this bad image. to do this we need to start by isolating and
|
||||
understanding the possible pretentious aspect of film noir. The solitude of the character? The voice-overs that
|
||||
narrate the thoughts? The appearance of the protagonist? The cliché (visual & script) of the polar noir too worn
|
||||
out? (the detective alone in his thoughts, in his apartment smoking a cigarette on the balcony)"
|
||||
</pre>
|
||||
<img src="https://files.erudi.fr/evilspins/ABOUT-6.jpg">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
img {
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-line;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
</style>
|
@@ -1,154 +0,0 @@
|
||||
<template @keydown.esc="closePlayer()">
|
||||
<div class="text-white w-full flex items-center flex-col">
|
||||
<button class="text-sm md:text-5xl leading-none button button--close m-3 flex justify-center items-center z-50"
|
||||
@click="closePlayer()">
|
||||
</button>
|
||||
<video class="mixPlayer w-full" controls ref="mixPlayer">
|
||||
<source :src="videoSD" type="video/mp4">
|
||||
</video>
|
||||
<nav class="text-esyellow w-full flex" v-if="currentTrack">
|
||||
<button v-for="(track, index) in tracks" @click="listenTo(track.start)" :index="track.id"
|
||||
class="border-l-wihte-400 border-l-2 p-2 flex-grow hover:bg-esyellow hover:text-black"
|
||||
:class="{ 'border-l-0': index === 0, 'bg-esyellow text-black': track.id === currentTrack.id }">
|
||||
<span class="block">
|
||||
{{ index + 1 }}
|
||||
</span>
|
||||
<span class="hidden 2xl:block">
|
||||
{{ track.title }}
|
||||
</span>
|
||||
<span class="hidden lg:block">
|
||||
{{ getArtistName(track.artist) }}
|
||||
</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<article class="text-white p-8 max-w-5xl" v-if="currentTrack">
|
||||
<div class="flex flex-col sm:flex-row items-center ">
|
||||
<a :href="currentTrack.url" target="_blank" class="mr-4">
|
||||
<atropos-component>
|
||||
<img class="flex-grow-0" :src="'https://f4.bcbits.com/img/' + currentTrack.cover + '_8.jpg'" />
|
||||
</atropos-component>
|
||||
</a>
|
||||
<div>
|
||||
<a :href="currentTrack.url" target="_blank" rel="noopener noreferrer">
|
||||
<h3 class="text-5xl">
|
||||
{{ currentTrack.title }}
|
||||
</h3>
|
||||
</a>
|
||||
<a v-if="currentArtist" :href="currentArtist.url" target="_blank" rel="noopener noreferrer">
|
||||
<h2 class="font-bold text-6xl text-esyellow">
|
||||
{{ currentArtist.name }}
|
||||
</h2>
|
||||
</a>
|
||||
<h4 class="text-xl text-slate-200">
|
||||
{{ compilation.name }}
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="block mt-10">
|
||||
see artist page:<br>
|
||||
<a v-if="currentArtist" target="_blank" class="underline text-orange-500 hover:text-orange-400"
|
||||
:href="currentArtist.url">
|
||||
{{ currentArtist.name }}
|
||||
</a><br>
|
||||
purchase the track:<br>
|
||||
<a target="_blank" class="underline text-orange-500 hover:text-orange-400" :href="currentTrack.url">
|
||||
{{ currentTrack.title }}
|
||||
</a><br>
|
||||
<br>
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const route = useRoute()
|
||||
const store = useDataStore()
|
||||
|
||||
const compilation = ref()
|
||||
const tracks = ref()
|
||||
const mixPlayer = ref()
|
||||
const videoSD = ref()
|
||||
const currentTrack = ref()
|
||||
const { isLoaded } = storeToRefs(store)
|
||||
const currentArtist = computed(() => {
|
||||
return store.getArtistById(currentTrack.value.artist)
|
||||
})
|
||||
const getArtistName = (id: number) => {
|
||||
return store.getArtistById(id)?.name
|
||||
}
|
||||
|
||||
// LOAD DATAs
|
||||
|
||||
onMounted(() => {
|
||||
loadCompilation() // if user arrive directly on compilation page
|
||||
})
|
||||
watch(isLoaded, () => {
|
||||
loadCompilation() // if the user came from another page
|
||||
})
|
||||
|
||||
const watchPlayingTrack = () => {
|
||||
setInterval(() => {
|
||||
if (mixPlayer.value && compilation.value.id) {
|
||||
currentTrack.value = tracks.value.find((track: { start: number; }, index: number) => {
|
||||
const nextTrackStart = tracks.value[index + 1]?.start ?? Infinity
|
||||
return track.start <= mixPlayer.value.currentTime && mixPlayer.value.currentTime < nextTrackStart
|
||||
})
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const loadCompilation = () => {
|
||||
if (isLoaded.value) {
|
||||
compilation.value = store.getCompilationById(route.params.id as string)
|
||||
tracks.value = store.getTracksByCompilationId(route.params.id as string)
|
||||
videoSD.value = 'https://files.erudi.fr/evilspins/' + compilation.value.id + '-SD.mp4'
|
||||
mixPlayer.value.load()
|
||||
mixPlayer.value.play()
|
||||
mixPlayer.value.focus()
|
||||
watchPlayingTrack()
|
||||
}
|
||||
}
|
||||
|
||||
const listenTo = (start: number) => {
|
||||
mixPlayer.value.currentTime = start
|
||||
mixPlayer.value.play()
|
||||
}
|
||||
|
||||
const closePlayer = async () => {
|
||||
await navigateTo('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
filter: drop-shadow(8px 8px 0 rgb(0 0 0 / 0.8));
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.mixPlayer {
|
||||
background: black;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
nav>button:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.button--close {
|
||||
position: fixed;
|
||||
right: 2vw;
|
||||
|
||||
&:after {
|
||||
content: "\00d7";
|
||||
}
|
||||
}
|
||||
</style>
|
127
pages/index.vue
@@ -1,127 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="w-full flex justify-center">
|
||||
<nav class="[&>*]:p-2 text-white bottom-0 right-0 fixed flex justify-center z-50">
|
||||
<a href="https://www.youtube.com/channel/UCATtFHnOLDCv8qroi2KW3ZA" target="about:blank" class="mt-1">
|
||||
<img src="/youtube.svg" alt="youtube channel" />
|
||||
</a>
|
||||
<a href="mailto:contact@evilspins.com">📬</a>
|
||||
<a href="/about">❓</a>
|
||||
</nav>
|
||||
</div>
|
||||
<section class="splash-screen flex items-center flex-col">
|
||||
<figure class="ui">
|
||||
<img class="logo" src="/logo.svg">
|
||||
<h1 class="text-white pt-6 text-sm md:text-md lg:text-lg text-center font-bold tracking-widest">Compilations
|
||||
Indépendantes
|
||||
</h1>
|
||||
<button class="button button--screened relative top-16 flex justify-center items-center" @click="scrollDown()">
|
||||
↓
|
||||
</button>
|
||||
</figure>
|
||||
<div class="shadow screen" />
|
||||
<video class="animation screen" loop autoplay muted ref="animation">
|
||||
<source src="https://files.erudi.fr/evilspins/sloughi-run-loop-big.webm" type="video/webm">
|
||||
<source src="https://files.erudi.fr/evilspins/sloughi-run-loop-small.webm" type="video/webm"
|
||||
media="all and (max-width: 640px)">
|
||||
</video>
|
||||
</section>
|
||||
<section class="flex justify-center">
|
||||
<div class="flex max-w-2xl">
|
||||
<compilationsList />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
// SEO
|
||||
useSeoMeta({
|
||||
title: 'evilSpins - compilations indépendantes',
|
||||
ogTitle: 'evilSpins - compilations indépendantes',
|
||||
description: 'evilSpins - compilations indépendantes, la bande originale d\'un film qui n\'existe pas',
|
||||
ogDescription: 'evilSpins - compilations indépendantes, la bande originale d\'un film qui n\'existe pas',
|
||||
ogImage: 'https://evilspins.com/logo.svg'
|
||||
})
|
||||
|
||||
const dataStore = useDataStore()
|
||||
|
||||
const scrollDown = function () {
|
||||
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.logo,
|
||||
.button,
|
||||
.shadow,
|
||||
.animation,
|
||||
.mix {
|
||||
transition: .7s opacity;
|
||||
}
|
||||
|
||||
.screen {
|
||||
position: absolute;
|
||||
height: 100vh;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.splash-screen {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.animation {
|
||||
z-index: 1;
|
||||
object-fit: cover;
|
||||
opacity: .8;
|
||||
/* opacity: 0; */
|
||||
}
|
||||
|
||||
.mix {
|
||||
z-index: 4;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
z-index: 3;
|
||||
box-shadow: rgb(0, 0, 0) 0px 0px 170px 70px inset;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
.ui {
|
||||
z-index: 4;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
max-width: 80%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
filter: drop-shadow(8px 8px 0 rgb(0 0 0 / 0.8));
|
||||
}
|
||||
|
||||
.mixPlayer {
|
||||
background: black;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.hide {
|
||||
opacity: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.show {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div class="h-screen w-full flex justify-center p-16">
|
||||
<img src="/logo.svg">
|
||||
</div>
|
||||
</template>
|
10234
pnpm-lock.yaml
generated
Normal file
BIN
public/ES01A/bkg.jpg
Normal file
After Width: | Height: | Size: 149 KiB |
BIN
public/ES01A/name.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
50
public/ES01A/number1.svg
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
public/ES01A/object.png
Normal file
After Width: | Height: | Size: 4.4 MiB |
BIN
public/ES01B/B.jpg
Normal file
After Width: | Height: | Size: 2.8 MiB |
BIN
public/ES01B/bkg.jpg
Normal file
After Width: | Height: | Size: 149 KiB |
BIN
public/ES01B/name.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
22
public/ES01B/name.svg
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
public/ES01B/object.png
Executable file
After Width: | Height: | Size: 4.5 MiB |
BIN
public/ESPLAYLISTS/bkg.jpg
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
public/ESPLAYLISTS/name.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
public/ESPLAYLISTS/object.png
Normal file
After Width: | Height: | Size: 84 KiB |
163
public/logo.svg
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@@ -1,38 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
fill="#000000"
|
||||
height="800px"
|
||||
width="800px"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
id="Capa_1"
|
||||
viewBox="0 0 60 60"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="play.svg"
|
||||
width="25.177818"
|
||||
height="31.875"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
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="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="1.9448516"
|
||||
inkscape:cx="202.07197"
|
||||
inkscape:cy="136.00009"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
style="font-size:64px;line-height:0.6;font-family:'Noto Sans Rejang';-inkscape-font-specification:'Noto Sans Rejang';letter-spacing:0.03px;word-spacing:0.16px;stroke-width:5.38174;stroke-miterlimit:2.3;stroke-dasharray:1.07635, 5.91989"
|
||||
d="M 0,31.875 V 0 l 25.177818,15.9375 z"
|
||||
id="text1"
|
||||
aria-label="▸" />
|
||||
</svg>
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs2">
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
id="namedview2"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:zoom="0.94997202"
|
||||
inkscape:cx="340.01001"
|
||||
inkscape:cy="397.38012"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1132"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="path4" />
|
||||
|
||||
<g
|
||||
id="g4"
|
||||
transform="translate(9.7969913,-22.06049)"><g
|
||||
id="path4"
|
||||
inkscape:transform-center-x="-3.1076416"
|
||||
inkscape:transform-center-y="0.031482236"
|
||||
style="opacity:1"
|
||||
transform="matrix(0.16696126,-0.60372499,0.52316491,0.19267096,25.039308,35.922386)"><path
|
||||
id="path1"
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:2.3;paint-order:fill markers stroke;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||
d="m -9.5847112,-19.414681 a 2.4868138,2.4868138 0 0 0 -3.1047968,-1.516874 l -18.788944,6.087811 -18.791084,6.0858648 a 2.4868138,2.4868138 0 0 0 -0.900333,4.2129952 L -36.501957,8.6836742 -21.835903,21.91414 a 2.4868138,2.4868138 0 0 0 4.097569,-1.326305 l 4.124976,-19.3182854 4.1231169,-19.3163776 a 2.4868138,2.4868138 0 0 0 -0.09447,-1.367853 z m -0.7440438,70.809953 a 54.628808,47.339229 72.300195 0 1 -61.707096,-37.65028 54.628808,47.339229 72.300195 0 1 28.489522,-66.435356 54.628808,47.339229 72.300195 0 1 61.707096,37.650281 54.628808,47.339229 72.300195 0 1 -28.489522,66.435355 z" /></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.2 KiB |
2
public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-Agent: *
|
||||
Disallow:
|
@@ -1,81 +0,0 @@
|
||||
export default eventHandler(() => {
|
||||
return [
|
||||
{
|
||||
id: 0,
|
||||
name: "L'efondras",
|
||||
url: "https://leffondras.bandcamp.com/music",
|
||||
style: [0, 1, 2],
|
||||
cover: "0024705317"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: "The kundalini genie",
|
||||
url: "https://the-kundalini-genie.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "0012045550"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Fontaines D.C.",
|
||||
url: "https://fontainesdc.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "0027327090"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Fontanarosa",
|
||||
url: "https://fontanarosa.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "0035380235",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Johnny mafia",
|
||||
url: "https://johnnymafia.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "0035009392",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "New candys",
|
||||
url: "https://newcandys.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "0033518637",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Magic shoppe",
|
||||
url: "https://magicshoppe.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "0030748374"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "Les jaguars",
|
||||
url: "https://radiomartiko.bandcamp.com/album/surf-qu-b-cois",
|
||||
style: [0, 1, 2],
|
||||
cover: "0016551336",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "TRAAMS",
|
||||
url: "https://traams.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "0028348410",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "Blue orchid",
|
||||
url: "https://blue-orchid.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "0034796193",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "I love UFO",
|
||||
url: "https://bruitblanc.bandcamp.com",
|
||||
style: [0, 1, 2],
|
||||
cover: "a2203158939",
|
||||
}
|
||||
]
|
||||
})
|
@@ -1,16 +0,0 @@
|
||||
export default eventHandler(() => {
|
||||
return [
|
||||
{
|
||||
id: 'ES00A',
|
||||
name: 'zero',
|
||||
duration: 2794,
|
||||
description: '...',
|
||||
},
|
||||
{
|
||||
id: 'ES00B',
|
||||
name: 'zero b-sides',
|
||||
duration: 2470,
|
||||
description: '...',
|
||||
}
|
||||
]
|
||||
})
|
@@ -1,21 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export default eventHandler(async (event) => {
|
||||
const directoryPath = path.join(process.cwd(), 'media/files/music') // replace 'your-folder' with the folder you want to list
|
||||
|
||||
try {
|
||||
// Read the directory contents
|
||||
const files = await fs.promises.readdir(directoryPath)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
files: files.filter(file => !file.startsWith('.')) // optional: exclude unwanted files
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
}
|
||||
}
|
||||
})
|
@@ -1,16 +0,0 @@
|
||||
export default eventHandler(() => {
|
||||
return [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "post-rock"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "math-rock"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "indie-pop"
|
||||
}
|
||||
]
|
||||
})
|
@@ -1,224 +0,0 @@
|
||||
export default eventHandler(() => {
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
compilation: 'ES00A',
|
||||
title: 'The grinding wheel',
|
||||
artist: 0,
|
||||
start: 0,
|
||||
bpm: 0,
|
||||
url: 'https://arakirecords.bandcamp.com/track/the-grinding-wheel',
|
||||
cover: 'a3236746052',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
compilation: 'ES00A',
|
||||
title: 'Bleach',
|
||||
artist: 1,
|
||||
start: 393,
|
||||
bpm: 0,
|
||||
url: 'https://the-kundalini-genie.bandcamp.com/track/bleach-2',
|
||||
cover: 'a1714786533',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
compilation: 'ES00A',
|
||||
title: 'Televised mind',
|
||||
artist: 2,
|
||||
start: 892,
|
||||
bpm: 0,
|
||||
url: 'https://fontainesdc.bandcamp.com/track/televised-mind',
|
||||
cover: 'a3772806156'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
compilation: 'ES00A',
|
||||
title: 'In it',
|
||||
artist: 3,
|
||||
start: 1138,
|
||||
bpm: 0,
|
||||
url: 'https://howlinbananarecords.bandcamp.com/track/in-it',
|
||||
cover: 'a1720372066',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
compilation: 'ES00A',
|
||||
title: 'Bad michel',
|
||||
artist: 4,
|
||||
start: 1245,
|
||||
bpm: 0,
|
||||
url: 'https://johnnymafia.bandcamp.com/track/bad-michel-3',
|
||||
cover: 'a0984622869',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
compilation: 'ES00A',
|
||||
title: 'Overall',
|
||||
artist: 5,
|
||||
start: 1394,
|
||||
bpm: 0,
|
||||
url: 'https://newcandys.bandcamp.com/track/overall',
|
||||
cover: 'a0559661270',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
compilation: 'ES00A',
|
||||
title: 'Blowup',
|
||||
artist: 6,
|
||||
start: 1674,
|
||||
bpm: 0,
|
||||
url: 'https://magicshoppe.bandcamp.com/track/blowup',
|
||||
cover: 'a1444895293',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
compilation: 'ES00A',
|
||||
title: 'Guitar jet',
|
||||
artist: 7,
|
||||
start: 1880,
|
||||
bpm: 0,
|
||||
url: 'https://radiomartiko.bandcamp.com/track/guitare-jet',
|
||||
cover: 'a1494681687',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
compilation: 'ES00A',
|
||||
title: 'Intercontinental radio waves',
|
||||
artist: 8,
|
||||
start: 2024,
|
||||
bpm: 0,
|
||||
url: 'https://traams.bandcamp.com/track/intercontinental-radio-waves',
|
||||
cover: 'a0046738552',
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
compilation: 'ES00A',
|
||||
title: 'Here comes the sun',
|
||||
artist: 9,
|
||||
start: 2211,
|
||||
bpm: 0,
|
||||
url: 'https://blue-orchid.bandcamp.com/track/here-come-the-sun',
|
||||
cover: 'a4102567047',
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
compilation: 'ES00A',
|
||||
title: 'Like in the movies',
|
||||
artist: 10,
|
||||
start: 2559,
|
||||
bpm: 0,
|
||||
url: 'https://bruitblanc.bandcamp.com/track/like-in-the-movies-2',
|
||||
cover: 'a2203158939',
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
compilation: 'ES00B',
|
||||
title: 'Ce que révèle l\'éclipse',
|
||||
artist: 0,
|
||||
start: 0,
|
||||
bpm: 0,
|
||||
url: 'https://arakirecords.bandcamp.com/track/ce-que-r-v-le-l-clipse',
|
||||
cover: 'a3236746052',
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
compilation: 'ES00B',
|
||||
title: 'Bleedin\' Gums Mushrool',
|
||||
artist: 1,
|
||||
start: 263,
|
||||
bpm: 0,
|
||||
url: 'https://the-kundalini-genie.bandcamp.com/track/bleedin-gums-mushroom',
|
||||
cover: 'a1714786533',
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
compilation: 'ES00B',
|
||||
title: 'A lucid dream',
|
||||
artist: 2,
|
||||
start: 554,
|
||||
bpm: 0,
|
||||
url: 'https://fontainesdc.bandcamp.com/track/a-lucid-dream',
|
||||
cover: 'a3772806156',
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
compilation: 'ES00B',
|
||||
title: 'Lights off',
|
||||
artist: 3,
|
||||
start: 781,
|
||||
bpm: 0,
|
||||
url: 'https://howlinbananarecords.bandcamp.com/track/lights-off',
|
||||
cover: 'a1720372066',
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
compilation: 'ES00B',
|
||||
title: 'I\'m sentimental',
|
||||
artist: 4,
|
||||
start: 969,
|
||||
bpm: 0,
|
||||
url: 'https://johnnymafia.bandcamp.com/track/im-sentimental-2',
|
||||
cover: 'a2333676849',
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
compilation: 'ES00B',
|
||||
title: 'Thrill or trip',
|
||||
artist: 5,
|
||||
start: 1128,
|
||||
bpm: 0,
|
||||
url: 'https://newcandys.bandcamp.com/track/thrill-or-trip',
|
||||
cover: 'a0559661270',
|
||||
},
|
||||
{
|
||||
id: 27,
|
||||
compilation: 'ES00B',
|
||||
title: 'Redhead',
|
||||
artist: 6,
|
||||
start: 1303,
|
||||
bpm: 0,
|
||||
url: 'https://magicshoppe.bandcamp.com/track/redhead',
|
||||
cover: 'a0594426943',
|
||||
},
|
||||
{
|
||||
id: 28,
|
||||
compilation: 'ES00B',
|
||||
title: 'Supersonic twist',
|
||||
artist: 7,
|
||||
start: 1584,
|
||||
bpm: 0,
|
||||
url: 'https://open.spotify.com/track/66voQIZAJ3zD3Eju2qtNjF',
|
||||
cover: 'a1494681687',
|
||||
},
|
||||
{
|
||||
id: 29,
|
||||
compilation: 'ES00B',
|
||||
title: 'Flowers',
|
||||
artist: 8,
|
||||
start: 1749,
|
||||
bpm: 0,
|
||||
url: 'https://traams.bandcamp.com/track/flowers',
|
||||
cover: 'a3644668199',
|
||||
},
|
||||
{
|
||||
id: 30,
|
||||
compilation: 'ES00B',
|
||||
title: 'The shade',
|
||||
artist: 9,
|
||||
start: 1924,
|
||||
bpm: 0,
|
||||
url: 'https://blue-orchid.bandcamp.com/track/the-shade',
|
||||
cover: 'a0804204790',
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
compilation: 'ES00B',
|
||||
title: 'Like in the movies',
|
||||
artist: 10,
|
||||
start: 2185,
|
||||
bpm: 0,
|
||||
url: 'https://bruitblanc.bandcamp.com/track/like-in-the-movies',
|
||||
cover: 'a3647322740',
|
||||
},
|
||||
]
|
||||
})
|
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "../.nuxt/tsconfig.server.json"
|
||||
}
|
@@ -1,51 +0,0 @@
|
||||
import type { Compilation, Artist, Track } from '~/types/types'
|
||||
|
||||
// stores/data.ts
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useDataStore = defineStore('data', {
|
||||
state: () => ({
|
||||
compilations: [] as Compilation[], // Store your compilation data here
|
||||
artists: [] as Artist[], // Store artist data here
|
||||
tracks: [] as Track[], // Store track data here
|
||||
isLoaded: false, // To track if data is already loaded
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async loadData() {
|
||||
if (this.isLoaded) return // Avoid re-fetching if already loaded
|
||||
|
||||
// Fetch your data once (e.g., from an API or local JSON)
|
||||
const { data: compilations } = await useFetch('/api/compilations')
|
||||
const { data: artists } = await useFetch('/api/artists')
|
||||
const { data: tracks } = await useFetch('/api/tracks')
|
||||
|
||||
// Set the data in the store
|
||||
this.compilations = compilations.value
|
||||
this.artists = artists.value
|
||||
this.tracks = tracks.value
|
||||
this.isLoaded = true
|
||||
}
|
||||
},
|
||||
|
||||
getters: {
|
||||
// Obtenir tous les compilations
|
||||
getAllCompilations: (state) => state.compilations,
|
||||
getCompilationById: (state) => {
|
||||
return (id: string) => {
|
||||
return state.compilations.find(compilation => compilation.id === id)
|
||||
}
|
||||
},
|
||||
// Obtenir toutes les pistes d'une compilation donnée
|
||||
getTracksByCompilationId: (state) => (compilationId: string) => {
|
||||
return state.tracks.filter(track => track.compilation === compilationId)
|
||||
},
|
||||
// Filtrer les artistes selon certains critères
|
||||
getArtistById: (state) => (id: number) => state.artists.find(artist => artist.id === id),
|
||||
|
||||
// Obtenir toutes les pistes d'un artiste donné
|
||||
getTracksByArtistId: (state) => (artistId: string) => {
|
||||
return state.tracks.filter(track => track.artistId === artistId)
|
||||
},
|
||||
},
|
||||
})
|
@@ -11,12 +11,12 @@ module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
esyellow: '#fdec50ff',
|
||||
esyellow: "#fdec50ff",
|
||||
},
|
||||
screens: {
|
||||
'2sm': '320px',
|
||||
"2sm": "320px",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
};
|
||||
|
@@ -1,4 +1,18 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.server.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.shared.json"
|
||||
},
|
||||
{
|
||||
"path": "./.nuxt/tsconfig.node.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -1,27 +0,0 @@
|
||||
// types.ts
|
||||
export interface Compilation {
|
||||
id: string
|
||||
name: string
|
||||
duration: number
|
||||
tracks?: Track[]
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface Artist {
|
||||
id: number
|
||||
name: string
|
||||
url: string
|
||||
style: Array<number>
|
||||
cover: string
|
||||
}
|
||||
|
||||
export interface Track {
|
||||
id: string
|
||||
compilationId: string
|
||||
title: string
|
||||
artistId: number
|
||||
artist?: Artist
|
||||
start: number
|
||||
link: string
|
||||
cover: string
|
||||
}
|