Mopidy is an extensible music server written in Python

Related tags

python music-player mopidy
Overview

Mopidy

Mopidy is an extensible music server written in Python.

Mopidy plays music from local disk, Spotify, SoundCloud, Google Play Music, and more. You edit the playlist from any phone, tablet, or computer using a variety of MPD and web clients.

Stream music from the cloud

Vanilla Mopidy only plays music from files and radio streams. Through extensions, Mopidy can play music from cloud services like Spotify, SoundCloud, and Google Play Music. With Mopidy's extension support, backends for new music sources can be easily added.

Mopidy is just a server

Mopidy is a Python application that runs in a terminal or in the background on Linux computers or Macs that have network connectivity and audio output. Out of the box, Mopidy is an HTTP server. If you install the Mopidy-MPD extension, it becomes an MPD server too. Many additional frontends for controlling Mopidy are available as extensions.

Pick your favorite client

You and the people around you can all connect their favorite MPD or web client to the Mopidy server to search for music and manage the playlist together. With a browser or MPD client, which is available for all popular operating systems, you can control the music from any phone, tablet, or computer.

Mopidy on Raspberry Pi

The Raspberry Pi is an popular device to run Mopidy on, either using Raspbian, Ubuntu, or Arch Linux. Pimoroni recommends Mopidy for use with their Pirate Audio audio gear for Raspberry Pi. Mopidy is also a significant building block in the Pi Musicbox integrated audio jukebox system for Raspberry Pi.

Mopidy is hackable

Mopidy's extension support and Python, JSON-RPC, and JavaScript APIs make Mopidy a perfect base for your projects. In one hack, a Raspberry Pi was embedded in an old cassette player. The buttons and volume control are wired up with GPIO on the Raspberry Pi, and is used to control playback through a custom Mopidy extension. The cassettes have NFC tags used to select playlists from Spotify.

Getting started

To get started with Mopidy, begin by reading the installation docs.

Project resources

Latest PyPI version CI build status Read the Docs build status Test coverage Chat on Zulip
Issues
  • Look into adding a gstreamer fifo sink

    Look into adding a gstreamer fifo sink

    This keeps coming up as people really seem to like ncmpcpp's visualizer

    Trick for making a reliable FIFO sink is twofold. You need to use non-blocking writes, which means opening the write socket will fail unless there is a reader, hence you need a reader as well. Secondly if the buffer fills up because the external reader falls behind or there is none, your internal reader needs to clear the buffer.

    Following code captures the basic gist of this, but still needs some more work with respect to error handling.

    import errno
    import os
    import stat
    
    LINUX_FIFO_BUFFER_SIZE = 65536
    
    
    class FifoStreamer(object):                                                     
        def __init__(self, location):                                               
            self.location = location                                                
            self.reader = None                                                      
            self.writer = None                                                      
    
        def create(self):                                                           
            try:                                                                    
                mode = os.stat(self.location).st_mode                               
                if not stat.S_ISFIFO(mode):                                         
                    raise Exception('File exists but is not a FIFO')                
            except OSError as e:                                                    
                if e.errno == errno.ENOENT:                                         
                    os.mkfifo(self.location)                                        
                else:                                                               
                    raise                                                           
    
            # TODO: wrap in could not open reader / writer?
            self.reader = os.open(self.location, os.O_NONBLOCK | os.O_RDONLY)       
            self.writer = os.open(self.location, os.O_NONBLOCK | os.O_WRONLY)       
    
        def close(self):                                                            
            # TODO: make closing robust
            os.close(self.writer)                                                   
            os.close(self.reader)                                                   
    
        def write(self, data):                                                      
            while data:                                                             
                try:                                                                
                    written = os.write(self.writer, data)                           
                    data = data[written:]                                           
                except OSError as e:                                                
                    if e.errno == errno.EINTR:                                      
                        continue                                                    
                    elif e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):              
                        self.flush()                                                
                    else:                                                           
                        raise                                                       
    
        def flush(self):                                                            
            while True:                                                             
                try:                                                                    
                    if not os.read(self.reader, LINUX_FIFO_BUFFER_SIZE):                             
                        break                                                       
                except OSError as e:                                                
                    if e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):                
                        break                                                       
                    elif e.errno == errno.EINTR:                                    
                        continue                                                    
                    else:                                                           
                        raise 
    

    The code above now needs to be integrated with GStreamer do actually do it's thing. Assuming 0.10 this is actually very much doable, reason I am hesitant is that we are planing a move to 1.x and the gir binding are not suited for making elements in python. In the case of this code in 1.x the problem boils down to not being able to create the pad templates that BaseSink expect to find. Creating this as a plain element might be doable, but you would need to re-implement way to much of the handling BaseSink makes sure you get right.

    import gobject                                                                  
    
    import pygst                                                                    
    pygst.require('0.10')                                                           
    import gst                                                             
    
    
    class FifoSink(gst.BaseSink):                                                   
        __gstdetails__ = (                                                          
            'FifoSink',                                                             
            'Sink',                                                                 
            'Sink designed to handle FIFO output.',                        
            'Mopidy')                                                               
    
        __gsttemplates__ = (gst.PadTemplate('sink', gst.PAD_SINK, gst.PAD_ALWAYS,   
                                            gst.caps_new_any()),)                   
    
        # TODO: don't allow changing location in flight, i.e. create getter/setter
        location = gobject.property(type=str)                                       
    
        def __init__(self):                                                         
            gst.BaseSink.__init__(self)                                             
            self.streamer = None                                                    
    
        def do_start(self):                                                                                               
            self.streamer = FifoStreamer(self.location)                             
            self.streamer.create()                                                  
            return True                                                             
    
        def do_stop(self):                                                          
            self.streamer.close()                                                   
            return True                                                             
    
        def do_render(self, buf):                                                   
            try:                                                                    
                self.streamer.write(bytes(buf))                                     
                return gst.FLOW_OK                                                  
            except OSError as e:                                                    
                self.error("Failed: %s", e)                                         
                return gst.FLOW_ERROR                                               
    
    
    gobject.type_register(FifoSink)                                                 
    gst.element_register(                                                           
        FifoSink, 'fifosink', gst.RANK_MARGINAL)                                    
    
    if __name__ == '__main__':                                                      
        import gobject                                                              
        gobject.threads_init()                                                      
    
        output = """                                                                
    capsfilter caps="audio/x-raw-int,width=16,rate=44100,channels=1" ! 
    tee name=t 
    t. ! queue ! alsasink                                                           
    t. ! queue ! fifosink location=/tmp/test2.fifo                                  
    """                                                                             
    
        sink = gst.parse_bin_from_description(                                      
            output, ghost_unconnected_pads=True)                                    
    
        playbin = gst.element_factory_make('playbin2')                              
        playbin.set_property('audio_sink', sink) 
    

    Note that one problem I ran into testing this was actually forgetting to match the audio format expected by ncmpcpp, so make sure mono/stereo do indeed match up.

    C-enhancement A-audio 
    opened by adamcik 73
  • core: Persist mopidy state between runs. Fix #310

    core: Persist mopidy state between runs. Fix #310

    I implemented writing/reading the core state to/from disk. Please review and comment.

    Instead of using a get_state() method (as recommended in #310), the core controller themselves knows what to export/import.

    Done:

    • moved json export/import write_library()/load_library() from mopidy/local to mopidy/models
    • new core methods save_state(), load_state()
    • save_state(), load_state() accessible via rpc
    • save state to disk at stop
    • load state from disk at start
    • new config: core.restore_state ("off", "load", "play")
    • update documentation
    • Persist following properties:
    mopidy.core.tracklist
        _tl_tracks
        _next_tlid
        get_consume()
        get_random()
        get_repeat()
        get_single()
    mopidy.core.history
        _history
    mopidy.core.playlist
        get_current_tl_track()
        get_time_position()
    mopidy.core.mixer
        get_volume()
    

    Todo:

    • seek to play position does not work. Timing issue.
    • use extra thread to load state from disk at start?
    C-enhancement A-core 
    opened by dublok 51
  • Stop using appsrc for spotify playback.

    Stop using appsrc for spotify playback.

    All playback should be done via gstreamer elements that support the uri interface gstreamer supports. This would greatly simplify a lot of issues as we could fall back to simply always passing the uri to the uridecodebin as is instead of doing funny things like using an appsrc for spotify. I have more thoughts about this and the recent comments in #40 and #38 fit into this.

    Major hurdle right no is that though I know how to write gstreamer elements in python (and C for that matter) it is not yet clear how to implement the URI interface via the python bindings. This might just be a gobject issue more than a gstreamer one, but I'm still looking.

    C-enhancement A-audio 
    opened by adamcik 47
  • javascript objects dissappear

    javascript objects dissappear

    When using the webclient with 0.16 (adapted for changes from the changelog), I get an error stating that the mopidy.playback and mopidy.tracklist objects are not defined. This happens not instantly, but after using the webclient for a while. After that, the Mopidy object is created, but there is no playback or tracklist object. The same behaviour in multiple browsers and on multiple systems. After restarting mopidy, the objects are back again, this is the object in the browser: http://pastebin.com/DW9t0mcD

    C-bug A-http 
    opened by woutervanwijk 43
  • Next track is loaded in currently playing track / Mopidy never changes track

    Next track is loaded in currently playing track / Mopidy never changes track

    I'm having an issue where the track never changes track. The next track just starts playing after the current track ends, but continues past the total time of the current track. Eventually, it realizes that the track it finished and just stops playing. This happens no matter what output or mixer I use. It might be hard to picture what I'm saying, so attached is a picture of what it looks like from ncmpcpp.

    Note: mopidy is currently playing the next track, Unsre Stärke heißt zu schwach, despite displaying the previous track, Verstummt!

    2016-06-13-141459_1146x63_scrot

    Unfortunately, I can't track down relevant data from the mopidy log. It seems to simply not realize that the track has changed, thus it does not log any change there.

    Note that I can manually change the track with mpc next etc. Also, if it reaches the end of the playlist while still displaying the first song selected, it will end normally with this in the log:

    DEBUG 2016-06-13 14:13:34,350 [29984:MpdSession-13] mopidy.mpd.session Request from [::ffff:50.76.48.109]:5684: idle

    Version: Mopidy 2.0.0

    Extensions tested:

    • Mopidy-GMusic (1.0.0)
    • Mopidy-SoundCloud (2.0.2)
    • Mopidy-Youtube (2.0.2)

    Edit: It eventually gets to this:

    DEBUG    2016-06-13 14:47:26,482 [29984:MpdSession-18] mopidy.mpd.session
      Request from [::ffff:50.76.48.109]:25131: status
    DEBUG    2016-06-13 14:47:26,486 [29984:MpdSession-18] mopidy.mpd.session
      Response to [::ffff:50.76.48.109]:25131: 
        volume: 100
        repeat: 0
        random: 0
        single: 0
        consume: 0
        playlist: 7
        playlistlength: 4
        xfade: 0
        state: play
        song: 0
        songid: 3
        time: 523:215
        elapsed: 523.607
        bitrate: 320
        OK
    DEBUG    2016-06-13 14:47:26,582 [29984:MainThread] mopidy.audio.gst
      Got TAG bus message: tags={'audio-codec': [u'MPEG-1 Layer 3 (MP3)'], 'bitrate': [320000], 'has-crc': [False], 'channel-mode': [u'stereo']}
    DEBUG    2016-06-13 14:47:26,584 [29984:MainThread] mopidy.audio.gst
      Got TAG bus message: tags={'audio-codec': [u'MPEG-1 Layer 3 (MP3)'], 'bitrate': [320000], 'channel-mode': [u'joint-stereo']}
    DEBUG    2016-06-13 14:47:26,620 [29984:MpdSession-18] mopidy.mpd.session
      Request from [::ffff:50.76.48.109]:25131: idle
    DEBUG    2016-06-13 14:47:26,791 [29984:MainThread] mopidy.audio.gst
      Got TAG bus message: tags={'audio-codec': [u'MPEG-1 Layer 3 (MP3)'], 'minimum-bitrate': [320031], 'bitrate': [320000], 'maximum-bitrate': [320031], 'channel-mode': [u'joint-stereo']}
    DEBUG    2016-06-13 14:47:26,843 [29984:MainThread] mopidy.audio.gst
      Got TAG bus message: tags={'audio-codec': [u'MPEG-1 Layer 3 (MP3)'], 'minimum-bitrate': [319725], 'bitrate': [320000], 'maximum-bitrate': [320031], 'channel-mode': [u'joint-stereo']}
    DEBUG    2016-06-13 14:47:27,624 [29984:MpdSession-18] mopidy.mpd.session
      Request from [::ffff:50.76.48.109]:25131: noidle
    DEBUG    2016-06-13 14:47:27,627 [29984:MpdSession-18] mopidy.mpd.session
      Response to [::ffff:50.76.48.109]:25131: OK
    DEBUG    2016-06-13 14:47:27,755 [29984:MpdSession-18] mopidy.mpd.session
      Request from [::ffff:50.76.48.109]:25131: status
    DEBUG    2016-06-13 14:47:27,759 [29984:MpdSession-18] mopidy.mpd.session
      Response to [::ffff:50.76.48.109]:25131: 
        volume: 100
        repeat: 0
        random: 0
        single: 0
        consume: 0
        playlist: 7
        playlistlength: 4
        xfade: 0
        state: play
        song: 0
        songid: 3
        time: 2:215
        elapsed: 2.194
        bitrate: 320
        OK
    

    where the song that it started playing initially just restarts.

    C-bug A-core 
    opened by jludwig 39
  • Apparent memory leak during normal MP3 playback

    Apparent memory leak during normal MP3 playback

    I have what appears to be a memory leak on a Mopidy installation on a RasPi, booting and running over NFS. Initially I thought the problem was that I had no swap set up, so I added some, but it continues to happen.

    The extremely odd thing about this is that the NFS root image is identical to three other RasPi servers handling sound systems in other rooms of the house, but those other three systems have run for weeks without exhibiting any issues. The failing unit freezes up nearly once a day. The only difference among all four servers is the exact content of the music library, but all libraries are pretty small...less than 2k songs, far less in some cases. All songs are in MP3 format, in case it makes a difference.

    Here's a screenshot of top that I had left running to see the system state when it freezes. Note the insane load and 0k free swap, and the fact that kswapd is pegging the CPU, but also that mopidy is the one using all the memory. Normally the mopidy process is down around 5-6% mem usage.

    image

    I'm already using a local SQLite db. I can run a local scan from the command line with no issues.

    After the above screenshot, I power-cycled the RasPi and got back into a top session so I could pay more attention to it. The %MEM value reported has slowly crept upward from about 5% to 42%, where it is now. I expect it will continue to rise if I do nothing.

    In contrast, one of the stable RasPi audio servers has been running for over two weeks solid and playing audio for at least 72 hours without stopping, and the mopidy process is at 6.7% usage. It even has a slightly larger library.

    ...and after a while longer, the memory use did indeed continue to creep upwards (at 50% when I acted), and I ran sudo service mopidy force-reload as a test to see if that would help. It did--mopidy memory usage dropped down near zero for a moment, and then back to the expected ~5%. No full service restart needed, though I assume that also would have done the same.

    The different RasPis involved all report Raspberry Pi 3 Model B Rev 1.2 from the output of cat /sys/firmware/devicetree/base/model. They are running over NFS mounted root folders that are exact copies of each other, except for the hostname. The contents of /etc/mopidy/mopidy.conf are identical. The memory usage takes many hours to grow significantly, but it will eventually eat up all available RAM and swap space.

    I have confirmed as well that the memory use stops increasing when audio is stopped (not sure about paused though). The following set of graphs shows RAM and SWAP usage on the problem system over a 24-hour period, including two full PLAY-STOP cycles (hours long) with one final PLAY event at the end to confirm. I did another force-reload immediately after this to avoid a complete system freeze and drop RAM/swap usage back down to normal levels.

    image

    You can see how the RAM is eaten up first (from 50% to 80%) during the first cycle, then shortly after the second cycle starts, it eats the last bit of safe RAM (from 80% to 85%) and then switches to consuming swap (from 20% to 80%) until stopped. Finally, it ticks up again at a consistent rate about 15 minutes before the end of the graph, which is where I started playback again.

    Note, as far as I can tell, pausing playback in the Mopidy/moped web interface has the same effect as stopping it in this regard, i.e. it stops eating memory.

    Host platform is Raspbian on a RasPi 3B v1.2:

    Linux lucas-audio 4.19.66-v7+ #1253 SMP Thu Aug 15 11:49:46 BST 2019 armv7l GNU/Linux
    

    It is possible that this issue is related to either #1750 (most likely) or #1648 (less likely).

    C-bug A-audio 
    opened by jrowberg 37
  • Early Python 2.6.x doesn't play nice with unicode_literals

    Early Python 2.6.x doesn't play nice with unicode_literals

    Mopidy won't start on my SLES11-SP2 (Python 2.6) and openSUSE 11.4 (Python 2.7) systems.

    When starting Mopidy, it fails with the following error:

    [email protected]:~$  mopidy
    Traceback (most recent call last):
      File "/usr/local/bin/mopidy", line 5, in <module>
        main()
      File "/usr/local/lib64/python2.6/site-packages/mopidy/__main__.py", line 48, in main
        options = parse_options()
      File "/usr/local/lib64/python2.6/site-packages/mopidy/__main__.py", line 85, in parse_options
        help='show GStreamer help options')
      File "/usr/lib64/python2.6/optparse.py", line 1012, in add_option
        raise TypeError, "invalid arguments"
    TypeError: invalid arguments
    [email protected]:~$  python --version
    Python 2.6
    [email protected]:~$  uname -a
    Linux faust 3.0.13-0.27-default #1 SMP Wed Feb 15 13:33:49 UTC 2012 (d73692b) x86_64 x86_64 x86_64 GNU/Linux
    [email protected]:~$  cat /etc/SuSE-release 
    SUSE Linux Enterprise Server 11 (x86_64)
    VERSION = 11
    PATCHLEVEL = 2
    [email protected]:~$  
    

    I believe it has something to do with unicode string handling, but I'm no Python expert, so I got stuck.

    C-bug 
    opened by edooper 34
  • ASX playlists are not correctly typefound

    ASX playlists are not correctly typefound

    For some radio stations the Tunein API provides a URL to a playlist file which needs to be downloaded and parsed in order to extract the actual stream URIs (e.g. http://www.bbc.co.uk/radio/listen/live/r2.asx). The code to do this already exists in https://github.com/mopidy/mopidy/tree/develop/mopidy/audio for scanning local playlists but I wanted to reuse this for these remote playlists. I know the stream extension just provides Scanner.scan() with a playable stream URI and it's happy, but I when I try to give it a remote playlist URI (like the BBC one above) it can't handle it. Unfortunately I don't have the exact exception message to hand.

    I don't understand what goes on inside gstreamer than doesn't differentiate between playing local and remote music files, but does have a problem with remote vs local playlist files. Why can't it just use the various playlist handlers that are registered?

    I could fetch the playlist data myself, wrap it in a file like object and give that to Scanner.scan() - similar to what the tests do - or call the parse_asx|pls|m3u functions directly. Failing that I could just duplicate the playlist parsing code in the extension but that's horrible. What's the best thing to do here?

    C-enhancement A-audio 
    opened by kingosticks 34
  • [Solved] USB: Distorted Audio via gstreamer to alsa on Raspberry Pi

    [Solved] USB: Distorted Audio via gstreamer to alsa on Raspberry Pi

    Hello all,

    I've been playing with mopidy on my my Rpi. I am trying to play music from Spotify through mopidy via Alsa, while a squeezelite client is also active. Thus, I activated dmix from Alsa to enable simultaneous use of the USB DAC by more than one app. Pulseaudio was giving me all kinds of headaches in this combination.

    What I noticed is that squeezelite has not problems with Audio quality going through dmix, wherease mopidy produces crackling sounds and dropouts. squeezelite did the same thing until I adjusted the alsa buffer to 160ms. So basicallly my setup works, but the audio quality of mopidy is not so great.

    • Is there any method to specify an (alsa) buffer for the gstreamer pipeline?
    • Or can you sent to alsa directly without gstreamer?

    I suspect some problem along the mopidy-spotify -> gstreamer -> alsa -> dmix pipeline - possibly a too small buffer or up-sample problem.

    I'd like to avoid using pulseaudio because I couldn't get it to work in my setup. I have not tried jackd1, but I am not sure if squeezelite would play ball.

    A-audio 
    opened by Gymnae 33
  • "Segmentation fault" on startup on Raspberry Pi

    I installed the latest version of mopidy from git on a Rasbperry Pi (Debian Squeeze for ARM). Before, I installed the following dependencies:

    • Pykka 0.14 (via pip install pykka)
    • gstreamer 0.10 (via apt-get install ...)
    • libspotify 12.1.51 eabi-armv6t (downloaded from spotify and successfully built)
    • pyspotify 1.7 (latest from git)

    Then I installed Mopidy 0.7.2 (latest from git).

    When I start Mopidy I run into a segmentation fault:

    INFO     Starting Mopidy 0.7.2 on Linux-3.1.9+-armv6l-with-debian-6.0.4 CPython 2.6.6
    INFO     Mopidy uses SPOTIFY(R) CORE
    INFO     MPD server running at [127.0.0.1]:6600
    INFO     Disabled: mopidy.frontends.lastfm.LastfmFrontend (No module named pylast)
    INFO     Disabled: mopidy.frontends.mpris.MprisFrontend (No module named dbus)
    INFO     Connected to Spotify
    Segmentation fault
    

    What's going wrong here? Is it a general bug or something with the versions I use? The apt repo doesn't seem to work for Debian/ARM.

    opened by ghost 33
  • Add new config types, improve existing types

    Add new config types, improve existing types

    • Added new Float type. It's very similar to the existing Integer type, but accepts floating point numbers, and doesn't allow you to set choices.
    • Added new Pair type, for when you want to accept a pair of values in a single config setting. Pairs default to a pair of strings, but you can set either subtype to whatever type you want (including nested pairs).
    • Added transformer property to String type. This allows you to apply an arbitrary transformation to individual config settings that could utimately be anything at all.
    • If a transformation is applied to a String config value, it will now return a subclass of the built-in str type. This subclass stores the original value as well as the transformed value, so that it can be re-serialised correctly.
    • The "Expected Path" type originally created for the Path type is (for now) just a subclass of the "Transformed Value" class, due to the duplicate implementation. In future, this should be updated to return pathlib Path objects, not strings.
    • Made Secret type pass through the transformer property correctly to its String parent class.
    • Lists can now return a frozenset instead of a tuple, configured by the "unique" property.
    • Lists now accept an arbitrary subtype, similar to the Pair type. This can be any type at all, including Pairs.
    • Resolved a couple of todo comments regarding missing tests for the List type.
    • The Path type will now correctly serialise None to an empty string.
    opened by djmattyg007 2
  • Add core status function

    Add core status function

    In situations where it isn't practical to use websockets, the only reliable way to retrieve the current status of the server is to poll to the JSON-RPC endpoint. Unfortunately, there hasn't been a single unified "get status of the server" function up until now. In order to determine whether or not "repeat", "random", "single" and "consume" are enabled, you would need to make four separate requests. Now, you will only need to request a single endpoint.

    The output was modelled on the existing "status" endpoint provided by the MPD frontend. It includes a little more information out of the box, and isn't aiming to be 100% compatible with what MPD provides. It is expected that once this change has been released, an update can be made to the MPD frontend to utilise this, rather than request all the different functions itself.

    opened by djmattyg007 0
  • Fix/seek when stopped

    Fix/seek when stopped

    null

    A-core 
    opened by kingosticks 1
  • Seeking when stopped does not go into PLAYING state

    Seeking when stopped does not go into PLAYING state

    Describe the bug After performing a seek in the STOPPED state, playback starts at the correct place but Mopidy's state is still STOPPED and commands like stop and pause do not work.

    How to reproduce

    1. Stop playback
    2. Seek somewhere in the track (File or Spotify backend)
    3. Pause playback

    Expected behaviour Playback should pause.

    Environment

    • Raspberry Pi OS Lite
    • Running latest release as a service

    Additional context Log shows GST state changing to PLAYING but Mopidy's state does not i.e. no mopidy.core.playback Changing state: stopped -> playing message.

    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MpdSession-17] mopidy_mpd.session Request from [::1]:41370: seekcur "30"
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Audio-3] mopidy.audio.gst Changing state to GST_STATE_READY: result=GST_STATE_CHANGE_SUCCESS
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.audio.gst Got STATE_CHANGED bus message: old=GST_STATE_NULL new=GST_STATE_READY pending=GST_STATE_VOID_PENDING
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Audio-3] mopidy.audio.gst Sending TAG event for track 'spotify:track:5wnITloAfJ33x0m1R0sQwF': 'taglist, artist=(string)"DON\\ BROCO", title=(string)"One\\ True\\ Prince", album=(string)"One\\ True\\ Prince";'
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Audio-3] mopidy.audio.gst Got source-setup signal: element=GstAppSrc
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Audio-3] mopidy.audio.gst Changing state to GST_STATE_PLAYING: result=GST_STATE_CHANGE_ASYNC
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MpdSession-17] mopidy_mpd.session Response to [::1]:41370: OK
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.audio.gst Got STREAM_START bus message
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.audio.actor Audio event: stream_changed(uri='appsrc://')
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.listener Sending stream_changed to AudioListener: {'uri': 'appsrc://'}
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Audio-3] mopidy.audio.gst Sending flushing seek: position=30000000000
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Dummy-19] mopidy.audio.gst Got SEGMENT pad event: rate=1.0 format=time start=30000000000 stop=18446744073709551615 position=30000000000
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Dummy-19] mopidy.audio.actor Audio event: position_changed(position=30000)
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Dummy-19] mopidy.listener Sending position_changed to AudioListener: {'position': 30000}
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Core-12] mopidy.core.playback Triggering seeked event
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [Core-12] mopidy.listener Sending seeked to CoreListener: {'time_position': 30000}
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MpdFrontend-15] mopidy.listener Sending player to MpdSession: {}
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.audio.gst Got STATE_CHANGED bus message: old=GST_STATE_READY new=GST_STATE_PAUSED pending=GST_STATE_PLAYING
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.audio.gst Got ASYNC_DONE bus message.
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.audio.gst Got STATE_CHANGED bus message: old=GST_STATE_PAUSED new=GST_STATE_PLAYING pending=GST_STATE_VOID_PENDING
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.audio.actor Audio event: state_changed(old_state=stopped, new_state=playing, target_state=None)
    Aug 09 16:16:39 testpi mopidy[18939]: DEBUG    [MainThread] mopidy.listener Sending state_changed to AudioListener: {'old_state': 'stopped', 'new_state': 'playing', 'target_state': None}
    

    Compared to just starting playback (no seek):

    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MpdSession-17] mopidy_mpd.session Request from [::1]:41528: play
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Audio-3] mopidy.audio.gst Changing state to GST_STATE_READY: result=GST_STATE_CHANGE_SUCCESS
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.gst Got STATE_CHANGED bus message: old=GST_STATE_NULL new=GST_STATE_READY pending=GST_STATE_VOID_PENDING
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Audio-3] mopidy.audio.gst Sending TAG event for track 'spotify:track:5wnITloAfJ33x0m1R0sQwF': 'taglist, artist=(string)"DON\\ BROCO", title=(string)"One\\ True\\ Prince", album=(string)"One\\ True\\ Prince";'
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Audio-3] mopidy.audio.gst Got source-setup signal: element=GstAppSrc
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Audio-3] mopidy.audio.gst Changing state to GST_STATE_PLAYING: result=GST_STATE_CHANGE_ASYNC
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MpdSession-17] mopidy_mpd.session Response to [::1]:41528: OK
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.gst Got STREAM_START bus message
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.actor Audio event: stream_changed(uri='appsrc://')
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.listener Sending stream_changed to AudioListener: {'uri': 'appsrc://'}
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Core-12] mopidy.core.playback Changing state: stopped -> playing
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Core-12] mopidy.core.playback Triggering playback state change event
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Core-12] mopidy.listener Sending playback_state_changed to CoreListener: {'old_state': 'stopped', 'new_state': 'playing'}
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Core-12] mopidy.core.playback Triggering track playback started event
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Core-12] mopidy.listener Sending track_playback_started to CoreListener: {'tl_track': TlTrack(tlid=1, track=Track(album=Album(artists=[Artist(name='DON BROCO', uri='spotify:artist:1aOt6LvXOV6I8dv1A5Diia')], date='2021', name='One True Prince', uri='spotify:album:4PzJRdK8unANl6KqYrFjv1'), artists=[Artist(name='DON BROCO', uri='spotify:artist:1aOt6LvXOV6I8dv1A5Diia')], bitrate=320, date='2021', disc_no=0, length=239000, name='One True Prince', track_no=1, uri='spotify:track:5wnITloAfJ33x0m1R0sQwF'))}
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MpdFrontend-15] mopidy.listener Sending player to MpdSession: {}
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Dummy-19] mopidy.audio.gst Got SEGMENT pad event: rate=1.0 format=time start=0 stop=18446744073709551615 position=0
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Dummy-19] mopidy.audio.actor Audio event: position_changed(position=0)
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [Dummy-19] mopidy.listener Sending position_changed to AudioListener: {'position': 0}
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.gst Got TAG bus message: tags={'artist': ['DON BROCO'], 'title': ['One True Prince'], 'album': ['One True Prince']}
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.actor Audio event: tags_changed(tags=['artist', 'title', 'album'])
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.listener Sending tags_changed to AudioListener: {'tags': ['artist', 'title', 'album']}
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.gst Got STATE_CHANGED bus message: old=GST_STATE_READY new=GST_STATE_PAUSED pending=GST_STATE_PLAYING
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.gst Got ASYNC_DONE bus message.
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.gst Got STATE_CHANGED bus message: old=GST_STATE_PAUSED new=GST_STATE_PLAYING pending=GST_STATE_VOID_PENDING
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.audio.actor Audio event: state_changed(old_state=stopped, new_state=playing, target_state=None)
    Aug 09 16:21:32 testpi mopidy[20058]: DEBUG    [MainThread] mopidy.listener Sending state_changed to AudioListener: {'old_state': 'stopped', 'new_state': 'playing', 'target_state': None}
    

    Note, we do not have a test that exercises this.

    opened by kingosticks 2
  • Add an external URI field to models

    Add an external URI field to models

    Tracks, Playlists, Artists, Albums from online streaming services usually have associated URI to view the resource on the service's website/app. It would be good if we could expose these by adding them to our models or through a new API (similar to get_images).

    Originally from https://github.com/jaedb/Iris/issues/754

    A-core A-models 
    opened by kingosticks 1
  • Support getting the extension version from installation metadata

    Support getting the extension version from installation metadata

    As well as interrogating the Extension subclass for a version attribute, support looking at the installed package metadata.

    This data is already available (it's stored next to the entry points data), and every package is guaranteed to have it.

    This will reduce the amount of boilerplate needed in extensions (because you don't need to copy the version number to so many places) and will make it easier to support other packaging systems (built on pyproject.toml and PEP 518) and versioning styles (such as automatic calver).

    I am not suggesting that the documentation be adjusted to use poetry or whatever. However, for those of us with prior Python experience, being able to do this is nice.

    Work around (from 3.8 and above, or using importlib-metadata):

    class Extension(ext.Extension):
    
        dist_name = 'Mopidy-Soundspot'
        ext_name = 'soundspot'
        version = importlib.metadata.version(dist_name)
    
    opened by AstraLuma 3
  • Add ability to exclude backends during search

    Add ability to exclude backends during search

    This lets you exclude just a single search results provider, rather than having to manually select all providers you do want search results from. This will be useful in situations where it isn't possible for human users to select which backends they want results from, such as through the MPD frontend.

    I also took the chance to rename the function 'get_backends_to_uris' to 'get_backends_for_uris', because it's a more accurate representation of what it does.

    opened by djmattyg007 9
  • Support magic values for adding tracks at a specific position

    Support magic values for adding tracks at a specific position

    With this change, you can add tracks directly below the currently playing track without first having to retrieve this value yourself. When used over Mopidy's HTTP interfaces, this reduces the chance that the tracks you're adding will accidentally be skipped over between your two requests.

    Two other magic values have been added that allow you to more explicitly target the start or end of the tracklist.

    I noticed there were no existing tests for the at_position argument, so I added some at the same time to handle other existing cases in addition to the new cases.

    opened by djmattyg007 9
  • mopidy as service comes up in wrong state

    mopidy as service comes up in wrong state

    mopidy as service spawns too soon if it can't access youtube or spotify at startup it can't play from them even later (until restart)

    I had to workaround mopidy.service like this:

    ExecStart=sh -c 'c=0; r=1; while [ $c -lt 20 -a $r -ne 0 ] ; do ping -c 1 -n -w 1 spotify.com; r=$?; c=expr $c + 1; sleep 5; done; /usr/bin/mopidy --config /usr/share/mopidy/conf.d:/etc/mopidy/mopidy.conf'

    opened by mgrouch 2
  • Sleep timer/auto-stop play capabilities/extension

    Sleep timer/auto-stop play capabilities/extension

    So as per previous issue/PR (https://github.com/mopidy/mopidy/issues/956 and https://github.com/mopidy/mopidy/pull/1085), there was an attempt quite some time ago to add a sleep timer to Mopidy core, ended up not getting merged as it was determined should be done as an Extension. There is an Alarm Clock extension, but still no Sleep Timer. Which surprises me. Would love to get a timer to turn pause/stop the audio.

    C-extension-request 
    opened by Drizzt321 1
Releases(v3.2.0)
  • v3.2.0(Jul 7, 2021)

  • v3.1.1(Dec 26, 2020)

  • v3.1.0(Dec 15, 2020)

  • v3.0.2(Jun 18, 2020)

  • v0.19.3(Aug 13, 2014)

    Bug fix release.

    • Audio: Fix negative track length for radio streams. (Fixes: #662, PR: #796)
    • Audio: Tell GStreamer to not pick Jack sink. (Fixes: #604)
    • Zeroconf: Fix discovery by adding .local to the announced hostname. (PR: #795)
    • Zeroconf: Fix intermittent DBus/Avahi exception.
    • Extensions: Fail early if trying to setup an extension which doesn’t implement the mopidy.ext.Extension.setup() method. (Fixes: #813)
    Source code(tar.gz)
    Source code(zip)
  • v0.19.2(Aug 13, 2014)

    Bug fix release, directly from the Mopidy development sprint at EuroPython 2014 in Berlin.

    • Audio: Make audio/mixer_volume work on the software mixer again. This was broken with the mixer changes in 0.19.0. (Fixes: #791)
    • HTTP frontend: When using Tornado 4.0, allow WebSocket requests from other hosts. (Fixes: #788)
    • MPD frontend: Fix crash when MPD commands are called with the wrong number of arguments. This was broken with the MPD command changes in 0.19.0. (Fixes: #789)
    Source code(tar.gz)
    Source code(zip)
  • v0.19.1(Aug 13, 2014)

    Bug fix release.

    • Dependencies: Mopidy now requires Tornado >= 2.3, instead of >= 3.1. This should make Mopidy continue to work on Debian/Raspbian stable, where Tornado 2.3 is the newest version available.
    • HTTP frontend: Add missing string interpolation placeholder.
    • Development: mopidy --version and mopidy.core.Core.get_version() now returns the correct version when Mopidy is run from a Git repo other than Mopidy’s own. (Related to #706)
    Source code(tar.gz)
    Source code(zip)
  • v0.19.0(Jul 21, 2014)

    The focus of 0.19 have been on improving the MPD implementation, replacing GStreamer mixers with our own mixer API, and on making web clients installable with pip, like any other Mopidy extension.

    Since the release of 0.18, we’ve closed or merged 53 issues and pull requests through about 445 commits by 12 people, including five new guys. Thanks to everyone that has contributed!

    Dependencies

    • Mopidy now requires Tornado >= 3.1.
    • Mopidy no longer requires CherryPy or ws4py. Previously, these were optional dependencies required for the HTTP frontend to work.

    Backend API

    • Breaking change: Imports of the backend API from mopidy.backends no longer works. The new API introuced in v0.18 is now required. Most extensions already use the new API location.

    Commands

    • The mopidy-convert-config tool for migrating the setings.py configuration file used by Mopidy up until 0.14 to the new config file format has been removed after over a year of trusty service. If you still need to convert your old settings.py configuration file, do so using and older release, like Mopidy 0.18, or migrate the configuration to the new format by hand.

    Configuration

    • Add optional=True support to mopidy.config.Boolean.

    Logging

    • Fix proper decoding of exception messages that depends on the user’s locale.
    • Colorize logs depending on log level. This can be turned off with the new logging/color configuration. (Fixes: #772)

    Extension support

    • Breaking change: Removed the Extension methods that were deprecated in 0.18: get_backend_classes(), get_frontend_classes(), and register_gstreamer_elements(). Use mopidy.ext.Extension.setup() instead, as most extensions already do.

    Audio

    • Breaking change: Removed support for GStreamer mixers. GStreamer 1.x does not support volume control, so we changed to use software mixing by default in v0.17.0. Now, we’re removing support for all other GStreamer mixers and are reintroducing mixers as something extensions can provide independently of GStreamer. (Fixes: #665, PR: #760)
    • Breaking change: Changed the audio/mixer config value to refer to Mopidy mixer extensions instead of GStreamer mixers. The default value, software, still has the same behavior. All other values will either no longer work or will at the very least require you to install an additional extension.
    • Changed the audio/mixer_volume config value behavior from affecting GStreamer mixers to affecting Mopidy mixer extensions instead. The end result should be the same without any changes to this config value.
    • Deprecated the audio/mixer_track config value. This config value is no longer in use. Mixer extensions that need additional configuration handle this themselves.
    • Use Proxy configuration when streaming media from the Internet. (Partly fixing #390)
    • Fix proper decoding of exception messages that depends on the user’s locale.
    • Fix recognition of ASX and XSPF playlists with tags in all caps or with carriage return line endings. (Fixes: #687)
    • Support simpler ASX playlist variant with elements without children.
    • Added target_state attribute to the audio layer’s state_changed() event. Currently, it is None except when we’re paused because of buffering. Then the new field exposes our target state after buffering has completed.

    Mixers

    • Added new mopidy.mixer.Mixer API which can be implemented by extensions.
    • Created a bundled extension, Mopidy-SoftwareMixer, for controlling volume in software in GStreamer’s pipeline. This is Mopidy’s default mixer. To use this mixer, set the audio/mixer config value to software.
    • Created an external extension, Mopidy-ALSAMixer, for controlling volume with hardware through ALSA. To use this mixer, install the extension, and set the audio/mixer config value to alsamixer.

    HTTP frontend

    • CherryPy and ws4py have been replaced with Tornado. This will hopefully reduce CPU usage on OS X (#445) and improve error handling in corner cases, like when returning from suspend (#718).
    • Added support for packaging web clients as Mopidy extensions and installing them using pip. See the HTTP server side API for details. (Fixes: #440)
    • Added web page at /mopidy/ which lists all web clients installed as Mopidy extensions. (Fixes: #440)
    • Added support for extending the HTTP frontend with additional server side functionality. See HTTP server side API for details.
    • Exposed the core API using HTTP POST requests with JSON-RPC payloads at /mopidy/rpc. This is the same JSON-RPC interface as is exposed over the WebSocket at /mopidy/ws, so you can run any core API command.
    • The HTTP POST interfaces does not give you access to events from Mopidy, like the WebSocket does. The WebSocket interface is still recommended for web clients. The HTTP POST interface may be easier to use for simpler programs, that just needs to query the currently playing track or similar. See HTTP POST API for details.
    • If Zeroconf is enabled, we now announce the _mopidy-http._tcp service in addition to _http._tcp. This is to make it easier to automatically find Mopidy’s HTTP server among other Zeroconf-published HTTP servers on the local network.

    Mopidy.js client library

    This version has been released to npm as Mopidy.js v0.4.0.

    • Update Mopidy.js to use when.js 3. If you maintain a Mopidy client, you should review the differences between when.js 2 and 3 and the when.js debugging guide.
    • All of Mopidy.js’ promise rejection values are now of the Error type. This ensures that all JavaScript VMs will show a useful stack trace if a rejected promise’s value is used to throw an exception. To allow catch clauses to handle different errors differently, server side errors are of the type Mopidy.ServerError, and connection related errors are of the type Mopidy.ConnectionError.
    • Add support for method calls with by-name arguments. The old calling convention, by-position-only, is still the default, but this will change in the future. A warning is logged to the console if you don’t explicitly select a calling convention. See the Mopidy.js JavaScript library docs for details.

    MPD frontend

    • Proper command tokenization for MPD requests. This replaces the old regex based system with an MPD protocol specific tokenizer responsible for breaking requests into pieces before the handlers have at them. (Fixes: #591 and #592)
    • Updated command handler system. As part of the tokenizer cleanup we’ve updated how commands are registered and making it simpler to create new handlers.
    • Simplified a bunch of handlers. All the “browse” type commands now use a common browse helper under the hood for less repetition. Likewise the query handling of “search” commands has been somewhat simplified.
    • Adds placeholders for missing MPD commands, preparing the way for bumping the protocol version once they have been added.
    • Respond to all pending requests before closing connection. (PR: #722)
    • Stop incorrectly catching LookupError in command handling. (Fixes: #741)
    • Browse support for playlists and albums has been added. (PR: #749, #754)
    • The lsinfo command now returns browse results before local playlists. This is helpful as not all clients sort the returned items. (PR: #755)
    • Browse now supports different entries with identical names. (PR: #762)
    • Search terms that are empty or consists of only whitespace are no longer included in the search query sent to backends. (PR: #758)

    Local backend

    • The JSON local library backend now logs a friendly message telling you about mopidy local scan if you don’t have a local library cache. (Fixes: #711)
    • The local scan command now use multiple threads to walk the file system and check files’ modification time. This speeds up scanning, escpecially when scanning remote file systems over e.g. NFS. the local scan command now creates necessary folders if they don’t already exist. Previously, this was only done by the Mopidy server, so doing a local scan before running the server the first time resulted in a crash. (Fixes: #703)
    • Fix proper decoding of exception messages that depends on the user’s locale.

    Stream backend

    • Add config value stream/metadata_blacklist to blacklist certain URIs we should not open to read metadata from before they are opened for playback. This is typically needed for services that invalidate URIs after a single use. (Fixes: #660)
    Source code(tar.gz)
    Source code(zip)
  • v0.18.0(May 7, 2014)

    The focus of 0.18 have been on two fronts: the local library and browsing.

    First, the local library’s old tag cache file used for storing the track metadata scanned from your music collection has been replaced with a far simpler implementation using JSON as the storage format. At the same time, the local library have been made replaceable by extensions, so you can now create extensions that use your favorite database to store the metadata.

    Second, we’ve finally implemented the long awaited “file system” browsing feature that you know from MPD. It is supported by both the MPD frontend and the local and Spotify backends. It is also used by the new Mopidy-Dirble extension to provide you with a directory of Internet radio stations from all over the world.

    Since the release of 0.17, we’ve closed or merged 49 issues and pull requests through about 285 commits by 11 people, including six new guys. Thanks to everyone that has contributed!

    Core API

    • Add mopidy.core.Core.version() for HTTP clients to manage compatibility between API versions. (Fixes: #597)
    • Add mopidy.models.Ref class for use as a lightweight reference to other model types, containing just an URI, a name, and an object type. It is barely used for now, but its use will be extended over time.
    • Add mopidy.core.LibraryController.browse() method for browsing a virtual file system of tracks. Backends can implement support for this by implementing mopidy.backend.LibraryProvider.browse().
    • Events emitted on play/stop, pause/resume, next/previous and on end of track has been cleaned up to work consistently. See the message of commit 1d108752f64fc31ba96e75b415cda5ffb7a23df8 for the full details. (Fixes: #629)

    Backend API

    • Move the backend API classes from mopidy.backends.base to mopidy.backend and remove the Base prefix from the class names:
      • From mopidy.backends.base.Backend to mopidy.backend.Backend
      • From mopidy.backends.base.BaseLibraryProvider to mopidy.backend.LibraryProvider
      • From mopidy.backends.base.BasePlaybackProvider to mopidy.backend.PlaybackProvider
      • From mopidy.backends.base.BasePlaylistsProvider to mopidy.backend.PlaylistsProvider
      • From mopidy.backends.listener.BackendListener to mopidy.backend.BackendListener
      • Imports from the old locations still works, but are deprecated.
    • Add mopidy.backend.LibraryProvider.browse(), which can be implemented by backends that wants to expose directories of tracks in Mopidy’s virtual file system.

    Frontend API

    • The dummy backend used for testing many frontends have moved from mopidy.backends.dummy to mopidy.backend.dummy.

    Commands

    • Reduce amount of logging from dependencies when using mopidy -v. (Fixes: #593)
    • Add support for additional logging verbosity levels with mopidy -vv and mopidy -vvv which increases the amount of logging from dependencies. (Fixes: #593)

    Configuration

    • The default for the mopidy --config option has been updated to include $XDG_CONFIG_DIRS in addition to $XDG_CONFIG_DIR. (Fixes #431)
    • Added support for deprecating config values in order to allow for graceful removal of the no longer used config value local/tag_cache_file.

    Extension support

    • Switched to using a registry model for classes provided by extension. This allows extensions to be extended by other extensions, as needed by for example pluggable libraries for the local backend. See mopidy.ext.Registry for details. (Fixes #601)
    • Added the new method mopidy.ext.Extension.setup(). This method replaces the now deprecated get_backend_classes(), get_frontend_classes(), and register_gstreamer_elements().

    Audio

    • Added audio/mixer_volume to set the initial volume of mixers. This is especially useful for setting the software mixer volume to something else than the default 100%. (Fixes: #633)

    Local backend

    Note: After upgrading to Mopidy 0.18 you must run mopidy local scan to reindex your local music collection. This is due to the change of storage format.

    • Added support for browsing local directories in Mopidy’s virtual file system.
    • Finished the work on creating pluggable libraries. Users can now reconfigure Mopidy to use alternate library providers of their choosing for local files. (Fixes issue #44, partially resolves #397, and causes a temporary regression of #527.)
    • Switched default local library provider from a “tag cache” file that closely resembled the one used by the original MPD server to a compressed JSON file. This greatly simplifies our library code and reuses our existing model serialization code, as used by the HTTP API and web clients.
    • Removed our outdated and bug-ridden “tag cache” local library implementation.
    • Added the config value local/library to select which library to use. It defaults to json, which is the only local library bundled with Mopidy.
    • Added the config value local/data_dir to have a common config for where to store local library data. This is intended to avoid every single local library provider having to have it’s own config value for this.
    • Added the config value local/scan_flush_threshold to control how often to tell local libraries to store changes when scanning local music.

    Streaming backend

    • Add live lookup of URI metadata. (Fixes #540)
    • Add support for extended M3U playlist, meaning that basic track metadata stored in playlists will be used by Mopidy.

    HTTP frontend

    • Upgrade Mopidy.js dependencies and add support for using Mopidy.js with Browserify. This version has been released to npm as Mopidy.js v0.2.0. (Fixes: #609)

    MPD frontend

    • Make the lsinfo, listall, and listallinfo commands support browsing of Mopidy’s virtual file system. (Fixes: #145)
    • Empty commands now return a ACK [[email protected]] {} No command given error instead of OK. This is consistent with the original MPD server implementation.

    Internal changes

    • Events from the audio actor, backends, and core actor are now emitted asyncronously through the GObject event loop. This should resolve the issue that has blocked the merge of the EOT-vs-EOS fix for a long time.
    Source code(tar.gz)
    Source code(zip)
  • v0.18.1(May 7, 2014)

    Bug fix release.

    • Disable extension instead of crashing if a dependency has the wrong version. (Fixes: #657)
    • Make logging work to both console, debug log file, and any custom logging setup from logging/config_file at the same time. (Fixes: #661)
    Source code(tar.gz)
    Source code(zip)
  • v0.18.2(May 7, 2014)

    Bug fix release.

    • We now log warnings for wrongly configured extensions, and clearly label them in mopidy config, but does no longer stop Mopidy from starting because of misconfigured extensions. (Fixes: #682)
    • Fix a crash in the server side WebSocket handler caused by connection problems with clients. (Fixes: #428, #571)
    • Fix the time_position field of the track_playback_ended event, which has been always 0 since v0.18.0. This made scrobbles by Mopidy-Scrobbler not be persisted by Last.fm, because Mopidy reported that you listened to 0 seconds of each track. (Fixes: #674)
    • Fix the log setup so that it is possible to increase the amount of logging from a specific logger using the loglevels config section. (Fixes: #684)
    • Serialization of Playlist models with the last_modified field set to a datetime.datetime instance did not work. The type of mopidy.models.Playlist.last_modified has been redefined from a datetime.datetime instance to the number of milliseconds since Unix epoch as an integer. This makes serialization of the time stamp simpler.
    • Minor refactor of the MPD server context so that Mopidy’s MPD protocol implementation can easier be reused. (Fixes: #646)
    • Network and signal handling has been updated to play nice on Windows systems.
    Source code(tar.gz)
    Source code(zip)
  • v0.18.3(May 7, 2014)

Owner
Mopidy
The extensible music server
Mopidy
Typographic Beat-Oriented Notation for music

tbon Typographic Beat-Oriented Notation for music Tbon aims to be the fastest way to enter pitches, rhythms, meter and dynamic levels from a computer

null 11 Jan 23, 2020
Music typeset with the Lilypond system

Intro (from long ago) This repo contains sheet music typeset with the Lilypond typesetter. The music chosen is in favour of cello music (mostly chambe

Enthusiastic about  the Cello 95 Sep 24, 2021
网易云音乐第三方

ieaseMusic Elegant NeteaseMusic desktop app, Rock with NeteaseMusic ?? Built by Electron, React, MobX, JSS API 由 Binaryify/NeteaseCloudMusicApi 提供。 Pr

null 8.6k Sep 22, 2021
OpenSheetMusicDisplay renders sheet music in MusicXML format in your web browser based on VexFlow. OSMD is brought to you by PhonicScore.com.

OpenSheetMusicDisplay (OSMD) A MusicXML renderer for the Browser opensheetmusicdisplay.org About OSMD • Demo • Key Features • Limitations • How to Use

Open Sheet Music Display 800 Sep 15, 2021
A language for music notation

Lydown is a language and compiler for creating music scores, parts and snippets. The lydown code is compiled to lilypond code and then compiled to PDF

Sharon Rosner 21 Apr 8, 2021
🎵 Music notation engraving library for MEI with MusicXML and Humdrum support and various toolkits (JavaScript, Python)

Verovio is a fast, portable and lightweight library for engraving Music Encoding Initiative (MEI) digital scores into SVG images. Verovio also contain

RISM Digital Center 426 Sep 22, 2021
Music player for deepin desktop environment.

deepin-music Deepin music is a local music player with beautiful design and simple functions developed by Deepin Technology. Dependencies Build depend

Wuhan Deepin Technology Co.,Ltd. 155 Sep 16, 2021
A music programming language for musicians. :notes:

Installation | Docs | Changelog | Contributing composers chatting Alda is a text-based programming language for music composition. It allows you to co

Alda 4.7k Sep 25, 2021
music library manager and MusicBrainz tagger

beets Beets is the media library management system for obsessive music geeks. The purpose of beets is to get your music collection right once and for

beetbox 10.4k Sep 15, 2021
A Music programming language. Translates source code into MIDI. Includes a player. Supports MIDI-Karaoke. Includes a MIDI analyzer.

Get Started | Features | Screenshots | Programming | CLI | Contribute | License Midica is an interpreter for a Music Programming Language. It translat

Jan Trukenmüller 47 Sep 17, 2021
Frescobaldi LilyPond Editor

README for Frescobaldi Homepage: http://www.frescobaldi.org/ Main author: Wilbert Berendsen Frescobaldi is a LilyPond sheet music text editor. It aims

Frescobaldi 501 Sep 21, 2021
🎚️ Open Source Audio Matching and Mastering

Matching + Mastering = ❤️ Matchering 2.0 is a novel Containerized Web Application and Python Library for audio matching and mastering. It follows a si

Sergey Grishakov 490 Sep 24, 2021
web based music sheet viewer (go, pdfjs) as a single binary

Digital Music Stand A simple cross-platform browser-based pdfjs-based viewer to display and search music sheets. A single binary including all assets.

Patrick Wieschollek 21 Aug 27, 2021
C++ library and Python bindings for the Music Encoding Initiative format

LibMEI LibMEI is a C++ library for reading and writing MEI files It is developed by the Distributed Digital Music Archives and Libraries Lab at the Sc

Distributed Digital Music Archives and Libraries Lab 49 Aug 30, 2021
Streaming music player that finds free music for you

Desktop music player focused on streaming from free sources Links Official website Mastodon Twitter Support channel (Matrix): #nuclear:matrix.org Disc

null 6.8k Sep 17, 2021
GTK 3 client for the Music Player Daemon - I'm looking for new maintainers!

Sonata is looking for new maintainers! I (@multani) don't use Sonata much anymore and as a consequence, I've been very slow to answer even to the few

Jonathan Ballet 123 Aug 28, 2021
Collaborative Programmable Music

888 888 _ooooooooo._ 888 ,o888PP""""PP88

Overtone 5.2k Sep 24, 2021
The git repository of the advanced drum machine

Hydrogen drum machine Hydrogen is an advanced drum machine for GNU/Linux, Mac and Windows. It's main goal is to bring professional yet simple and intu

Hydrogen 702 Sep 15, 2021
Lilypond music preprocessor

Ripple - DRY for Lilypond Ripple is a small program that helps you generate scores and parts without repeating yourself, performing complex includes o

Sharon Rosner 21 Mar 24, 2020