FEAT: evilspins v3 :: tracks info in player
This commit is contained in:
		| @@ -133,6 +133,5 @@ img { | ||||
| pre { | ||||
|   white-space: pre-line; | ||||
|   font-family: sans-serif; | ||||
|   @apply text-xl; | ||||
| } | ||||
| </style> | ||||
| </style> | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| <template> | ||||
|   <section> | ||||
|     <zero-a></zero-a> | ||||
|     <zero-b></zero-b> | ||||
|   </section> | ||||
| </template> | ||||
							
								
								
									
										152
									
								
								pages/compilations/[id].vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								pages/compilations/[id].vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| <template @keydown.esc="closePlayer()"> | ||||
|   <div class="bg-black 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"> | ||||
|       <button v-for="(track, index) in tracks" @click="listenTo(track.start)" | ||||
|         class="border-l-wihte-400 border-l-2 flex-grow hover:bg-esyellow hover:text-black" | ||||
|         :class="{ 'border-l-0': index === 0 }"> | ||||
|         <span class="block"> | ||||
|           {{ index + 1 }} | ||||
|         </span> | ||||
|         <span class="hidden 2xl:block"> | ||||
|               {{ track.title }} | ||||
|             </span> | ||||
|             <span class="hidden lg:block"> | ||||
|               {{ store.getArtistById(track.artist).name }} | ||||
|             </span> | ||||
|       </button> | ||||
|     </nav> | ||||
|  | ||||
|     <article class="text-white p-8 max-w-5xl" v-if="currentTrack"> | ||||
|       <div class="flex items-center"> | ||||
|         <div class="mr-4"> | ||||
|           <img class="flex-grow-0" :src="'https://f4.bcbits.com/img/' + currentTrack.cover + '_8.jpg'" /> | ||||
|         </div> | ||||
|         <div> | ||||
|           <h3 class="text-5xl"> | ||||
|             {{ currentTrack.title }} | ||||
|           </h3> | ||||
|           <h2 class="font-bold text-6xl text-esyellow"> | ||||
|             {{ store.getArtistById(currentTrack.artist).name }} | ||||
|           </h2> | ||||
|           <h4 class="text-xl text-slate-200"> | ||||
|             {{ compilation.name }} | ||||
|           </h4> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <p class="block mt-10"> | ||||
|         see artist page:<br> | ||||
|         <a target="_blank" class="underline text-orange-500 hover:text-orange-400" :href="store.getArtistById(currentTrack.artist).url"> | ||||
|           {{ store.getArtistById(currentTrack.artist).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 currentArtist = computed(()=>{ | ||||
|   currentTrack | ||||
| }) | ||||
|  | ||||
| const { isLoaded } = storeToRefs(store) | ||||
|  | ||||
| onMounted(() => { | ||||
|   loadCompilation() | ||||
| }) | ||||
|  | ||||
| watch(isLoaded, () => { | ||||
|   loadCompilation() | ||||
| }) | ||||
|  | ||||
| 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) | ||||
|     tracks.value = store.getTracksByCompilationId(route.params.id) | ||||
|     videoSD.value = 'https://files.erudi.fr/evilspins/' + compilation.value.id + '-HD.mp4' | ||||
|     mixPlayer.value.load() | ||||
|     mixPlayer.value.play() | ||||
|     mixPlayer.value.focus() | ||||
|     watchPlayingTrack() | ||||
|   } | ||||
| } | ||||
|  | ||||
| const listenTo = (start) => { | ||||
|   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)); | ||||
| } | ||||
|  | ||||
| .mixPlayer { | ||||
|   background: black; | ||||
|   max-height: 70vh; | ||||
| } | ||||
|  | ||||
| nav > button:first-child { | ||||
|   border-left: none; | ||||
| } | ||||
|  | ||||
| .button--close { | ||||
|   position: fixed; | ||||
|   right: 2vw; | ||||
|   &:after { | ||||
|     content: "\00d7"; | ||||
|   } | ||||
| } | ||||
| .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; | ||||
|   background-color: #ffffff59; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										176
									
								
								pages/index.vue
									
									
									
									
									
								
							
							
						
						
									
										176
									
								
								pages/index.vue
									
									
									
									
									
								
							| @@ -3,7 +3,7 @@ | ||||
|     <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="ho no" /> | ||||
|           <img src="/youtube.svg" alt="youtube channel" /> | ||||
|         </a> | ||||
|         <a href="mailto:contact@evilspins.com">📬</a> | ||||
|         <a href="/about">❓</a> | ||||
| @@ -12,6 +12,9 @@ | ||||
|     <section class="splash-screen flex items-center flex-col" @keydown.esc="closePlayer()"> | ||||
|       <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> | ||||
| @@ -22,82 +25,10 @@ | ||||
|         <source src="https://files.erudi.fr/evilspins/sloughi-run-loop-small.webm" type="video/webm" | ||||
|           media="all and (max-width: 640px)"> | ||||
|       </video> | ||||
|       <div class="mix hide bg-black w-full screen overflow-scroll flex items-center flex-col"> | ||||
|         <button class="button button--close m-4 flex justify-center items-center z-50" @click="closePlayer()"> | ||||
|           <svg width="20px" height="30px"> | ||||
|             <line x1="0" y1="0" x2="20" y2="20" stroke="black" stroke-width="2" /> | ||||
|             <line x1="0" y1="20" x2="20" y2="0" stroke="black" stroke-width="2" /> | ||||
|           </svg> | ||||
|         </button> | ||||
|         <video class="mixPlayer w-full" controls ref="mixPlayer"> | ||||
|           <!-- <source :src="mixPlayerSourceHD" type="video/mp4"> --> | ||||
|           <source :src="mixPlayerSourceSD" type="video/mp4"> | ||||
|         </video> | ||||
|         <nav class="text-esyellow w-full flex"> | ||||
|           <button v-for="(track, index) in currentCompilationTracks" @click="listenTo(track.start)" | ||||
|             class="border-l-wihte-400 border-l-2 flex-grow hover:bg-esyellow hover:text-black" | ||||
|             :class="{ 'border-l-0': index === 0 }"> | ||||
|             <span class="block"> | ||||
|               {{ index + 1 }} | ||||
|             </span> | ||||
|             <!-- <span class="hidden lg:block"> | ||||
|               {{ track.title }} | ||||
|             </span> --> | ||||
|             <!-- {{ track.title }} {{ track.name }} --> | ||||
|           </button> | ||||
|         </nav> | ||||
|         <article class="text-white p-8 max-w-5xl"> | ||||
|           <div class="flex items-center"> | ||||
|             <div class="mr-4"> | ||||
|               <img class="flex-grow-0" :src="'https://f4.bcbits.com/img/' + currentCover + '_8.jpg'" /> | ||||
|             </div> | ||||
|             <div> | ||||
|               <h3 class="text-5xl"> | ||||
|                 {{ currentTitle }} | ||||
|               </h3> | ||||
|               <h2 class="font-bold text-6xl text-esyellow"> | ||||
|                 {{ currentArtist.name }} | ||||
|               </h2> | ||||
|               <h4 class="text-xl text-slate-200"> | ||||
|                 {{ currentCompilationName }} | ||||
|               </h4> | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
|           <p class="block mt-10"> | ||||
|             see artist page:<br> | ||||
|             <a target="_blank" class="underline text-orange-500 hover:text-orange-400" :href="currentLink"> | ||||
|               {{ currentArtist.url }} | ||||
|             </a><br> | ||||
|             purchace the track:<br> | ||||
|             <a target="_blank" class="underline text-orange-500 hover:text-orange-400" :href="currentLink"> | ||||
|               {{ currentLink }} | ||||
|             </a><br> | ||||
|             <br> | ||||
|           </p> | ||||
|         </article> | ||||
|         <section class=" bg-black"> | ||||
|           <zero-a @click="play('ES00A')" class="w-40"> | ||||
|             <button class="button absolute object-center p-4 flex justify-center items-center"> | ||||
|               <svg width="40px" height="30px"> | ||||
|                 <polygon points="0,0 0,30 30,15" /> | ||||
|               </svg> | ||||
|             </button> | ||||
|           </zero-a> | ||||
|           <zero-b @click="play('ES00B')" class="w-40"></zero-b> | ||||
|         </section> | ||||
|       </div> | ||||
|     </section> | ||||
|     <section class="flex justify-center"> | ||||
|       <div class="flex max-w-2xl"> | ||||
|         <zero-a @click="play('ES00A')"> | ||||
|           <button class="button absolute object-center p-4 flex justify-center items-center"> | ||||
|             <svg width="40px" height="30px"> | ||||
|               <polygon points="0,0 0,30 30,15" /> | ||||
|             </svg> | ||||
|           </button> | ||||
|         </zero-a> | ||||
|         <zero-b @click="play('ES00B')"></zero-b> | ||||
|         <compilationsList /> | ||||
|       </div> | ||||
|     </section> | ||||
|   </div> | ||||
| @@ -105,106 +36,21 @@ | ||||
| <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', | ||||
|   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' | ||||
| }) | ||||
|  | ||||
| interface Compilation { | ||||
|   id: string, | ||||
|   name: string, | ||||
|   duration: number, | ||||
| } | ||||
|  | ||||
| // animate player | ||||
| const mixPlayer = ref() | ||||
| const mixPlayerSourceHD = ref('') | ||||
| const mixPlayerSourceSD = ref('') | ||||
|  | ||||
| const currentCompilationId = ref('') | ||||
| const currentCompilationName = ref('') | ||||
| const currentCompilationTracks = ref({}) | ||||
| const currentArtist = ref('') | ||||
| const currentTitle = ref('') | ||||
| const currentLink = ref('') | ||||
| const currentCover = ref('') | ||||
|  | ||||
| const play = (id: string) => { | ||||
|   document.body.style.overflow = 'hidden' // Block scrolling | ||||
|   currentCompilationId.value = id | ||||
|   mixPlayerSourceHD.value = 'https://files.erudi.fr/evilspins/' + currentCompilationId.value + '-HD.mp4' | ||||
|   mixPlayerSourceSD.value = 'https://files.erudi.fr/evilspins/' + currentCompilationId.value + '-SD.mp4' | ||||
|  | ||||
|   fadeOut(document.querySelector('.button')) | ||||
|   fadeOut(document.querySelector('.logo')) | ||||
|   fadeOut(document.querySelector('.animation')) | ||||
|   fadeIn(document.querySelector('.mix')) | ||||
|   fadeOut(document.querySelector('.shadow')) | ||||
|   mixPlayer.value.load() | ||||
|   mixPlayer.value.play() | ||||
|   mixPlayer.value.focus() | ||||
| } | ||||
|  | ||||
| const closePlayer = () => { | ||||
|   fadeIn(document.querySelector('.animation')) | ||||
|   fadeIn(document.querySelector('.button')) | ||||
|   fadeIn(document.querySelector('.logo')) | ||||
|   fadeOut(document.querySelector('.mix')) | ||||
|   fadeIn(document.querySelector('.shadow')) | ||||
|   mixPlayer.value.pause() | ||||
|   document.body.style.overflow = '' // Reset scroll when closing | ||||
| } | ||||
|  | ||||
| const listenTo = (timeToPlay: number) => { | ||||
|   mixPlayer.value.currentTime = timeToPlay | ||||
|   mixPlayer.value.play() | ||||
| } | ||||
|  | ||||
| const fadeOut = (elt: HTMLElement) => { | ||||
|   elt.classList.add('hide') | ||||
| } | ||||
| const fadeIn = (elt: HTMLElement) => { | ||||
|   elt.classList.remove('hide') | ||||
| } | ||||
| const dataStore = useDataStore() | ||||
|  | ||||
| const scrollDown = function () { | ||||
|   window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }) | ||||
| } | ||||
|  | ||||
| // load data | ||||
| const { data: tracksData } = await useFetch('/api/tracks') | ||||
| const { data: artistsData } = await useFetch('/api/artists') | ||||
| const { data: stylesData } = await useFetch('/api/styles') | ||||
| const { data: compilationsData } = await useFetch('/api/compilations') | ||||
|  | ||||
| onMounted(() => { | ||||
|   setInterval(() => { | ||||
|     if (mixPlayer.value && currentCompilationId.value) { | ||||
|       const compilations = JSON.parse(JSON.stringify(compilationsData.value)) | ||||
|       const tracks = JSON.parse(JSON.stringify(tracksData.value)) | ||||
|       const artists = JSON.parse(JSON.stringify(artistsData.value)) | ||||
|       compilations.map((compilation: Compilation) => { | ||||
|         if (compilation.id === currentCompilationId.value) { | ||||
|           currentCompilationName.value = compilations.find((compilation: { id: string; }) => compilation.id === currentCompilationId.value).name | ||||
|           currentCompilationTracks.value = tracks.filter((track: { compilation: string; }) => track.compilation === compilation.id) | ||||
|           const currentTrack = currentCompilationTracks.value.find((track: { start: number; }, index: number) => { | ||||
|             const nextTrackStart = currentCompilationTracks.value[index + 1]?.start ?? Infinity | ||||
|             return track.start <= mixPlayer.value.currentTime && mixPlayer.value.currentTime < nextTrackStart | ||||
|           }) | ||||
|           currentArtist.value = artists.find((artist: { id: any; }) => artist.id === currentTrack.artist) | ||||
|           currentTitle.value = currentTrack.title | ||||
|           currentLink.value = currentTrack.link | ||||
|           currentCover.value = currentTrack.cover | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   }, 1000) | ||||
| }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss"> | ||||
| <style lang="scss" scoped> | ||||
| body { | ||||
|   margin: 0; | ||||
|   overflow-x: hidden; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user