Breaking

Showing posts with label Javascript Music Player. Show all posts
Showing posts with label Javascript Music Player. Show all posts

Tuesday, October 11, 2016

October 11, 2016

Membuat SlidingUpPanel Dengan HTML,CSS Dan Javascript Bagian 3


Pada postingan sebelumnya tentang Membuat SlidingUpPanel Dengan HTML, CSS Dan Javascript Bagian 2 kita telah selesai SlidingUpPanel, di postingan kali ini kita akan membuat javascript untuk memainkan lagu dari yang disimpan dalam folder songs.

Buatlah file javascript baru di dalam folder proyek Anda beri nama player.js. Seperti sebelumnya ssaat membuat sliding-up-panel.js buatlah class MusicPlayer pada file tersebut kemudian tambahkan constructor pada kelas MusicPlayer dan inisialisasikan variable yang akan digunakan.
'use strict';
class MusicPlayer{
constructor(){
this.player = document.querySelector('audio');
this.playlist = document.querySelector('.playlist');
this.playPauseBtn = Array.from(document.querySelectorAll('.play-btn'));
this.nextBtn = document.querySelector('.next-btn');
this.prevBtn = document.querySelector('.prev-btn');

this.songs = Array.from(document.querySelectorAll('.song a'));
this.currentSong = 0;

this.onPlayPause = this.onPlayPause.bind(this);
this.onNextClick = this.onNextClick.bind(this);
this.onPreviousClick = this.onPreviousClick.bind(this);
this.onProgress = this.onProgress.bind(this);
this.onMetadataLoaded = this.onMetadataLoaded.bind(this);
this.onSongEnded = this.onSongEnded.bind(this);
this.songChanged = this.songChanged;
this.setupMarqueeAnimation = this.setupMarqueeAnimation.bind(this);
this.addEventListeners();
this.initPlaylist();

}
}
window.addEventListener('load',() => new MusicPlayer());

Sekarang buat fungsi untuk menginisialisasikan daftar putar dengan melakukan XMLHttpRequest ke sisi server dan menerima semua nama file yang ada dalam folder songs.
initPlaylist(){
this.getPlaylist();
}
getPlaylist(){
var self = this;
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
xhr.onload = function(e){
e.preventDefault();
if(xhr.status == 200 || xhr.status == 409){
//parse the response to JSON Array
var data = JSON.parse(xhr.response.match(/\[.*\]/ig, "")[0]);
// loopping through all item in array, where each item contain link to the file.
data.forEach(function(item,index){
// load metadata from the file
self.loadPlaylistMetadata(item,index);
});
resolve(data);
}else{
reject('cant get paylist');
}
}
// open XMLHttpRequest with GET method
xhr.open('get', '\songlist', true);
// set header Content-Type to JSON
xhr.setRequestHeader('Content-Type', 'application/json');
// send the request to server
xhr.send();
});
}
loadPlaylistMetadata(path,index){
self =this;
// create new li element for the playlist item and set the attribute
var li = document.createElement('li');
li.setAttribute('class','song');
li.setAttribute('data-index',index);

// create new link element and set the href attribute to the file path
var a = document.createElement('a');
a.setAttribute('href',path);

// create new image element
var img = document.createElement('img');
img.className = 'cover';
img.style.cssFloat = 'left';

//create new container for title and artist metadata
var meta = document.createElement('div');
meta.className = 'metatag playlist';

//create new span element to set title from the file metadata
var title = document.createElement('span');
title.className = 'title';
title.style.whiteSpace = 'nowrap';

//create new span element to set artist name from the file metadata
var artist = document.createElement('span');
artist.className = 'artist';
//insert link/anchor element to li element
li.appendChild(a);
//insert img element to link/anchor element
a.appendChild(img);
//insert meta container to the link/anchor element
a.appendChild(meta);
/* create new p element and insert title and artist to it then append it to meta container,
* this element is necessary if we want the title or artist to
* be marqueed if the width is more than container width.*/
var p = document.createElement('p').appendChild(title);
meta.appendChild(p);
p = document.createElement('p').appendChild(artist);
meta.appendChild(p);
// listen to link click event
a.onclick = function(e){
e.preventDefault();
self.player.setAttribute('src',link);
self.songChanged(self.currentSong,li.getAttribute('data-index'));
self.currentSong = parseInt(li.getAttribute('data-index'));
self.playPauseBtn[0].children[0].click();
};
//append li element to playlist
this.playlist.children[0].appendChild(li);
// read meta data from the file using jsmediatags
jsmediatags.read(path, {
onSuccess: function(tag) {
var url = path;
var filename = url.split('/');
filename = filename[filename.length-1];
if(tag.tags.picture != undefined){
var base64String = "";
for (var i = 0; i < tag.tags.picture.data.length; i++) {
base64String += String.fromCharCode(tag.tags.picture.data[i]);
}
img.setAttribute('src',"data:"+ tag.tags.picture.format +";base64," + window.btoa(base64String));
}else{
img.setAttribute('src',"assets/img/albumart_mp_unknown.png");
}
// if the artist name in metadata is undefined or null, set it to unknown.
artist.innerHTML = tag.tags.artist || 'unknown';
// if the title in metadata is undefined or null, use filename instead.
title.innerHTML = tag.tags.title || filename;

// setup marquee animation
self.setupMarqueeAnimation(titles[i],artists[i]);

},
onError: function(error) {
console.log(error);
}
});
}
Buatlah fungsi baru untuk menambahkan animasi marquee pada title/artist jik lebarnya melebihi container/parent.
setupMarqueeAnimation(title,artist){
if(title.offsetWidth > parseInt(title.parentNode.style.width.replace('px',''))){
title.className += ' marquee-title';
}else{
title.style.willChange = 'initial';
title.className = 'title';
}
if(artist.offsetWidth > parseInt(artist.parentNode.style.width.replace('px',''))){
artist.className += ' marquee-artist';
}else{
artist.className = 'artist';
}
}
Setelah itu buat fungsi untuk menambahkan event listener yang dibutuhkan.
addEventListeners(){
this.player.ontimeupdate = this.onProgress;
this.player.onended = this.onSongEnded;
this.player.onloadedmetadata = this.onMetadataLoaded;
for(var i in this.playPauseBtn){
this.playPauseBtn[i].addEventListener('click',this.onPlayPause);
}
this.nextBtn.addEventListener('click',this.onNextClick);
this.prevBtn.addEventListener('click',this.onPreviousClick);
}
Sekarang buat fungsi-fungsi yang digunakan di event listener diatas.
onSongEnded(e){
this.moveToNext();
}
onNextClick(e){
this.moveToNext();
e.preventDefault();
}
onPreviousClick(e){
this.moveToPrevious();
e.preventDefault();
}
onPlayPause(e){
if(this.player.getAttribute('src') == ''){
this.player.setAttribute('src',this.songs[0].getAttribute('href'));
this.currentSong = 0;
this.songChanged(null,0);
}
if(this.player.paused){
this.player.play().then().catch(function(error){
console.log(error);
});
for(var i in this.playPauseBtn){
this.playPauseBtn[i].children[0].setAttribute('src','assets/icons/pause-btn.svg');
this.playPauseBtn[i].setAttribute('state','play');
}
}else{
try{
this.player.pause();
for(var i in this.playPauseBtn){
this.playPauseBtn[i].children[0].setAttribute('src','assets/icons/play-btn.svg');
this.playPauseBtn[i].setAttribute('state','play');
}
}catch(error){
console.log(error);
}
}
e.preventDefault();
}
onMetadataLoaded(e){
var self = this;
var artists = document.querySelectorAll('.meta .artist');
var titles = document.querySelectorAll('.meta .title');
var covers = Array.from(document.querySelectorAll('img.coverart'));
jsmediatags.read(e.target.src, {
onSuccess: function(tag) {
var url = e.target.src;
var filename = url.split('/');
filename = filename[filename.length-1]
if(tag.tags.picture != undefined){
for(var j in covers){
var base64String = "";
for (var i = 0; i < tag.tags.picture.data.length; i++) {
base64String += String.fromCharCode(tag.tags.picture.data[i]);
}
covers[j].setAttribute('src',"data:"+ tag.tags.picture.format +";base64," + window.btoa(base64String));
}
}else{
for(var j in covers){
covers[j].setAttribute('src',"assets/img/albumart_mp_unknown.png");
}
}
for(var i = 0; i < artists.length;i++){
titles[i].parentNode.style.width = 200;
artists[i].parentNode.style.width = 200;
artists[i].innerHTML = tag.tags.artist;
titles[i].innerHTML = tag.tags.title;

// if the artist name in metadata is undefined or null, set it to unknown.
artist.innerHTML = tag.tags.artist || 'unknown';
// if the title in metadata is undefined or null, use filename instead.
title.innerHTML = tag.tags.title || filename;

// setup marquee animation
self.setupMarqueeAnimation(titles[i],artists[i]);
}

},
onError: function(error) {
console.log(error);
}
});
}

moveToNext(){
var next = this.currentSong + 1,link;
if(next < this.songs.length){
link = this.songs[next].getAttribute('href');
this.songChanged(this.currentSong,next);
this.currentSong = next;
this.player.setAttribute('src',link);

}else{
link = this.songs[0].getAttribute('href');
this.songChanged(this.currentSong,0);
this.currentSong = 0;
this.player.setAttribute('src',link);
}
this.player.load();
this.player.play();
}
moveToPrevious(){
var prev = this.currentSong - 1,link;
if(prev >= 0){
link = this.songs[prev].getAttribute('href');
this.songChanged(this.currentSong,prev);
this.currentSong = prev;
this.player.setAttribute('src',link);

}else{
prev = this.songs.length - 1;
var link = this.songs[prev].getAttribute('href');
this.songChanged(this.currentSong,prev);
this.currentSong = prev;
this.player.setAttribute('src',link);
}
this.player.play();
}
Kemudian buat fungsi baru lagi untuk mengubah warna background dari item yang saat ini sedang dimainkan.
songChanged(before,current){
//if the song before the current one is not null, remove active class from that element
if(before != null){
this.songs[before].removeAttribute('class');
}
// set the current song item class in playlist to active
this.songs[current].setAttribute('class','active');
}
Sampai disini pembuatan pemutar musiknya telah selesai, sekarang buka cmd/terminal kemudiang ketikkan perintah supervisor index.js kemudian buka browser dan ubah url menjadi http:/localhost:3000 untuk melihat hasilnya. Kurang lebih hasilnya menjadi akan seperti berikut.



untuk postingan selanjutnya kita akan memperbaiki beberapa tampilan termasuk panel header yang akan kita buat berganti tampilan saat kita membuka panel. :)

Tuesday, October 4, 2016

October 04, 2016

Membuat SlidingUpPanel Dengan HTML,CSS Dan Javascript Bagian 1


Pada kesempatan kali ini saya akan membahas tentang bagaimana Membuat SlidingUpPanel Dengan HTML, CSS dan javascript. Biasanya SlidingUpPanel ini banyak digunakan di aplikasi-aplikasi pemutar musik di Android. Namun disini kita akan mencoba membuatnya menggunakan HTML, CSS dan javascript. Kita akan membuat pemutar musik sederhana dengan SlidingUpPanel yang hampir mirip dengan Google Play Music.

Sebelum memulai ada baiknya Anda menginstall Nodejs terlebih dahulu jika di Komputer/Laptop Anda belum terinstall Nodejs. Nodejs merupakan teknologi javascript sisi server. Anda dapat mendownload Nodejs di https://nodejs.org.
Setelah mengistall nodejs Anda perlu menginisialisasikan folder proyek Anda terlebih dahulu, dan mengisi rincian tentang proyek Anda. Pada folder proyek Anda buka terminal/cmd kemudian ketikkan perintah berikut.
npm init
Setelah itu isi semua rincian yang ditanyakan.

Selanjutnya Anda peru menginstall beberapa modul yang akan kita gunakan diantaranya adalah:
  1. express
  2. jsmediatags
  3. fs
  4. path
  5. supervisor
Untuk menginstall modul-modul diatas Anda dapat menggunakan cmd/terminal dengan mengetikkan perintah sebagai berikut :


Mac/Linux :

sudo npm install -g [nama-module]


Windows :
npm install -g [nama-module]

Ubah [nama-modul] menjadi nama modul yang ingin diinstall, contoh saya ingin menginstall modul express pada laptop yang memiliki sistem operasi Windows maka saya mengetikan perintah berikut :
npm install -g express.

Sebelum membuat file-file untuk sisi klien kita perlu membuat minimal 1 file javascript untuk digunakan di sisi server. Buatlah file baru dan beri nama index.js, kemudian tuliskan script berikut.
'use strict';
var fs = require('fs'),
express = require('express'),
app = express(),
path = require('path');

var data = [];
readFiles('songs/', function(filename, content) {
data.push('http://localhost:3000/songs/' +filename);
}, function(err) {
throw err;
});

function readFiles(dirname, onFileContent, onError) {
fs.readdir(dirname, function(err, filenames) {
if (err) {
onError(err);
return;
}
filenames.forEach(function(filename) {
fs.readFile(dirname + filename, 'utf-8', function(err, content) {
if (err) {
onError(err);
return;
}
onFileContent(filename, content);
});
});
});
}
app.use(function (req, res, next) {
var filename = req.url || "index.html";
var ext = path.extname(filename);
var localPath = __dirname;
var validExtensions = {
".html" : "text/html",
".js": "application/javascript",
".json": "application/json",
".css": "text/css",
".txt": "text/plain",
".jpg": "image/jpeg",
".gif": "image/gif",
".png": "image/png",
".ico": "image/png",
".svg": "image/svg+xml",
".webm": "audio/webm",
".aac": "audio/aac",
".ogg": "audio/ogg",
".wav": "audio/wav",
".mp3": "audio/mpeg",
".mp4": "audio/mp4",
".m4a": "audio/mp4"
};
var isValidExt = validExtensions[ext];

if (isValidExt) {
localPath += filename;

fs.exists(localPath, function(exists) {
if(exists) {
console.log("Serving file: " + localPath);
getFile(localPath, res, validExtensions[ext]);
} else {
console.log("File not found: " + localPath);

if(ext === 'text/html'){
getFile(__dirname + '/404.html', res, validExtensions[ext]);
}
}
});

} else if(req.url == "/songlist"){
res.setHeader("Content-Type", 'application/json');
res.statusCode = 200;
res.end(JSON.stringify(data));
}else {
console.log("Invalid file extension detected: " + ext);
getFile(__dirname + '/index.html', res, 'text/html');
}
});
app.listen(3000);
function getFile(localPath, res, mimeType) {
fs.readFile(localPath, function(err, contents) {
if(!err) {
res.setHeader("Content-Length", contents.length);
res.setHeader("Content-Type", mimeType);
res.statusCode = 200;
res.end(contents);
} else {
res.writeHead(500);
res.end();
}
});
}


Pada script diatas saya menggunakan express untuk membuat http server pada port 3000, kemudian terdapat fungsi app.use() yang digunakan untuk mengirim resource ke sisi klien berdasarkan url yang diminta, disana saya hanya melakukan pengecekan apakah file resource yg diminta valid atau tidak dan apakah file tersebut tersedia di sisi server.

Sekarang kita akan mulai membuat desainnya, pertama-tama buatlah file html baru beri nama index.html kemudian buatlah template HTML seperti berikut.
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="author" content="Muhammad Sayuti" />
<meta name="viewport" content="width=device-width,minimum-scale=1">
<meta name="theme-color" content="#EA2663">
<title>Sliding Up Panel</title>
</head>
<body>
<div class="playlist">
<ul>
</ul>
</div>
<div id="sliding-up-panel">
<div id="sliding-up-panel-header">
<img src="assets/img/albumart_mp_unknown.png" class="coverart header" />
<div class="meta header">
<span class="artist"></span>
<span class="title"></span>
</div>
<div class="player-control header">
<a href="#" id="prev-btn">Prev</a>
<a href="#" id="play-btn" state="pause"><img src="icons/play-button.svg" style="width:25px;height:25px;"/></a>
<a href="#" id="next-btn">Next</a>
</div>
</div>
<div id="sliding-up-panel-body">
<img src="assets/img/albumart_mp_unknown.png" class="coverart content" />
<div class="meta content">
<span class="artist"></span>
<span class="title"></span>
</div>
<div class="player-control content">
<a href="#" id="prev-btn">Prev</a>
<a href="#" id="play-btn" state="pause"><img src="icons/play-button.svg" style="width:25px;height:25px;"/></a>
<a href="#" id="next-btn">Next</a>
</div>
</div>
</div>
<audio id="player" src="" style="display:hidden;"></audio>

</body>
</html>
Anda dapat menggunakan lagu yang Anda miliki dan menempatkannya di folder songs, pastikan nama file tidak ada spasi untuk menghindari error saat memainkan lagu, karena spasi akan di konversi secara otomatis menjadi karakter unicode sehingga nantinya akan muncul error bahwa file tidak ditemukan karena spasi pada nama file diubah ke karakter unicode.

Pada template HTML diatas tag audio saya beri property style dimana value dari display adalah hidden karena nantinya kita tidak menggunakan kontrol default dari element audio,namun menggunakan beberapa link yang dapat Anda lihat pada element div dengan nama class player-control. Disini css saya bagi menjadi 2 file, 1 yang dibuat untuk mengatur tampilan SlidingUpPanel dan yang kedua untuk mengatur tampilan yang lain.
Buat file baru dan beri nama sliding-up-panel.css kemudian isikan seperti berikut.
#sliding-up-panel{
display:block;
position:fixed;
top:0;
left:0;
right:0;
bottom: 0;
}
#sliding-up-panel-header{
background-color: #424242;
box-sizing: border-box;
position:relative;
width: 100%;
height: 64px;
overflow: hidden;
}
#sliding-up-panel-body{
background-color: #757575;
overflow: auto;
height: 100%;
width: 100%;
}

Sekarang buat 1 file css lagi beri nama player.css, kemudian isikan seperti berikut.
*{
padding:0px;
margin:0px;
font-family:san-serif;
font-size:24px;
}
html,body{
background-color: #757575;
}
a{
text-decoration: none;
}
.playlist{
display: block;
width:100%;
}
/* Playlist style */
.playlist ul li{
display: block;
border-bottom: 1px solid black;

}
.playlist ul li a{
height: 57px;
display: block;
color: white;
}
.playlist ul li a.active{
background-color: rgba(0,0,0, 0.5);
color: #42A5F5;
}
.playlist .metatag .title{
font-size: 16px;
white-space: nowrap;
}
.playlist .metatag.playlist{
float:left;
padding:10px;
width: 87.3%;
overflow: hidden;
}
.playlist .metatag .artist{
font-size: 14px;
white-space: nowrap;
}
.playlist .cover{
height:100%;
max-width:8%;
}
/* Player Control style */
.player-control{
display: flex;
flex-basis: 33.3333333%;
align-items: center;
align-self: baseline;
width:134px;
}

/* to prevent SidingUpPanel not moving when we drag the header
* we need to set pointer-events to none.*/

.player-control.header{
pointer-events: none;
height: 100%;
padding: 0 0.5em;
float: right;
}

.player-control.content{
height: 64px;
margin: 0 auto !important;
pointer-events: none;
}
/* because it's a link with fake href set user-select to none and
* retrieve all pointer-events */
.player-control a{
pointer-events: all;
user-select: none;
}
.player-control img{
margin: auto 0;
width:100%;
pointer-events: none;
}
.play-btn img{
width: 50px;
height: 50px;
}
.next-btn img{
width: 40px;
height: 40px;
}
.prev-btn img{
width: 40px;
height: 40px;
}
/* Meta style General*/
.meta{
pointer-events: none;
color: #FFF;
}
.meta .title{
font-size: 16px;
white-space: nowrap;
display: inline-block;
position: relative;
left:0px;
}
.meta .artist{
font-size: 14px;
white-space: nowrap;
}
.marquee-title{
display: inline-block;
transition: transform 20s linear infinite;
animation: marquee2 20s linear infinite;
}
.marquee-artist{
display: inline-block;
transition: transform 20s linear infinite;
animation: marquee 20s linear infinite;
}
/* Meta style for sliding-up-panel-header*/
.meta.header{
display: flex;
flex-flow: row wrap;
float:left;
margin:15px;
max-width: 200px;
overflow: hidden;
}

/* Meta style for sliding-up-panel-body*/
.meta.content{
display: flex;
flex-flow: row wrap;
align-items: center;
margin: 1.5em auto;
max-width: 200px;
overflow: hidden;
text-align: center;
}

/* Coverart style*/
img.coverart{
display: hidden;
}
img.coverart.header[src]{
float: left;
pointer-events: none;
height:100%;
max-width: 70px;
border: none;
}
img.coverart.content[src]{
display: block;
margin: 0.5em auto;
height:50%;
border: none;
}



/* Keyframes animation */
@keyframes marquee {
0% {
transform:translateX(-25%);
}
25% {
transform:translateX(-50%);
}
50% {
transform:translateX(-25%);
}
75% {
transform:translateX(0%);
}
100%{
transform:translateX(-25%);
}
}
@keyframes marquee2 {
0% {
transform:translateX(0%);
}
25% {
transform:translateX(-25%);
}
50% {
transform:translateX(-50%);
}
75% {
transform:translateX(-25%);
}
100%{
transform:translateX(0%);
}
}

Sekarang desain awal sudah selesai, Anda dapat dapat menjalankan nodejs server dengan membuka cmd/terminal pada folder proyek Anda dan mengetikkan perintah supervisor index.js. Tampilan yang Anda lihat kurang lebih akan seperti berikut.


Pada postingan selanjutnya kita akan mulai membuat file-file javascript sisi klien.