npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mipod

v0.1.3

Published

A node module for exposing an MPD API, either as REST service, as Websocket or as a library to include. Written in Typescript, executed in Javascript.

Downloads

9

Readme

mipod

A nodejs module for exposing a REST API and Websocket for the Music Player Daemon (MPD). Written in Typescript, it generates ready-to-use Javascript.

It can be used either as a nodejs dependency module or as a stand-alone server.

Mipod provides a mapping for most of MPD commands (play, pause, add, current and much more). It also provides more advanced library management, you can for instance browse the musics files folder by folder or get all in a go. There are also some non-MPD features such as songs rating.

Installation

You can either grab the sources from github:

Note that it contains both Typescript sources and generated Javascript

or you can install through NPM:

  • npm install mipod

Note that you wouldn't get Typescript sources this way

Usage

Stand-alone REST server

As a stand-alone server, you only need the Javascript files, not Typescript. Run mipod-rest.js with node:

node mipod-rest.js

That's all you need to start the server with default parameters. It will listen to requests on port 80 and connects to a MPD server on localhost:6600. Try out http://localhost/mipod/play or http://localhost/mipod/pause, if you have an MPD server running, you should hear immediate results.

Command-line options are:

  • -p=$X, --port=$X setup server port (default 80)
  • --prefix=$path setup root for REST requests (default empty)
  • --mpdHost=$host MPD server hostname (default localhost)
  • --mpdPort=$X MPD server port (default 6600)
  • --dataPath=$path local path where data files will be stored
  • --dontUseLibCache deactivate MPD caching (will be slower, but saves memory - see dedicated section below for more information).
  • --loadLibOnStartup load the whole library from MPD on startup and refresh its cache.
  • -h, --help help

Stand-alone Websocket server

It's very similar to the REST server. Run:

node mipod-ws.js

And the server will listen on default port (80) for websocket events. Options are the same than for the REST server, only --prefix will differ a bit since it won't prefix the REST resource obviously, but the websocket word event.

Node module inclusion

Since mipod is written in Typescript, you may want to benefit from this and import it in your own typescript code:

    import mipod = require('mipod');

or do the equivalent in Javascript:

    var mipod = require('mipod');

Then, once again, you can either run it as a REST server or as a websocket server.

  • For REST, register routes by calling:

        mipod.asRest(app, opts);

    Where app is your own express application and opts is a set of options equivalent to the command-line arguments described above:

    • dataPath: string
    • useLibCache: boolean
    • prefix: string
    • loadLibOnStartup: boolean
    • mpdHost: string
    • mpdPort: number
  • For websocket:

        mipod.asWebSocket(socket, opts);

    Where socket is a Socket object from ''socket.io''. opts is the same than above.

Commands

| Command | REST | Websocket | Description |---|---|---|---| | Play | GET /play | play | Enter "play" mode | Play path | POST {entry: String} /play | play-entry {entry: String} | Play given file (song or playlist), replacing the current playlist | Play index | GET /playidx/:idx | play-idx {idx: Number} | Play song from playlist at given index | Add | POST {entry: String} /mipod/add | add {entry: String} | Add given file (song or playlist) to current playlist | Clear | GET /clear | clear | Clear current playlist | Pause | GET /pause | pause | Pause current song | Stop | GET /stop | stop | Stop current song | Next | GET /next | next | Next song in playlist | Prev | GET /prev | prev | Previous song in playlist | Volume | GET /volume/:value | volume {value: Number} | Set the volume (from 0 to 100) | Repeat | GET /repeat/:enabled | repeat {enabled: Boolean} | Enable or disable repeat mode (expect 0/1) | Random | GET /random/:enabled | random {enabled: Boolean} | Enable or disable random mode (expect 0/1) | Single | GET /single/:enabled | single {enabled: Boolean} | Enable or disable single mode (expect 0/1) | Consume | GET /consume/:enabled | consume {enabled: Boolean} | Enable or disable consume mode (expect 0/1) | Seek | GET /seek/:songIdx/:posInSong | seek {songIdx: Number, posInSong: Number} | Seek song position | Remove from queue | GET /rmqueue/:songIdx | rmqueue {songIdx: Number} | Remove song from its index in current playlist | Delete playlist | GET /deletelist/:name | deletelist {name: String} | Delete saved playlist | Save playlist | GET /savelist/:name | savelist {name: String} | Save current playlist with given file name | Play all | POST {entries: [String]} /playall | playall {entries: [String]} | Play all songs / playlists from json (replaces current playlist) | Add all | POST {entries: [String]} /addall | addal {entries: [String]} | Add all songs / playlists from json to current playlist | Update | POST {path: String} /update | update {path: String} | Update MPD database on given path (empty path = whole db) | Current | GET /current | current | Get current song info being played | Status | GET /status | status | Get current status (various information, such as active flags, current song id, bitrate, etc.) | Idle | GET /idle | idle | Wait until an event occurs | Notify | n/a | notify | Get status information right now (same as "status"), and get notified each time something changed. It's a combination of "idle" and "status" run in loop. | Playlist info | GET /playlistInfo | playlistInfo (n/a yet!) | Get current playlist content | Playlist item info | GET /playlistInfo/:idx | playlistItemInfo {idx: Number} (n/a yet!) | Get information on a specific item in current playlist | Custom | GET /custom/:command | custom {command: String} | Run a custom MPD command | Load lib once | GET /lib-loadonce | lib-loadonce | Trigger library scan, which will put result in cache and available for "get" calls | Reload lib | GET /lib-reload | lib-reload | Force rescanning the library (clears cache) | Loading progress | GET /lib-progress | lib-progress | Get progress information on library loading. This call will returns a number in range [0, number of songs] | Get library | POST {treeDesc: Maybe [String], leafDesc: Maybe [String]} /lib-get/:start/:count | lib-get {start: Number, count: Number, treeDesc: Maybe [String], leafDesc: Maybe [String]} | Get a map of currently loaded songs, using paginating info providedReturned json is {"status":(status code as String),"finished":(boolean, false if there's still items to pick up),"next":(number, the next item id to pick up),"data":(a map representing data as requested)}"start" is the start item id of requested page. Note that you should use the "next" returned number as subsequent "start" call"count" is the number of items you try to get. Note that you may receive less than "count" items when MPD scanning is still ongoing or if you've reached the total number of items"treeDesc" is the tree descriptor (see dedicated section below). If unset, ["genre","albumArtist|artist","album"] will be used"leafDesc" is the leaf descriptor (see dedicated section below). If unset, all available data will be returned (which may affect performance on large libraries) | Lib push mode | n/a | lib-push {maxBatchSize: Number, treeDesc: Maybe [String], leafDesc: Maybe [String]} | Configure push mode (Websocket only), and receive pushed data (similar to "lib-get") as soon as "loadonce" or "reload" is emitted. | Loading finished | n/a | lib-finished-loading | This websocket event is fired as soon as the library has completely finished to load (one-way event, server to client). | List directory | POST {path: String, leafDesc: Maybe [String]} /lsinfo | lsinfo {token: Maybe Number, path: String, leafDesc: Maybe [String]} | An equivalent method of "lib-get" that returns only a flat reprensentation of a given path. If a token was provided, it's returned in response event (Websocket only). | Search | POST {search: String, leafDesc: Maybe [String]} /search/:mode | search {token: Maybe Number, mode: String, search: String, leafDesc: Maybe [String]} | Search for an MPD entry matching posted given string. "mode" can be any type od data recognized by MPD (check MPD documentation), for instance "file" or "any". If a token was provided, it's returned in response event (Websocket only). | Tag | POST {targets: [{targetType: String, target: String}]} /tag/:tagName/:tagValue? | tag {tagName: String, tagValue: String, targets: [{targetType: String, target: String}]} | Get (if tagValue undefined) or set (if tagValue defined) a custom tag associated to a given target. Expecting POST data: "targetType" refers to a MPD tag (song, artist, album etc.)"target" depends on "targetType": for a song, will be the MPD path for instanceOn websockets, the context parameters are returned in the response event. | Delete tag | DELETE {targets: [{targetType: String, target: String}]} /tag/:tagName | deltag {tagName: String, targets: [{targetType: String, target: String}]} | Delete a given tag. Expecting JSON: "targetType" refers to a MPD tag (song, artist, album etc.)"target" depends on "targetType": for a song, will be the MPD path for instanceOn websockets, the context parameters are returned in the response event.

Tree and leaf descriptors

Some commands sent to MPD will return a list of entries, which are basically directories, playlist files and song files with metadata. Mipod has the ability to organize them the way you want. You would like sometimes to get them as flat lists, or as trees organized by albums, artists, etc. That's what treeDesc and leafDesc are for.

A tree descriptor describes the successive levels of the tree. For instance if you want to get all songs organized by genre, then inside genres organized by artists, and finally by albums, you would write the following tree descriptor (the order matters!): ["genre","artist","album"]. A special character, "|", can be used as a "if exists / else" selector. If instead of "artist" you write "albumArtist|artist", it means that mipod will first search for the album artist of a song, then if it's not found it will search for the artist. You can have more than one pipe in a tree level.

A leaf descriptor describes the final level of the tree (that is, leaves). For instance, if your tree is just ["album"] and your leaf is ["artist","title","track","file"], then a map of albums will be returned, and for each album an array of objects, each containing artist, title, track and file information.

The available names are:

  • file
  • lastModified
  • time
  • artist
  • albumArtist
  • title
  • album
  • track
  • date
  • genre
  • composer

Examples

REST

Examples here use jquery ($.ajax)

Swith to play mode

    $.ajax({
        type: 'GET',
        url: '/play',
        cache: false,
        success: function(data) {},
    });

Play a given file

    $.ajax({
        type: 'POST',
        url: '/play',
        data: JSON.stringify({entry: "some/music/file.mp3"}),
        contentType: "application/json; charset=utf-8",
        success: function(data) {},
    });

Load the full library

    $.get("/lib-loadonce", function(data) {
        loadPage({
            start: 0,
            count: 1000
        });
    }, 'json');

    function loadPage(loadingInfo) {
        $.post('/lib-get/' + loadingInfo.start + '/' + loadingInfo.count, {},
            function(pageInfo) {
                // Process data (add "pageInfo.data" to client layout)
                if (pageInfo.finished) {
                    // Finished..
                } else {
                    loadingInfo.start = pageInfo.next;
                    setTimeout(function() {
                        loadPage(loadingInfo);
                    }, 300);
                }
            }, 'json')
        .fail(function() {
            // Report failure...
            setTimeout(function() {
                loadPage(loadingInfo);
            }, 300);
        });
    }

Websocket

Initializing socket.io

    var socket = io();

Swith to play mode

    socket.emit("play");

Play a given file

    socket.emit("play-entry", {entry: "some/music/file.mp3"});

Load the full library (push mode)

    function startLoading() {
        socket.emit("lib-push", {
            maxBatchSize: 500,
            treeDesc: ["genre", "albumArtist|artist", "album"],
            leafDesc: ["file","track","title"]
        });
        socket.emit("lib-loadonce");
    }

    // Register listeners
    socket.on("lib-finished-loading", function(body) {
        console.log("Finished to scan " + body.nbItems + " items");
    });

    socket.on("lib-push", function(body) {
        console.log("Scanned " + body.progress + " items");
        // Process data (add "body.data" to client layout)
    });
    

License

Copyright 2014 Joël Takvorian, MIT License

Contact

Feel free to report issues on Github or contact me (contact information available on npmjs)