UNPKG

itunes-bridge

Version:

A macOS and Windows NodeJS package to control and get informations from iTunes and macOS Music app through AppleScript

402 lines (364 loc) 13.9 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>itunes-bridge.js - Documentation</title> <script src="scripts/prettify/prettify.js"></script> <script src="scripts/prettify/lang-css.js"></script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <input type="checkbox" id="nav-trigger" class="nav-trigger" /> <label for="nav-trigger" class="navicon-button x"> <div class="navicon"></div> </label> <label for="nav-trigger" class="overlay"></label> <nav> <li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Events</li><li class="nav-heading"><span class="nav-item-type type-event">E</span><span class="nav-item-name"><a href="iTunes-bridge.html#event:paused">paused</a></span></li><li class="nav-heading"><span class="nav-item-type type-event">E</span><span class="nav-item-name"><a href="iTunes-bridge.html#event:playing">playing</a></span></li><li class="nav-heading"><span class="nav-item-type type-event">E</span><span class="nav-item-name"><a href="iTunes-bridge.html#event:stopped">stopped</a></span></li><li class="nav-heading"><a href="global.html">Globals</a></li><li class="nav-item"><span class="nav-item-type type-member">M</span><span class="nav-item-name"><a href="global.html"></a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getCurrentTrack">getCurrentTrack</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getPlayerState">getPlayerState</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getPlaylistCount">getPlaylistCount</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getTrack">getTrack</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getTrackCount">getTrackCount</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#isRunning">isRunning</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#pause">pause</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#play">play</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#soundVolume">soundVolume</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#stop">stop</a></span></li> </nav> <div id="main"> <h1 class="page-title">itunes-bridge.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/** * This JavaScript file contains the magic. * * iTunes-bridge * @author AngryKiller * @copyright 2018 * @license GPL-3.0 * */ var exports = module.exports = {}; var fs = require('fs'); var {execSync} = require('child_process'); var events = require('events'); var event = new events.EventEmitter(); var plist = require('plist'); var path = require('path'); var os = require('os'); if(process.platform === "darwin"){ var libPath = path.resolve(os.homedir() + "/Music/iTunes/iTunes Library.xml"); }else if(process.platform === "win32"){ var libPath = path.resolve(os.homedir() + "/My Music/iTunes/iTunes Library.xml"); } var that = this; /** Get informations about the current playing track * @returns {object} * @example {"name":"Business", "artist":"Eminem", "album":"The Eminem Show (Explicit Version)", "mediaKind":"song", "duration":251, "elapsedTime":2, "remainingTime":249, "genre":"Rap/Hip Hop", "releaseYear":2002, "id":2630, "playerState":"playing"} */ exports.getCurrentTrack = function () { if (exports.isRunning()) { try { return runScript('getCurrentTrack', 'fetch', undefined, true); } catch (e) { console.log(e); } } else { return {playerState: "stopped"}; } }; /** * Get the player state * @returns {string} - Possible values: playing, stopped or paused * @example "playing" */ exports.getPlayerState = function() { if (exports.isRunning()) { try { return runScript('getPlayerState', 'fetch', undefined, false); } catch (e) { console.log(e); } } else { return "stopped"; } }; /** * Gets the iTunes sound volume or sets it if there's a parameter (Windows only) * @param volume {int} - Windows only * @returns {int} */ exports.soundVolume = function(volume) { if (exports.isRunning()) { if(volume !== undefined &amp;&amp; !isNaN(volume) &amp;&amp; process.platform === "win32"){ try{ return runScript('setSoundVolume', 'control', volume); }catch(e){ console.log(e); } }else{ try { return runScript('getSoundVolume', 'fetch', undefined, false); } catch (e) { console.log(e); } } }else{ console.log("iTunes is not running"); } }; /** * Tells iTunes to play */ exports.play = function (song) { runScript('play', 'control'); }; /** * Tells iTunes to pause */ exports.pause = function (){ runScript('pause', 'control'); }; /** * Tells iTunes to stop */ exports.stop = function (){ runScript('stop', 'control'); }; /** * Gets informations about a track from the library * @param {int} id - The id of the track * @returns {object} * @example { 'Track ID': 1428, Size: 9019045, 'Total Time': 217103, 'Disc Number': 1, 'Disc Count': 1, 'Track Number': 14, 'Track Count': 16, Year: 2011, BPM: 99, 'Date Modified': 2018-03-18T22:37:46.000Z, 'Date Added': 2018-03-24T14:03:15.000Z, 'Bit Rate': 320, 'Sample Rate': 44100, 'Play Count': 3, 'Play Date': 3604816264, 'Play Date UTC': 2018-03-25T07:51:04.000Z, 'Artwork Count': 1, 'Persistent ID': '535F1580FAEB42E4', 'Track Type': 'File', 'File Folder Count': 5, 'Library Folder Count': 1, Name: 'Ils sont cools', Artist: 'Orelsan, Gringe', 'Album Artist': 'Orelsan', Album: 'Le chant des sirènes', Genre: 'Rap/Hip Hop', Kind: 'Fichier audio MPEG', 'Sort Album': 'chant des sirènes', Location: 'file:///Users/steve/Music/iTunes/iTunes%20Media/Music/Orelsan/Le%20chant%20des%20sire%CC%80nes/14%20Ils%20sont%20cools.mp3' } */ exports.getTrack = function(id) { try { var obj = plist.parse(fs.readFileSync(libPath, 'utf8')); return obj.Tracks[id]; }catch(err){ return "not_found"; } }; /** * Gets the playlist count from the library * @returns {int} */ exports.getPlaylistCount = function () { try { var obj = plist.parse(fs.readFileSync(libPath, 'utf8')); return Object.keys(obj.Playlists).length; } catch (err) { return null; } }; // TODO: Support for arguments in the track count (album, artist, playlist...) /** * Gets the track count from the library * @returns {int} */ exports.getTrackCount = function () { try { var obj = plist.parse(fs.readFileSync(libPath, 'utf8')); return (Object.keys(obj.Tracks).length + 1); } catch (err) { return null; } }; // Starting the event system (track change and player state change) that.currentTrack = null; setInterval(function () { var currentTrack = exports.getCurrentTrack(); if (currentTrack &amp;&amp; that.currentTrack) { // On track change /** * Emits a playing event * * @fires iTunes-bridge#playing */ if (currentTrack.id !== that.currentTrack.id &amp;&amp; currentTrack.playerState === "playing") { that.currentTrack = currentTrack; /** * Playing event * * @event iTunes-bridge#playing * @type {object} * @property {string} type - Indicates whenever the player has been resumed or this is a new track being played. * @property {object} currentTrack - Gives the current track */ event.emit('playing', 'new_track', currentTrack); } /** * Emits a paused event * * @fires iTunes-bridge#paused */ else if (currentTrack.id !== that.currentTrack.id &amp;&amp; currentTrack.playerState === "paused") { that.currentTrack = currentTrack; /** * Paused event * * @event iTunes-bridge#paused * @type {object} * @property {string} type - Indicates whenever the player has been resumed or this is a new track being played. * @property {object} currentTrack - Gives the current track */ event.emit('paused', 'new_track', currentTrack); } /** * Emits a stopped event * * @fires iTunes-bridge#stopped */ else if (currentTrack.id !== that.currentTrack.id &amp;&amp; currentTrack.playerState === "stopped") { that.currentTrack = {"playerState": "stopped"}; /** * Stopped event. * * @event iTunes-bridge#stopped * @type {object} */ event.emit('stopped'); } // On player state change if (currentTrack.playerState !== that.currentTrack.playerState &amp;&amp; currentTrack.id === that.currentTrack.id) { that.currentTrack.playerState = currentTrack.playerState; event.emit(currentTrack.playerState, 'player_state_change', currentTrack); } } else { that.currentTrack = currentTrack; } }, 1500); exports.emitter = event; /** * Function to know if iTunes is running * @returns {boolean} - true or false */ exports.isRunning = function() { if(process.platform === "darwin") { try { execSync('pgrep -x "iTunes"'); return true; } catch (err) { return false; } }else if(process.platform === "win32"){ try { execSync('tasklist | find "iTunes.exe"'); return true; } catch (err) { return false; } } }; function runScript(req, type, args, isJson) { if (process.platform === "darwin"){ var iTunesCtrlScpt = path.join(__dirname, '/jxa/iTunesControl.js'); var iTunesFetcherScpt = path.join(__dirname, '/jxa/iTunesFetcher.js'); switch(type){ case "fetch": { if(isJson) { return JSON.parse(execSync('osascript ' +iTunesFetcherScpt+' ' + req)); }else{ return execSync('osascript ' +iTunesFetcherScpt+' ' + req); } break; } case "control": { try { execSync('osascript '+iTunesCtrlScpt+' ' + req+' '+args); }catch(e){ console.error(e); } break; } } } else if (process.platform === "win32") { var iTunesCtrlScpt = path.join(__dirname, '/wscript/iTunesControl.js'); var iTunesFetcherScpt = path.join(__dirname, '/wscript/iTunesFetcher.js'); switch(type){ case "fetch": { if(isJson) { return JSON.parse(execSync('cscript //Nologo ' + iTunesFetcherScpt + ' ' + req, { encoding: 'utf8'})); }else{ return execSync('cscript //Nologo ' + iTunesFetcherScpt+' ' + req, { encoding: 'utf8'}); } break; } case "control": { try { execSync('cscript //Nologo '+ iTunesCtrlScpt+' ' + req+' '+args); }catch(e){ console.error(e); } break; } } } } // Sends an event on module load // If you're wondering why there's a timeout, that's because if you use this in another module, you will require this file AND THEN load the eventemitter, so if these events are emitted immediately, you will never receive them :') setTimeout(function(){ switch(exports.getCurrentTrack().playerState){ case "playing":{ event.emit('playing', 'new_track', exports.getCurrentTrack()); break; } case "paused":{ event.emit('paused', 'new_track', exports.getCurrentTrack()); break; } case "stopped":{ event.emit('stopped'); break; } } }, 500); </code></pre> </article> </section> </div> <br class="clear"> <footer> Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Sat Jul 27 2019 01:48:17 GMT+0200 (GMT+02:00) using the Minami theme. </footer> <script>prettyPrint();</script> <script src="scripts/linenumber.js"></script> </body> </html>