| @@ -18,6 +18,8 @@ | |||
| "babel-eslint": "^10.1.0", | |||
| "eslint": "^6.7.2", | |||
| "eslint-plugin-vue": "^6.2.2", | |||
| "fs": "^0.0.1-security", | |||
| "jsmediatags": "^3.9.3", | |||
| "vue-template-compiler": "^2.6.11" | |||
| }, | |||
| "eslintConfig": { | |||
| @@ -0,0 +1,98 @@ | |||
| const jsmediatags = require('jsmediatags'); | |||
| const fs = require('fs'); | |||
| const INPUT = './public/audio/'; | |||
| const OUTPUT = 'src/data.json'; | |||
| const data = {"albums" : []}; | |||
| const promises = []; | |||
| try { | |||
| if (fs.existsSync(INPUT)) { | |||
| //file exists | |||
| } | |||
| else { | |||
| console.log("\n============== Weband Error ====================\n\n"); | |||
| console.log("Audio files are missing. Please run first : \n\n$ ./scripts/weband-init.sh <input_directory> \n"); | |||
| console.log("\n================================================\n\n"); | |||
| process.exit(1); | |||
| } | |||
| } catch(err) { | |||
| console.log(err); | |||
| process.exit(1); | |||
| } | |||
| function is_dir(path) { | |||
| try { | |||
| var stat = fs.lstatSync(path); | |||
| return stat.isDirectory(); | |||
| } catch (e) { | |||
| // lstatSync throws an error if path doesn't exist | |||
| return false; | |||
| } | |||
| } | |||
| function id3ToJSON(path){ | |||
| fs.readdirSync(path).forEach(file => { | |||
| let isDir = is_dir(path + file); | |||
| if(isDir) | |||
| id3ToJSON(path + file + '/'); | |||
| else { | |||
| let filePath = path + file; | |||
| promises.push(new Promise((resolve, reject) => { | |||
| new jsmediatags.Reader(filePath) | |||
| .read({ | |||
| onSuccess: (tag) => { | |||
| var tags = tag.tags; | |||
| if(!data.artist) | |||
| data.artist = tags.artist; | |||
| let album = getAlbum(tags.album); | |||
| if(!album){ | |||
| album = {"title": tags.album, "tracks": [], "year": tags.year} | |||
| data.albums.push(album); | |||
| } | |||
| let track = {"title": tags.title, "file" : filePath.replace(INPUT,'audio/')}; | |||
| album.tracks.push(track); | |||
| resolve(tag); | |||
| }, | |||
| onError: (error) => { | |||
| console.log('Error'); | |||
| console.log(error); | |||
| reject(error); | |||
| } | |||
| }); | |||
| })); | |||
| } | |||
| }); | |||
| } | |||
| function getAlbum(albumTitle){ | |||
| for(let i = 0 ; i < data.albums.length; i++){ | |||
| if(data.albums[i].title === albumTitle) | |||
| return data.albums[i]; | |||
| } | |||
| return null; | |||
| } | |||
| id3ToJSON(INPUT); | |||
| Promise.all(promises).then((values) => { | |||
| var jsonContent = JSON.stringify(data, null, 2); | |||
| console.log(jsonContent); | |||
| fs.writeFile(OUTPUT, jsonContent, 'utf8', function (err) { | |||
| if (err) { | |||
| console.log("An error occured while writing JSON Object to File."); | |||
| return console.log(err); | |||
| } | |||
| console.log("JSON file has been saved : " + OUTPUT); | |||
| }); | |||
| }); | |||
| @@ -0,0 +1,85 @@ | |||
| #!/bin/bash | |||
| DIRECTORY=$(cd `dirname $0` && pwd) | |||
| IMAGES_DIRECTORY=$DIRECTORY'/../src/assets/images/' | |||
| AUDIOS_DIRECTORY=$DIRECTORY'/../public/audio/' | |||
| ICONO_HEADER='icono_1.png' | |||
| ICONO_FOOTER='icono_2.png' | |||
| display_usage(){ | |||
| echo "" | |||
| echo " webbandd script" | |||
| echo "" | |||
| echo " usage : webbandd.sh <input_directory>" | |||
| echo "" | |||
| echo " <input_directory> must contains :" | |||
| echo " - two image files : icono_1.png & icono_2.png" | |||
| echo " - one folder per album with mp3 and/or wav audio files" | |||
| echo "" | |||
| } | |||
| error(){ | |||
| echo "" | |||
| echo " Weband ERROR :" | |||
| echo "" | |||
| echo " "$1 | |||
| echo "" | |||
| } | |||
| # 1 argument required | |||
| if [ $# -ne 1 ] | |||
| then | |||
| display_usage | |||
| exit 1 | |||
| fi | |||
| # check_image(){ | |||
| # if [[ -e $1'.svg' || -e $1'.png' || -e $1'.jpg' ]] | |||
| # then | |||
| # return 0 | |||
| # else | |||
| # error $1' image file is missing !' | |||
| # exit -1 | |||
| # fi | |||
| # } | |||
| check_image(){ | |||
| if [ -e $1 ] | |||
| then | |||
| return 0 | |||
| else | |||
| error $1' image file is missing !' | |||
| exit -1 | |||
| fi | |||
| } | |||
| check_image $1'/'$ICONO_HEADER | |||
| echo " => "$1'/'$ICONO_HEADER" image exists - OK " | |||
| check_image $1'/'$ICONO_FOOTER | |||
| echo " => "$1'/'$ICONO_FOOTER" image exists - OK " | |||
| #copy images (cover and contact) | |||
| cp $1'/'$ICONO_HEADER $IMAGES_DIRECTORY | |||
| cp $1'/'$ICONO_FOOTER $IMAGES_DIRECTORY | |||
| if [ -d $AUDIOS_DIRECTORY ] | |||
| then | |||
| rm -rf $AUDIOS_DIRECTORY | |||
| fi | |||
| mkdir $AUDIOS_DIRECTORY | |||
| for d in `find $1/* -type d` | |||
| do | |||
| echo ' copying audio directory "'$d'"' | |||
| cp -r $d $AUDIOS_DIRECTORY | |||
| done | |||
| echo " => audio files copy - OK " | |||
| node $DIRECTORY'/weband-id3-to-json.js' | |||
| @@ -1,28 +1,38 @@ | |||
| <template> | |||
| <div id="app"> | |||
| <img alt="Vue logo" src="./assets/logo.png"> | |||
| <HelloWorld msg="Welcome to Your Vue.js App"/> | |||
| <Weband/> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import HelloWorld from './components/HelloWorld.vue' | |||
| import Weband from './components/Weband.vue' | |||
| export default { | |||
| name: 'App', | |||
| components: { | |||
| HelloWorld | |||
| Weband | |||
| } | |||
| } | |||
| </script> | |||
| <style> | |||
| #app { | |||
| font-family: Avenir, Helvetica, Arial, sans-serif; | |||
| -webkit-font-smoothing: antialiased; | |||
| -moz-osx-font-smoothing: grayscale; | |||
| text-align: center; | |||
| color: #2c3e50; | |||
| margin-top: 60px; | |||
| @import './assets/css/_variables.css'; | |||
| *{ | |||
| margin :0; | |||
| padding:0; | |||
| } | |||
| body{ | |||
| font-family: var(--font-family); | |||
| font-size: 2rem; | |||
| letter-spacing: 0.1rem; | |||
| } | |||
| body, html, #app { | |||
| margin :0; | |||
| padding:0; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,10 @@ | |||
| @font-face { | |||
| font-family: "work-sans"; | |||
| src: url('../fonts/work-sans/WorkSans-Regular.otf'); | |||
| } | |||
| :root{ | |||
| --font-family : 'work-sans'; | |||
| --pretty-margin: 0.5rem; | |||
| --main-color: black; | |||
| --secondary-color: black; | |||
| } | |||
| @@ -1,58 +0,0 @@ | |||
| <template> | |||
| <div class="hello"> | |||
| <h1>{{ msg }}</h1> | |||
| <p> | |||
| For a guide and recipes on how to configure / customize this project,<br> | |||
| check out the | |||
| <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>. | |||
| </p> | |||
| <h3>Installed CLI Plugins</h3> | |||
| <ul> | |||
| <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li> | |||
| <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li> | |||
| </ul> | |||
| <h3>Essential Links</h3> | |||
| <ul> | |||
| <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li> | |||
| <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li> | |||
| <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li> | |||
| <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li> | |||
| <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li> | |||
| </ul> | |||
| <h3>Ecosystem</h3> | |||
| <ul> | |||
| <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li> | |||
| <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li> | |||
| <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li> | |||
| <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li> | |||
| <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li> | |||
| </ul> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: 'HelloWorld', | |||
| props: { | |||
| msg: String | |||
| } | |||
| } | |||
| </script> | |||
| <!-- Add "scoped" attribute to limit CSS to this component only --> | |||
| <style scoped> | |||
| h3 { | |||
| margin: 40px 0 0; | |||
| } | |||
| ul { | |||
| list-style-type: none; | |||
| padding: 0; | |||
| } | |||
| li { | |||
| display: inline-block; | |||
| margin: 0 10px; | |||
| } | |||
| a { | |||
| color: #42b983; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,374 @@ | |||
| <template> | |||
| <div v-if="!collapsed" class="player"> | |||
| <div class="progress-wrap"> | |||
| <div class="time-info"> | |||
| {{audio ? formatDuration(audio.currentTime) : "00:00"}} | |||
| </div> | |||
| <progress :value="audioPosition" :max="audio ? Math.floor(audio.duration): 0"></progress> | |||
| <div class="time-info"> | |||
| {{audio ? formatDuration(audio.duration) : "00:00"}} | |||
| </div> | |||
| </div> | |||
| <h3>{{album.title}}</h3> | |||
| <ul> | |||
| <li | |||
| v-for="(track, index) in album.tracks" | |||
| v-on:click="trackClicked('track-'+index)" | |||
| v-bind:key="index+'-'+track.title" | |||
| v-bind:class="{ active: audio && audio.getAttribute('id') === 'track-'+index }"> | |||
| <span class="track-number">{{index + 1}}</span> | |||
| <span class="track-title">{{track.title.trim()}}</span> | |||
| <audio v-on:timeupdate="updateAudioPosition()" v-bind:src="track.file" v-bind:id="'track-'+ index"></audio> | |||
| </li> | |||
| </ul> | |||
| <div class="button-wrap"> | |||
| <img v-if="playing" v-on:click="pause()" src="../assets/images/pause.png" id="play-button"> | |||
| <img v-else v-on:click="play()" src="../assets/images/play.png" id="play-button"> | |||
| </div> | |||
| <div class="player-switch opened" v-on:click="toggle()"></div> | |||
| </div> | |||
| <div v-else class="player-switch" v-on:click="toggle()"> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| name: 'Player', | |||
| props: { | |||
| album: Object, | |||
| }, | |||
| data: function () { | |||
| return { | |||
| switcher : null, | |||
| collapsed: true, | |||
| playing: false, | |||
| audioPosition: 0, | |||
| audio : null, | |||
| startTimeInfo: "00:00", | |||
| endTimeInfo: "00:00", | |||
| playerImage: '../assets/images/play.png' | |||
| } | |||
| }, | |||
| updated() { | |||
| //this.bindAudioTags(); | |||
| }, | |||
| mounted(){ | |||
| this.switcher = document.getElementsByClassName('player-switch')[0]; | |||
| document.body.append(this.switcher); | |||
| // this.currentAudio = null; | |||
| // this.playing = false; | |||
| // this.playerContainer = document.createElement("div"); | |||
| // this.container = document.querySelector('#player') | |||
| // this.playerContainer.classList.add('dd-player'); | |||
| // this.switcher = document.createElement("div"); | |||
| // this.switcher.classList.add('dd-player-switch'); | |||
| // this.playerContainer.classList.add('collapsed'); | |||
| // document.body.appendChild(this.switcher); | |||
| // | |||
| // this.switcher.addEventListener('click', () => { | |||
| // this.playerContainer.classList.toggle('collapsed'); | |||
| // this.switcher.classList.toggle('opened'); | |||
| // }); | |||
| // | |||
| // | |||
| // this.addProgressBar(); | |||
| // this.bindAudioTags(); | |||
| // this.addPlayButton(); | |||
| // | |||
| // let wrap = document.createElement("div"); | |||
| // wrap.setAttribute('id', 'wrap-playlist') | |||
| // this.container.parentElement.appendChild(this.playerContainer); | |||
| // this.playerContainer.appendChild(wrap); | |||
| // wrap.appendChild(this.container); | |||
| }, | |||
| methods: { | |||
| toggle : function(){ | |||
| // this.playerContainer.classList.toggle('collapsed'); | |||
| this.collapsed = !this.collapsed; | |||
| this.switcher.classList.toggle('opened'); | |||
| }, | |||
| play: function(){ | |||
| if(!this.audio) | |||
| this.audio = document.querySelectorAll('audio')[0]; | |||
| this.playing = true; | |||
| this.audio.play(); | |||
| // if(this.playing){ | |||
| // this.audio.play(); | |||
| // // this.currentAudio.parentElement.classList.add('playing'); | |||
| // // self.progressBar.setAttribute('max', Math.floor(self.currentAudio.duration)); | |||
| // // self.endTimeInfo.innerHTML = self.formatDuration(self.currentAudio.duration); | |||
| // // this.playButton.setAttribute('src','./images/pause.png'); | |||
| // } | |||
| // else{ | |||
| // this.audio.pause(); | |||
| // // self.currentAudio.parentElement.classList.remove('playing'); | |||
| // // this.playButton.setAttribute('src','./images/play.png'); | |||
| // } | |||
| }, | |||
| pause: function(){ | |||
| this.playing = false; | |||
| this.audio.pause(); | |||
| }, | |||
| trackClicked: function(audioId){ | |||
| //let track = document.getElementById(audioId); | |||
| if(this.playing){ | |||
| this.pause(); | |||
| this.audio = document.getElementById(audioId); | |||
| } | |||
| else{ | |||
| this.audio = document.getElementById(audioId); | |||
| } | |||
| this.playing = true; | |||
| this.audio.play(); | |||
| }, | |||
| updateAudioPosition: function(){ | |||
| this.audioPosition = Math.floor(this.audio.currentTime); | |||
| }, | |||
| // addProgressBar: function(){ | |||
| // this.progressBar = document.createElement("progress"); | |||
| // this.progressBar.setAttribute('value', 0); | |||
| // this.startTimeInfo = document.createElement('div'); | |||
| // this.endTimeInfo = document.createElement('div'); | |||
| // let progressWrap = document.createElement("div"); | |||
| // this.startTimeInfo.innerHTML = "00:00"; | |||
| // this.endTimeInfo.innerHTML = "00:00"; | |||
| // progressWrap.appendChild(this.startTimeInfo); | |||
| // progressWrap.appendChild(this.progressBar); | |||
| // progressWrap.appendChild(this.endTimeInfo); | |||
| // this.startTimeInfo.classList.add('time-info'); | |||
| // this.endTimeInfo.classList.add('time-info'); | |||
| // progressWrap.classList.add('progress-wrap'); | |||
| // this.playerContainer.append(progressWrap); | |||
| // let self = this; | |||
| // this.progressBar.addEventListener('click', (e) => { | |||
| // if(self.currentAudio){ | |||
| // self.currentAudio.currentTime = Math.floor(((e.layerX - self.progressBar.offsetLeft) * self.currentAudio.duration) / self.progressBar.offsetWidth); | |||
| // self.progressBar.value = Math.floor(self.currentAudio.currentTime ); | |||
| // } | |||
| // }); | |||
| // | |||
| // }, | |||
| // addPlayButton: function(){ | |||
| // this.playButton = document.createElement("img"); | |||
| // this.playButton.setAttribute('src','./images/play.png'); | |||
| // this.playButton.setAttribute('id','play-button'); | |||
| // let wrap = document.createElement("div"); | |||
| // wrap.append(this.playButton); | |||
| // this.container.append(wrap); | |||
| // let self = this; | |||
| // this.playButton.addEventListener('click', () => { | |||
| // self.playing = !self.playing; | |||
| // if(self.playing){ | |||
| // self.currentAudio.play(); | |||
| // self.currentAudio.parentElement.classList.add('playing'); | |||
| // self.progressBar.setAttribute('max', Math.floor(self.currentAudio.duration)); | |||
| // self.endTimeInfo.innerHTML = self.formatDuration(self.currentAudio.duration); | |||
| // this.playButton.setAttribute('src','./images/pause.png'); | |||
| // } | |||
| // else{ | |||
| // self.currentAudio.pause(); | |||
| // self.currentAudio.parentElement.classList.remove('playing'); | |||
| // this.playButton.setAttribute('src','./images/play.png'); | |||
| // } | |||
| // }) | |||
| // }, | |||
| // bindAudioTags: function(){ | |||
| // let liTags = this.container.querySelectorAll('li') | |||
| // let self = this; | |||
| // let i = 0; | |||
| // liTags.forEach(function(itemElement){ | |||
| // let audioTag = itemElement.querySelector('audio') | |||
| // if(audioTag){ | |||
| // audioTag.addEventListener('timeupdate', () => { | |||
| // self.audioTimeUpdate(audioTag); | |||
| // }); | |||
| // audioTag.addEventListener('ended', () => { | |||
| // self.audioEnded(i); | |||
| // }) | |||
| // | |||
| // //itemElement.appendChild(audioElement); | |||
| // audioTag.setAttribute('id','track-'+i); | |||
| // if(i === 0) | |||
| // self.currentAudio = audioTag; | |||
| // itemElement.addEventListener('click', () => { | |||
| // let items = self.container.getElementsByTagName('li'); | |||
| // for(let i = 0; i < items.length; i++){ | |||
| // items[i].classList.remove('playing'); | |||
| // } | |||
| // if(self.currentAudio) | |||
| // self.currentAudio.pause(); | |||
| // audioTag.play(); | |||
| // self.playButton.setAttribute('src','./images/pause.png'); | |||
| // self.playing = true; | |||
| // itemElement.classList.add('playing'); | |||
| // self.currentAudio = audioTag; | |||
| // self.progressBar.setAttribute('max', Math.floor(self.currentAudio.duration)); | |||
| // self.endTimeInfo.innerHTML = self.formatDuration(self.currentAudio.duration); | |||
| // self.progressBar.setAttribute('value', 0); | |||
| // }); | |||
| // | |||
| // i++; | |||
| // } | |||
| // }); | |||
| // }, | |||
| // audioTimeUpdate: function(audioElt){ | |||
| // this.progressBar.value = Math.floor(audioElt.currentTime); | |||
| // this.startTimeInfo.innerHTML = this.formatDuration(audioElt.currentTime); | |||
| // }, | |||
| // | |||
| // audioEnded: function(n){ | |||
| // let audios = document.getElementsByTagName('audio'); | |||
| // let next = n == audios.length - 1 ? 0 : n + 1; | |||
| // this.currentAudio.pause(); | |||
| // this.currentAudio.currentTime = 0; | |||
| // this.currentAudio = document.getElementsByTagName('audio')[next]; | |||
| // this.currentAudio.parentElement.click(); | |||
| // }, | |||
| formatDuration: function(time){ | |||
| let s = parseInt(time% 60); | |||
| let m = parseInt((time / 60) % 60); | |||
| return (m < 10 ? ('0'+m) : m) + ':' + (s < 10 ? ('0'+s) : s); | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style> | |||
| *{ | |||
| --one-dpi: 72px; | |||
| --dd-blue: rgb(0,42,255); | |||
| } | |||
| .player{ | |||
| width: 25vw; | |||
| height: 100vh; | |||
| position:fixed; | |||
| top: 0px; | |||
| background-color: white; | |||
| right:0px; | |||
| color: black; | |||
| padding-top: var(--one-dpi); | |||
| padding-bottom: var(--one-dpi); | |||
| display: flex; | |||
| flex-direction: column; | |||
| /* justify-content: space-between; */ | |||
| } | |||
| .collapsed{ | |||
| display: none; | |||
| } | |||
| .player-switch{ | |||
| position :fixed; | |||
| bottom: 0; | |||
| right: 0; | |||
| width: var(--one-dpi); | |||
| height:var(--one-dpi); | |||
| background-color: white; | |||
| cursor: pointer; | |||
| z-index:10; | |||
| } | |||
| .player-switch.opened{ | |||
| background-color: var(--dd-blue); | |||
| } | |||
| li.active{ | |||
| font-weight: bold; | |||
| } | |||
| .player h3 { | |||
| font-size: 1rem; | |||
| margin: calc(var(--one-dpi) / 2); | |||
| } | |||
| .player ul { | |||
| margin-left : calc(var(--one-dpi) / 2); | |||
| color: var(--dd-blue); | |||
| font-size: 1.5rem; | |||
| list-style-type : none; | |||
| } | |||
| /* | |||
| .dd-player ul li img{ | |||
| width:24px; | |||
| height:24px; | |||
| margin-right: 12px; | |||
| } | |||
| .playing{ | |||
| font-weight: bold; | |||
| color : black; | |||
| } | |||
| */ | |||
| .progress-wrap{ | |||
| display:flex; | |||
| width: 100%; | |||
| justify-content: space-around; | |||
| } | |||
| .progress-wrap .time-info{ | |||
| font-size:0.66rem; | |||
| display: flex; | |||
| justify-content: center; | |||
| } | |||
| progress{ | |||
| display: inline; | |||
| height: 10px; | |||
| width:66%; | |||
| } | |||
| /* | |||
| #play-button{ | |||
| max-width: calc(var(--one-dpi) / 2); | |||
| max-height: calc(var(--one-dpi) / 2); | |||
| margin: calc(var(--one-dpi) / 2); | |||
| } | |||
| */ | |||
| progress::-moz-progress-bar{ | |||
| background-color: black; | |||
| } | |||
| progress{ | |||
| background-color: #rgba(200,200,200,0.5); | |||
| } | |||
| .button-wrap{ | |||
| margin-top: 100px; | |||
| } | |||
| /* | |||
| .dd-player ul li, #play-button{ | |||
| cursor : pointer; | |||
| } | |||
| #wrap-playlist{ | |||
| flex: 1; | |||
| } | |||
| */ | |||
| span.track-number{ | |||
| width: 25px; | |||
| display: inline-block; | |||
| } | |||
| span.track-title:before{ | |||
| content: " - "; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,149 @@ | |||
| <template> | |||
| <div class="weband"> | |||
| <section class="cover"> | |||
| </section> | |||
| <section class="albums"> | |||
| <h2>{{this.artist}} | Albums</h2> | |||
| <ul> | |||
| <li v-for="album in albums" v-bind:key="album.title" v-on:click="albumSelected(album)"> | |||
| {{album.title}} | {{album.year}} | |||
| </li> | |||
| </ul> | |||
| </section> | |||
| <section class="icono-2"> | |||
| </section> | |||
| <section class="contact"> | |||
| <span>contact<span class="point">@</span>{{this.artist.toLowerCase()}}<span class="point">.</span>space</span> | |||
| <footer class="flex-center"> | |||
| dede<span class='point'>.</span>space<span class='pipe'>|</span>{{new Date().getFullYear()}} | |||
| </footer> | |||
| </section> | |||
| <Player v-bind:album="currentAlbum"/> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import Player from './Player.vue' | |||
| import * as data from '../data.json'; | |||
| export default { | |||
| name: 'Weband', | |||
| props: { | |||
| msg: String | |||
| }, | |||
| components: { | |||
| Player | |||
| }, | |||
| data: function () { | |||
| return { | |||
| artist: data.artist, | |||
| albums: data.albums, | |||
| currentAlbum: data.albums[0] | |||
| } | |||
| }, | |||
| methods: { | |||
| albumSelected: function(album){ | |||
| this.currentAlbum = album; | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped> | |||
| @import '../assets/css/_variables.css'; | |||
| .point { | |||
| margin: 0px var(--pretty-margin); | |||
| } | |||
| .pipe { | |||
| margin: 0px calc(3 * var(--pretty-margin)); | |||
| } | |||
| section{ | |||
| height: 100vh; | |||
| } | |||
| section h2 { | |||
| font-size: 2rem; | |||
| } | |||
| section:nth-of-type(odd){ | |||
| background-color: white; | |||
| color: var(--secondary-color); | |||
| } | |||
| section:nth-of-type(even){ | |||
| background-color: var(--secondary-color); | |||
| color: white; | |||
| } | |||
| section.cover{ | |||
| background : url(../assets/images/icono_1.png) no-repeat center center; | |||
| background-color: var(--main-color); | |||
| } | |||
| section.albums{ | |||
| background-color: black; | |||
| color:white; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| line-height: 200%; | |||
| } | |||
| section.albums > * { | |||
| padding-left: 10vw; | |||
| } | |||
| section.albums h2 { | |||
| padding-left: 20vw; | |||
| font-weight: normal; | |||
| } | |||
| section.albums ul { | |||
| list-style-type: none; | |||
| } | |||
| section.icono-2{ | |||
| background : url(../assets/images/icono_2.png) no-repeat center center; | |||
| background-color: var(--secondary-color); | |||
| } | |||
| section.contact{ | |||
| display : flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| position :relative; | |||
| /* background : url(../assets/images/contact.png) no-repeat center center; */ | |||
| background-color: var(--main-color); | |||
| /*color:white; */ | |||
| } | |||
| footer{ | |||
| font-size: 0.92rem; | |||
| position: absolute; | |||
| width: 100%; | |||
| bottom: 0; | |||
| text-align: center; | |||
| } | |||
| .hidden{ | |||
| display: none; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,36 @@ | |||
| { | |||
| "albums" : [ | |||
| { | |||
| "title" : "Album 1", | |||
| "date" : "2019", | |||
| "tracks" : [ | |||
| { | |||
| "file" : "audio/album_1/01.mp3", | |||
| "title" : "Track 1" | |||
| }, | |||
| { | |||
| "file" : "audio/album_1/02.mp3", | |||
| "title" : "Track 2" | |||
| }, | |||
| { | |||
| "file" : "audio/album_1/03.mp3", | |||
| "title" : "Track 3" | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "title" : "Album 2", | |||
| "date" : "2020", | |||
| "tracks" : [ | |||
| { | |||
| "file" : "audio/album_2/04.mp3", | |||
| "title" : "Track X" | |||
| }, | |||
| { | |||
| "file" : "audio/album_2/05.mp3", | |||
| "title" : "Track Y" | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| } | |||
| @@ -3729,6 +3729,11 @@ fs.realpath@^1.0.0: | |||
| resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" | |||
| integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= | |||
| fs@^0.0.1-security: | |||
| version "0.0.1-security" | |||
| resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" | |||
| integrity sha1-invTcYa23d84E/I4WLV+yq9eQdQ= | |||
| fsevents@^1.2.7: | |||
| version "1.2.13" | |||
| resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" | |||
| @@ -4699,6 +4704,13 @@ jsesc@~0.5.0: | |||
| resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" | |||
| integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= | |||
| jsmediatags@^3.9.3: | |||
| version "3.9.3" | |||
| resolved "https://registry.yarnpkg.com/jsmediatags/-/jsmediatags-3.9.3.tgz#309632d221d701bd385df65c9c6840cb399e11ff" | |||
| integrity sha512-h53yFnPYF1Y5jwr2ebcVzIIsvRpSalm0jhNiJDUztoPPHGpuHxi9YHUzdDgiw+ykiinXHd1s6HSIbudHw79zQw== | |||
| dependencies: | |||
| xhr2 "^0.1.4" | |||
| json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: | |||
| version "1.0.2" | |||
| resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" | |||
| @@ -8190,6 +8202,11 @@ ws@^6.0.0, ws@^6.2.1: | |||
| dependencies: | |||
| async-limiter "~1.0.0" | |||
| xhr2@^0.1.4: | |||
| version "0.1.4" | |||
| resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" | |||
| integrity sha1-f4dliEdxbbUCYyOBL4GMras4el8= | |||
| xtend@^4.0.0, xtend@~4.0.1: | |||
| version "4.0.2" | |||
| resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" | |||