From 152ef58e413812b490d556e238502f49a392af67 Mon Sep 17 00:00:00 2001 From: Paoda Date: Fri, 22 Mar 2019 22:01:44 -0700 Subject: [PATCH] Partial Progress with rewriting parts of Song.js Also started work on Writing Storage.js Storage.js manages info across sessions of melodii --- src/App.js | 11 ++- src/components/Body.js | 2 +- src/components/Misc.js | 2 +- src/melodii/MusicPlayer.js | 22 +++--- src/melodii/Song.js | 77 ++++++++++++++++++++- src/melodii/Storage.js | 133 +++++++++++++++++++++++++++++++++++++ 6 files changed, 230 insertions(+), 17 deletions(-) create mode 100644 src/melodii/Storage.js diff --git a/src/App.js b/src/App.js index 3b749f1..8ce5e14 100644 --- a/src/App.js +++ b/src/App.js @@ -24,17 +24,16 @@ const filepath = new Filepath("C:\\Users\\Paoda\\Downloads\\Music"); (async () => { let list = await filepath.getValidFiles(); - let song = new Song(list[~~(Math.random() * list.length)]); - song = await Song.getMetadata(song); - Song.setAlbumArt(song.metadata); + let song = new Song(list[~~(Math.random() * list.length)], true); + await song.getMetadata(); mp.loadSong(song); // mp.play(); mp.element.onended = async () => { - let song = new Song(list[~~(Math.random() * list.length)]); - song = await Song.getMetadata(song); - Song.setAlbumArt(song.metadata); + let song = new Song(list[~~(Math.random() * list.length)], true); + await song.getMetadata(); + mp.loadSong(song); mp.play(); }; diff --git a/src/components/Body.js b/src/components/Body.js index d62a54a..93958a5 100644 --- a/src/components/Body.js +++ b/src/components/Body.js @@ -152,7 +152,7 @@ export default class Body extends React.Component { term = parseInt(term, 10); temp.tbody = table.tbody.filter(obj => obj.year === term); } else { - // type == time + // type === time term = term.toLowerCase(); temp.tbody = table.tbody.filter(obj => obj.time.toLowerCase().includes(term) diff --git a/src/components/Misc.js b/src/components/Misc.js index 663f359..fca1362 100644 --- a/src/components/Misc.js +++ b/src/components/Misc.js @@ -262,7 +262,7 @@ export function sortTable(table, term) { return a.year - b.year; }); } else if (term === "time") { - //term == time convert the time into seconds + //term === time convert the time into seconds //The messy else if + else is becomes a.inSeconds || b.inSeconds can be undefined. tbody.sort((a, b) => { if (a.inSeconds && b.inSeconds) { diff --git a/src/melodii/MusicPlayer.js b/src/melodii/MusicPlayer.js index f816cc3..11a8af9 100644 --- a/src/melodii/MusicPlayer.js +++ b/src/melodii/MusicPlayer.js @@ -49,21 +49,27 @@ export default class MusicPlayer { */ load(playlist) { const song = playlist.next(); - let path = song.location; - try { - this.element.src = this.getURICompatible(path); - this.element.load(); - console.log("'" + path + "'" + " from " + playlist.title + " was succesfully loaded"); - } catch(e) { - console.error(path + " failed to load " + e.name + " from " + playlist.title); - } + song.getMetadata().then(() => { + let path = song.location; + + try { + this.element.src = this.getURICompatible(path); + this.element.load(); + console.log("'" + path + "'" + " from " + playlist.title + " was succesfully loaded"); + } catch(e) { + console.error(path + " failed to load " + e.name + " from " + playlist.title); + } + }) } /** Loads Song * @param {Song} song */ loadSong(song) { + + song.displayAlbumArt(); + if (archive.getCurrentSong() !== undefined) archive.add(archive.getCurrentSong()); let path = song.location; diff --git a/src/melodii/Song.js b/src/melodii/Song.js index 2dcf7fd..94dcab2 100644 --- a/src/melodii/Song.js +++ b/src/melodii/Song.js @@ -9,10 +9,85 @@ export default class Song { * @param {String} path * @param {Boolean} ShouldAlbumArtExist */ - constructor(path, ShouldAlbumArtExist) { //whether album art should exist + constructor(path, shouldCreateAlbumArt = false) { //whether album art should exist this.location = path; + this.title = ""; + this.unf_time = 0; + this.album = ""; + this.year = 0; + this.genres = []; + this.genre = ""; + + this.albumArt = ""; + this.albumArtPresent = false; + + this.shouldCreateAlbumArt = shouldCreateAlbumArt; } + /** + * Gets Metadata from File. + */ + getMetadata() { + return new Promise((res, rej) => { + mm.parseFile(this.location, { native: true, duration: true }).then((metadata) => { + + this.metadata = metadata; + + console.log(metadata); + + const format = metadata.format; + const common = metadata.common; + + + this.title = common.title || ""; + this.unf_time = format.duration || 0; + this.album = common.album || ""; + this.year = common.year || 0; + this.genre = common.genre ? common.genre.toString() : ""; + + if (this.shouldCreateAlbumArt) this.getAlbumArt(common.picture); + + + res(); + }).catch((err) => rej(err)); + }); + } + + /** + * Finds and Loads Album Art. Saves it to Song instance. + * TODO: Move the Event emission to only occur when the song is played. + * @param {*} picture + */ + getAlbumArt(picture) { + if (picture) { + if (picture.length > 0) { + console.log(picture); + const pic = picture[0]; + + this.albumArt = URL.createObjectURL(new Blob([pic.data], { + 'type': pic.format + })); + this.albumArtPresent = true; + } else console.error(this.title + ' Has Album Art, however, no data was found.'); + + } else console.warn(this.title, + ' does not have Album Art.'); + } + + displayAlbumArt() { + console.log("Album Art", this.albumArt); + console.log("Album Art is Present: ", this.albumArtPresent) + if (this.albumArtPresent) Emitter.emit('updateAlbumArt', this.albumart); + else Emitter.emit('updateAlbumArt', noalbumart); + } + + get time() { + let min = ~~((format.duration % 3600) / 60); + let sec = ~~(format.duration % 60); + if (sec < 10) sec = "0" + sec; + return min + ":" + sec; + } + + /** * Gets Metadata from Song * @param {Song} song diff --git a/src/melodii/Storage.js b/src/melodii/Storage.js new file mode 100644 index 0000000..d945217 --- /dev/null +++ b/src/melodii/Storage.js @@ -0,0 +1,133 @@ +import Song from './Song'; +import Filepath from './Filepath' + +/** + * This classs Manages: + * - User Settings + * - Metadata Saving + * - Passing of Metadata to Table Generation + * + * The Goal of This function is to remove the complete dependence of electron-settings + * and the fact that melodii (2019.03.22) relies on Tabe.js for Saving metadata as well as table configurations. + */ +class Storage { + constructor(path) { + this.path = path; + this.ready = false; // Async Actions still need to happen + + /** @type {Array} */ + this.albums = []; + + this.init(); + } + + /** + * Initialize the Storage Instance. + * @async + * @private + */ + async init() { + const filepath = new Filepath(path); + this.knownFiles = await filepath.getValidFiles(); + + this.ready = true; // All prerequisite Async has been done. + } + + + /** + * Goes Through all Known Songs and + * Creates Object Representatinos of all of them. + * + */ + parseSongList() { + const files = this.knownFiles; + + // Need to Iterate over every song, find the name of the album and create an album with that instance. + // Store the album in some array so we know what albums exist and have a method to check whether the album has been + // created or not. Once that is done we can then create a Song based of that, and add it to the album. + + // The end result should be an album class with all the properties filled in + // with attention put towards this.songs which would be an array of Song classes. + + files.forEach(async path => { + const song = new Song(path, true); + + const albumName = song.metadata.common.album; + + if (!albumExists(albumName)) { + const album = new Album(albumName); + album.addSong(song); + + + this.albums.push(album) + } + + + }); + + const self = this; + + /** + * Returns true if album is already present in List of known Albums. + * @param {*} name + */ + function albumExists(name) { + /// Please Write a Better version of this later. + let exists = false; + + for (let i = 0; i < self.albums.length; i++) { + if (self.albums[i].name === name) { + exists = true; + break; + } + } + + return exists; + } + + } + + + + + +} + +/** + * Class Representation of an Album of Songs + * + */ +class Album { + /** @param {String} name */ + constructor(name) { + // Properties Here are based off what is commonly found within Mp3 Tags. + + /** @type {String} */ + this.name + + /** @type {Array} */ + this.songs = []; + + /** Album Artist */ + this.artist = "" + } + + + /** + * Adds Class Representation of a Song to an Album Class + * @param {Song} song + */ + addSong(song) { + this.songs.push(song); + } + + + /** + * Removes a Song Class from the Album + * @param {String} path + */ + removeSong(path) { + console.error("This feature is unimplimented.") + } + +} \ No newline at end of file