# \[Extension development\] How to play an album?

**URL:** <https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989>\
**Category:** Misc\
**Created:** [May 6, 2020, 10:32pm UTC](https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989 "2020-05-06T22:32:06Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![bluepuma77](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/bluepuma77/32/1476_2.png) [@bluepuma77](https://discourse.mopidy.com/u/bluepuma77)\
**Post date:** [May 6, 2020, 10:32pm UTC](https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989/1 "2020-05-06T22:32:06Z")

</div>

I would like to play an album from my mopidy frontend extension.

So far it was easy as I just called the external mpc command:

```
def playAlbum(uri):
    doMPC('clear')
    doMPC('add', uri)
    doMPC('play')

playAlbum('spotify:playlist:37i9dQZF1DXbMYUPb05hjJ')

```

How should this be done the right way as an extension?

Thanks  
bluepuma

---

<div class="post-metadata">

**Author:** ![bluepuma77](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/bluepuma77/32/1476_2.png) [@bluepuma77](https://discourse.mopidy.com/u/bluepuma77)\
**Post date:** [May 7, 2020, 8:23am UTC](https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989/2 "2020-05-07T08:23:29Z")

</div>

There is a lot of great documentation on mopidy. Sometimes I just miss some easy snippets on how to do stuff in practice.

[rant on] Bad example, but how do I log? It states `logger = logging.getLogger( __name__ )` in the docs multiple times, but the obvious `log('An Exception occured', e)` is missing in the docs. Python experts might know, but I think the extra line would make life easier for newcomers. [rant off]

Anyway, through our previous discussions I was just blinded, thought I need to look up playlists and add only single tracks. Playing a Spotify album and playlist is as easy as it gets:

```
self.core.tracklist.clear()
self.core.tracklist.add(uris=['spotify:playlist:xyz'])
self.core.playback.play()
```

---

<div class="post-metadata">

**Author:** ![kingosticks](https://avatars.discourse-cdn.com/v4/letter/k/858c86/32.png) [@kingosticks](https://discourse.mopidy.com/u/kingosticks)\
**Post date:** [May 7, 2020, 9:12am UTC](https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989/3 "2020-05-07T09:12:47Z")

</div>

True, we are missing a basic example of actually logging in the docs. You’d normally want to use the namespaced logger instance e.g. `logging.error('some error happened')` or `logging.debug('some debug info')`. We have lots of examples in our codebase of usage. The source code is arguably a large part of the project documentation, we can’t cover everything in the docs pages.

---

<div class="post-metadata">

**Author:** ![kingosticks](https://avatars.discourse-cdn.com/v4/letter/k/858c86/32.png) [@kingosticks](https://discourse.mopidy.com/u/kingosticks)\
**Post date:** [May 7, 2020, 9:18am UTC](https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989/4 "2020-05-07T09:18:01Z")

</div>

Also:

> Keep in mind that the normal core and frontend methods will usually return `pykka.ThreadingFuture` objects, so you will need to add `.get()` at the end of most method calls in order to get to the actual return values.

Another good example to read through is the mpd frontend e.g. [https://github.com/mopidy/mopidy-mpd/blob/master/mopidy\_mpd/protocol/current\_playlist.py](https://github.com/mopidy/mopidy-mpd/blob/master/mopidy_mpd/protocol/current_playlist.py)

---

<div class="post-metadata">

**Author:** ![bluepuma77](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/bluepuma77/32/1476_2.png) [@bluepuma77](https://discourse.mopidy.com/u/bluepuma77)\
**Post date:** [May 7, 2020, 9:26am UTC](https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989/5 "2020-05-07T09:26:48Z")

</div>

For reference, here is example code how to play a folder with MP3s:

```
uri = 'file:/home/pi/music/folder/'

self.core.tracklist.clear()
uris = []
for ref in self.core.library.browse(uri).get():
    uris.append(ref.uri)
self.core.tracklist.add(uris=uris)
self.core.playback.play()

```

It seems the “Files/” prefix from `mpc` can not be used, instead you need the full path.

---

<div class="post-metadata">

**Author:** ![bluepuma77](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.mopidy.com/bluepuma77/32/1476_2.png) [@bluepuma77](https://discourse.mopidy.com/u/bluepuma77)\
**Post date:** [May 7, 2020, 11:51am UTC](https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989/6 "2020-05-07T11:51:11Z")

</div>

Thanks, I took some inspiration from [current\_playlist.py](https://github.com/mopidy/mopidy-mpd/blob/master/mopidy_mpd/protocol/current_playlist.py) for `add` and [dispatcher.py](https://github.com/mopidy/mopidy-mpd/blob/master/mopidy_mpd/dispatcher.py) for `browse`.

I came up with the following minimal code to play folders and Spotify:

```
def playAlbum(uri):
    logger.debug('playAlbum URI: ' + uri)                       

    self.core.tracklist.clear()

    # for mpc compatibility
    if uri.startswith('Files/'):
        uri = uri.replace('Files/', 'file:/home/pi/music/')

    # if uri is a directory, browse and add tracks    
    if os.path.isdir(uri.replace('file://','').replace('file:','')):                        
        logger.debug('Using browse to add directory: ' + uri)   
        for ref in self.core.library.browse(uri).get():
            self.core.tracklist.add(uris=[ref.uri])
    else:
        logger.debug('Add URI directly: ' + uri)                       
        self.core.tracklist.add(uris=[uri])      

    self.core.playback.play()

```

Then you can call it like this:

```
playAlbum('Files/folder')
playAlbum('file:/home/pi/music/folder')
playAlbum('spotify:album:a1b2c3')
playAlbum('spotify:playlist:1a2b3c')

```

Overall I think it would be great if the mopidy file interface could handle folders automagically 🤩

---

<div class="post-metadata">

**Author:** ![kingosticks](https://avatars.discourse-cdn.com/v4/letter/k/858c86/32.png) [@kingosticks](https://discourse.mopidy.com/u/kingosticks)\
**Post date:** [May 7, 2020, 2:05pm UTC](https://discourse.mopidy.com/t/extension-development-how-to-play-an-album/3989/7 "2020-05-07T14:05:09Z")

</div>

```auto
# for mpc compatibility
    if uri.startswith('Files/'):
        uri = uri.replace('Files/', 'file:/home/pi/music/')

```

To be clear, ‘Files’ and ‘Files/foo’ are names and not URIs. A Mopidy URI and it’s form is explained [here](https://docs.mopidy.com/en/latest/api/backend/#uris-and-routing-of-requests-to-the-backend). It’s a bit confusing if you are coming from using MPD where the difference is hidden from you; where MPD uses names there is usually a _mapping_ to the URI occurring at some point. I wouldn’t try to implement the mapping with string replacements:

```auto
playAlbum('Files/MyFiles/TheseFiles/folder')
playAlbum('Files/file:/folder')

```

And what about nested folders? Looks like they are skipped (after failing).

```auto
playAlbum('Files/folder/folder2')

```

There’s an argument for handling directory lookups in Mopidy-File backend but I think it’d be quite slow, maybe try implementing the improvement and see what it’s like to use? You’d want to call `browse` internally anyway, to filter out all the non-track stuff and make it less slow. And would you always want to be recursive? Requiring the usage of `browse` instead, to explicitly first gather the tracks to add, is more work but maybe that simpler interface is better.

I’d rather see `LibraryController.browse` handle track URIs better, it currently causes an exception (a bug).
