| @@ -18,6 +18,8 @@ | |||||
| "babel-eslint": "^10.1.0", | "babel-eslint": "^10.1.0", | ||||
| "eslint": "^6.7.2", | "eslint": "^6.7.2", | ||||
| "eslint-plugin-vue": "^6.2.2", | "eslint-plugin-vue": "^6.2.2", | ||||
| "fs": "^0.0.1-security", | |||||
| "jsmediatags": "^3.9.3", | |||||
| "vue-template-compiler": "^2.6.11" | "vue-template-compiler": "^2.6.11" | ||||
| }, | }, | ||||
| "eslintConfig": { | "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> | <template> | ||||
| <div id="app"> | <div id="app"> | ||||
| <img alt="Vue logo" src="./assets/logo.png"> | |||||
| <HelloWorld msg="Welcome to Your Vue.js App"/> | |||||
| <Weband/> | |||||
| </div> | </div> | ||||
| </template> | </template> | ||||
| <script> | <script> | ||||
| import HelloWorld from './components/HelloWorld.vue' | |||||
| import Weband from './components/Weband.vue' | |||||
| export default { | export default { | ||||
| name: 'App', | name: 'App', | ||||
| components: { | components: { | ||||
| HelloWorld | |||||
| Weband | |||||
| } | } | ||||
| } | } | ||||
| </script> | </script> | ||||
| <style> | <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> | </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" | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" | ||||
| integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= | 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: | fsevents@^1.2.7: | ||||
| version "1.2.13" | version "1.2.13" | ||||
| resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" | 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" | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" | ||||
| integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= | 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: | json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: | ||||
| version "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" | 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: | dependencies: | ||||
| async-limiter "~1.0.0" | 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: | xtend@^4.0.0, xtend@~4.0.1: | ||||
| version "4.0.2" | version "4.0.2" | ||||
| resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" | ||||