Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
674 changes: 674 additions & 0 deletions COPYING

Large diffs are not rendered by default.

Binary file removed Capture1.JPG
Binary file not shown.
Binary file added Capture1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Capture2.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Capture3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 0 additions & 21 deletions LICENSE

This file was deleted.

163 changes: 142 additions & 21 deletions MMM-MP3Player.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
/* Magic Mirror Module: MMM-MP3Player
* v1.0.1 - May 2018
*
* By Asim Siddiqui <asimhsidd@gmail.com>
* MIT License
*
* Remade by Pavel Smelov.
* Version 1.2.0 - 2020.09.14
* GPLv3 License
*/

var arrPlayed = [];
Module.register("MMM-MP3Player",{
defaults: {
musicPath: "modules/MMM-MP3Player/music/",
autoPlay: true,
random: false,
loopList: true,
},
getStyles: function() {
return ["style.css"];
},
Expand All @@ -20,10 +27,16 @@ Module.register("MMM-MP3Player",{
self.info.className = "info";
self.artist = document.createElement("span");
self.artist.className = "artist";
self.artist.innerHTML = "MMM-USBPlayer";
self.artist.innerHTML = "MMM-MP3Player";
self.song = document.createElement("span");
self.song.className = "name";
self.song.innerHTML = "Insert USB Drive";
if (self.config.autoPlay){ self.song.innerHTML = "AutoPlay Enabled"; }
else { self.song.innerHTML = "AutoPlay Disabled"; }
if (self.config.random){ self.song.innerHTML = self.song.innerHTML + "<br />Random Enabled"; }
else { self.song.innerHTML = self.song.innerHTML + "<br />Random Disabled"; }
//if (self.config.loopList){ self.song.innerHTML = self.song.innerHTML + "<br />Loop Enabled"; }
//else { self.song.innerHTML = self.song.innerHTML + "<br />Loop Disabled"; }
//self.song.innerHTML = "Searching mp3...";
progress = document.createElement("div");
progress.className = "progress-bar";
self.bar = document.createElement("div");
Expand All @@ -48,30 +61,59 @@ Module.register("MMM-MP3Player",{
var self = this;
switch(notification){
case "Error": // Universal error handler
self.USBAttached = false;
console.log("USB Detached!");
self.musicFound = false;
console.log("[MMM-MP3Player] Error! ", payload);
break;
case "Music_Files": // this populates the songs list (array)
self.songs = payload;
self.current = 0;
self.USBAttached = true;
console.log("USB Attached!");
console.log("Music Found");
self.sendSocketNotification("LOADFILE", self.songs[self.current]);
self.musicFound = true;
console.log("[MMM-MP3Player] Music Found");
arrPlayed = Array(self.songs.length).fill(false);
if (self.config.autoPlay){
if (self.config.random){
ind = Math.floor(Math.random() * self.songs.length); //(self.current + 1) % self.songs.length;
arrPlayed[ind] = true;
self.current = ind;
}
self.sendSocketNotification("LOADFILE", self.songs[self.current]);
}
break;
case "Music_File": // this is called everytime a song is sent from the server
//console.log(payload[3]);
if (payload[3] == 'music.png') {
self.album_art.className = "album-art";
self.album_art.classList.add('active');
}
else {
self.album_art.classList.toggle('active');
self.album_art.className = "album-art-found";
var chngstyle = document.querySelector('.album-art-found').style;
chngstyle.setProperty("--backgroundImage", "url('" + payload[3] + "')");
//var mystr = window.getComputedStyle(document.querySelector('.album-art-found'), '::before').getPropertyValue('background-image');
//console.log(mystr);
}

// create url of the raw data received and play it
audioElement=document.getElementById(self.identifier+"_player");
var binaryData = [];
binaryData.push(payload[0]);
var url = window.URL.createObjectURL(new Blob(binaryData, {type: "audio/mpeg"}));
if ((payload[2] = 'mp3') || (payload[2] = 'flac')){
var url = window.URL.createObjectURL(new Blob(binaryData, {type: "audio/mpeg"}));
}
/*else if (payload[2] = 'ogg'){
var url = window.URL.createObjectURL(new Blob(binaryData, {type: "audio/ogg"}));
}*/
else if (payload[2] = 'wav'){
var url = window.URL.createObjectURL(new Blob(binaryData, {type: "audio/wav"}));
}
audioElement.load();
audioElement.setAttribute('src', url);
audioElement.volume = 1;
audioElement.play();
self.artist.innerHTML = payload[1][0];
self.song.innerHTML = payload[1][1];
self.album_art.classList.add('active');
//self.album_art.classList.add('active');
// progress bar (thanks to Michael Foley @ https://codepen.io/mdf/pen/ZWbvBv)
var timer;
var percent = 0;
Expand All @@ -94,18 +136,97 @@ Module.register("MMM-MP3Player",{
}
// next track & loop
audioElement.onended = function() {
if(!self.USBAttached){
if(!self.musicFound){
self.album_art.classList.toggle('active');
return;
}
if(self.current==(self.songs.length-1)){ // this assures the loop
self.current = -1;
if (self.config.random){
if (!arrPlayed.includes(false)){ // if all files are played
if (!self.config.loopList) {
self.artist.innerHTML = "Playlist ended";
self.song.innerHTML = "";
console.log("[MMM-MP3Player] Playlist ended");
return;
}
arrPlayed.fill(false);
}
do {
ind = Math.floor(Math.random() * self.songs.length); //(self.current + 1) % self.songs.length;
} while ( (arrPlayed[ind]) || ((ind == self.current) && (self.songs.length>1)) ); //ind == self.current: not to play one song twice - in the end of list and in the beginning of newly created list)
arrPlayed[ind] = true;
self.current = ind;
}
else {
if(self.current==(self.songs.length-1)){ // if all files are played
if (!self.config.loopList){
self.artist.innerHTML = "Playlist ended";
self.song.innerHTML = "";
console.log("[MMM-MP3Player] Playlist ended");
return;
}
self.current = -1;
}
self.current++;
}
self.current++;
self.sendSocketNotification("LOADFILE", self.songs[self.current]);
};
console.log("Music Played");
};
console.log("[MMM-MP3Player] Music Played");
break;
}
}
},

notificationReceived: function(notification, payload){
var self = this;
if (self.musicFound){
switch(notification){
case "PLAY_MUSIC":
if (audioElement.paused){
audioElement.play();
}
else {
self.sendSocketNotification("LOADFILE", self.songs[self.current]);
}
break;
case "STOP_MUSIC":
audioElement.pause();
break;
case "NEXT_TRACK":
if(!self.musicFound){
self.album_art.classList.toggle('active');
return;
}
if (self.config.random){
if (!arrPlayed.includes(false)){
arrPlayed.fill(false);
}
do {
ind = Math.floor(Math.random() * self.songs.length); // (self.current + 1) % self.songs.length;
} while (arrPlayed[ind] || ind == self.current); // ind == self.current: not to play one song twice - in the end of list and in the beginning of newly created list)
arrPlayed[ind] = true;
self.current = ind;
}
else {
if(self.current==(self.songs.length-1)){ // this assures the loop
self.current = -1;
}
self.current++;
}
self.sendSocketNotification("LOADFILE", self.songs[self.current]);
break;
case "PREVIOUS_TRACK":
if(self.current==0){ // this assures the loop
self.current = (self.songs.length);
}
self.current--;
self.sendSocketNotification("LOADFILE", self.songs[self.current]);
break;
case "RANDOM_ON":
self.config.random = true;
break;
case "RANDOM_OFF":
self.config.random = false;
break;
}
}
}
});
82 changes: 63 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,79 @@
# MMM-MP3Player
A [MagicMirror²](https://github.com/MichMich/MagicMirror/) module for playing music from USB pen drive as soon as it is inserted. A music player object is built inside the module, no iframe is used. The best position is bottom left, although it depends upon the user. The player has nice rounded and shadow borders which adds a nice aesthetic sense to the overall MM. The player then waits for the user to insert/attach a USB pen drive containing the MP3 files. As soon as a USB drive is plugged, the music starts playing!

The module is built with error handling procedures. Beside the core modules, this module uses 3 other dependencies:
## About
A [MagicMirror²](https://github.com/MichMich/MagicMirror/) module for playing music from folder. <br>
This is the version of the [MMM-MP3Player module](https://github.com/asimhsidd/MMM-MP3Player) remade to my needs. <br>
Starting screen: <br>
![picture](Capture1.png) <br>
If no album cover found: <br>
![picture](Capture2.JPG) <br>
Album cover retrieved from ID3-Tags: <br>
![picture](Capture3.png)

* supports .mp3, .flac (surprisingly for me, it can be played as 'audio/mpeg' object as well) and .wav music formats;
* reads ID3 metadata (artist and song title) - if no data, the filename is displayed;
* displays album cover. Player tries to retrieve the image from ID3-Tags, otherwise it searches file `cover.jpg` in the playing music file directory.
* autoplay, random order, loop playlist capability;
* current settings of 'autoPlay' and 'random' options are displayed while loading the module;
* control through notifications (play, stop, next track, prev track, turn random on/off);
* module only supports rock music.

## Dependencies
Beside the core modules, this module uses one dependency:

| Module | URL |
| -----------|-------------------------------------------|
| usb | https://github.com/tessel/node-usb |
| node-id3 | https://www.npmjs.com/package/node-id3 |
| drivelist | https://www.npmjs.com/package/drivelist |

![picture](Capture1.JPG) <br>
![picture](Capture2.JPG)

## Using the module
## Installation

* Navigate to the modules directory via the follow command: `cd MagicMirror/modules`
* Clone the module from github: `git clone https://github.com/asimhsidd/MMM-MP3Player.git`
* Navigate to the MMM-MP3Player directory: `cd MMM-MP3Player`
* Install the dependencies: `npm install`
* Add the following configuration to the modules array in the `config/config.js` file:
* `cd MagicMirror/modules` // change present working directory to the modules folder
* `git clone https://github.com/x3mEr/MMM-MP3Player.git` // clone the module from github
* `cd MMM-MP3Player` // navigate to the MMM-MP3Player directory
* `npm install` // install dependencies
* Add the following configuration to the modules array in the `MagicMirror/config/config.js` file:
```js
modules: [
{
module: 'MMM-MP3Player',
position: 'top_left'
module: "MMM-MP3Player",
position: "top_left",
config: {
musicPath: "modules/MMM-MP3Player/music/",
autoPlay: true,
random: false,
loopList: true,
}
}
]
```
*Do not forget to add extra parameters for other modules (i.e. `classes: "default everyone",` for [face recognition module](https://github.com/nischi/MMM-Face-Reco-DNN))*
* Finally, add some cool music to the `musicPath` folder and enjoy!

## Configuration options for MMM-MP4Player
## Update
* `cd MagicMirror/modules/MMM-MP3Player` // change present working directory to the modules folder
* if you have done local edits, backup the files you have edited and run
`git reset --hard`
* `git pull` // to pull the changes from the repository

| Option | Description
|---------------|-----------
| `position` | *Required* The position of the screencast window. <br>**Options:** `['bottomRight', 'bottomCenter', 'bottomLeft', 'center', 'topRight', 'topCenter', 'topLeft']` <br>**Type:** `string` <br>**Note:** This module config actual sets the location, not the magic mirror position config.
## Configuration

| Option | Description |
| -----------|-------------------------------------------|
| `musicPath` | The path of the folder with music files. <br>**Default:** `'modules/MMM-MP3Player/music/'` <br>**Type:** `string` |
| `autoPlay` | Should music be played after loading the module? <br>**Default:** `true` <br>**Type:** `boolean` |
| `random` | Should music be shuffled? <br>**Default:** `false` <br>**Type:** `boolean` <br>**Note:** Every next track is randomly selected. So after the playlist ends the order of tracks will be another. |
| `loopList` | Loop the tracklist? <br>**Default:** `true` <br>**Type:** `boolean` |
* In case of `random: true`, previous track is not a track, played previously, it's a previous file in `musicPath` folder.

## Control with notifications

The playback can be controlled from another module (e.g. [voicecontrol](https://github.com/alexyak/voicecontrol)) with notifications.
To play track, pause playback, play next or previous track, turn random on/off, following notifications should be send, respectively:
```js
this.sendNotification('PLAY_MUSIC', 'some_info');
this.sendNotification('STOP_MUSIC', 'some_info');
this.sendNotification('NEXT_TRACK', 'some_info');
this.sendNotification('PREVIOUS_TRACK', 'some_info');
this.sendNotification('RANDOM_ON', 'some_info');
this.sendNotification('RANDOM_OFF', 'some_info');
```
21 changes: 21 additions & 0 deletions history.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
**2020.09.14**

- add: album cover from ID3-Tag or file named `cover.jpg` in folder with playing music file;
- add: turn random on/off over notification.

**2020.09.06**

- rm: `drive_path` variable;
- upd: logs.

**2020.09.04**

- add: .flac and .wav file formats support;
- add: `loopList` option;
- upd: README;
- fix: infinite loop when only one file is in music folder.


**2020.08.12**

- module is created.
Empty file.
Loading