=== modified file 'CMakeLists.txt'
--- CMakeLists.txt	2015-09-08 20:11:21 +0000
+++ CMakeLists.txt	2015-10-22 17:36:10 +0000
@@ -3,7 +3,7 @@
 project(ubuntu-media-hub)
 
 set(UBUNTU_MEDIA_HUB_VERSION_MAJOR 3)
-set(UBUNTU_MEDIA_HUB_VERSION_MINOR 2)
+set(UBUNTU_MEDIA_HUB_VERSION_MINOR 3)
 set(UBUNTU_MEDIA_HUB_VERSION_PATCH 0)
 
 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fPIC -pthread")

=== modified file 'include/core/media/track_list.h'
--- include/core/media/track_list.h	2015-07-20 20:39:42 +0000
+++ include/core/media/track_list.h	2015-10-22 17:36:10 +0000
@@ -41,6 +41,7 @@
 {
   public:
     typedef std::vector<Track::Id> Container;
+    typedef std::vector<Track::UriType> ContainerURI;
     typedef std::tuple<std::vector<Track::Id>, Track::Id> ContainerTrackIdTuple;
     typedef Container::iterator Iterator;
     typedef Container::const_iterator ConstIterator;
@@ -65,9 +66,12 @@
     /** Gets the URI for a given Track. */
     virtual Track::UriType query_uri_for_track(const Track::Id& id) = 0;
 
-    /** Adds a URI in the TrackList. */
+    /** Adds a URI into the TrackList. */
     virtual void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current) = 0;
 
+    /** Adds a list of URIs into the TrackList. */
+    virtual void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position) = 0;
+
     /** Removes a Track from the TrackList. */
     virtual void remove_track(const Track::Id& id) = 0;
 
@@ -103,6 +107,9 @@
     /** Indicates that a track has been added to the track list. */
     virtual const core::Signal<Track::Id>& on_track_added() const = 0;
 
+    /** Indicates that one or more tracks have been added to the track list. */
+    virtual const core::Signal<ContainerURI>& on_tracks_added() const = 0;
+
     /** Indicates that a track has been removed from the track list. */
     virtual const core::Signal<Track::Id>& on_track_removed() const = 0;
 

=== modified file 'src/core/media/mpris/track_list.h'
--- src/core/media/mpris/track_list.h	2015-07-27 22:15:33 +0000
+++ src/core/media/mpris/track_list.h	2015-10-22 17:36:10 +0000
@@ -51,6 +51,7 @@
     DBUS_CPP_METHOD_DEF(GetTracksMetadata, TrackList)
     DBUS_CPP_METHOD_DEF(GetTracksUri, TrackList)
     DBUS_CPP_METHOD_DEF(AddTrack, TrackList)
+    DBUS_CPP_METHOD_DEF(AddTracks, TrackList)
     DBUS_CPP_METHOD_DEF(RemoveTrack, TrackList)
     DBUS_CPP_METHOD_DEF(GoTo, TrackList)
     DBUS_CPP_METHOD_DEF(Reset, TrackList)
@@ -75,6 +76,13 @@
 
         DBUS_CPP_SIGNAL_DEF
         (
+            TracksAdded,
+            TrackList,
+            core::ubuntu::media::TrackList::ContainerURI
+        )
+
+        DBUS_CPP_SIGNAL_DEF
+        (
             TrackRemoved,
             TrackList,
             core::ubuntu::media::Track::Id
@@ -134,6 +142,7 @@
               {
                   configuration.object->template get_signal<Signals::TrackListReplaced>(),
                   configuration.object->template get_signal<Signals::TrackAdded>(),
+                  configuration.object->template get_signal<Signals::TracksAdded>(),
                   configuration.object->template get_signal<Signals::TrackRemoved>(),
                   configuration.object->template get_signal<Signals::TrackChanged>(),
                   configuration.object->template get_signal<Signals::TrackMetadataChanged>(),
@@ -178,6 +187,7 @@
         {
             core::dbus::Signal<Signals::TrackListReplaced, Signals::TrackListReplaced::ArgumentType>::Ptr tracklist_replaced;
             core::dbus::Signal<Signals::TrackAdded, Signals::TrackAdded::ArgumentType>::Ptr track_added;
+            core::dbus::Signal<Signals::TracksAdded, Signals::TracksAdded::ArgumentType>::Ptr tracks_added;
             core::dbus::Signal<Signals::TrackRemoved, Signals::TrackRemoved::ArgumentType>::Ptr track_removed;
             core::dbus::Signal<Signals::TrackChanged, Signals::TrackChanged::ArgumentType>::Ptr track_changed;
             core::dbus::Signal<Signals::TrackMetadataChanged, Signals::TrackMetadataChanged::ArgumentType>::Ptr track_metadata_changed;

=== modified file 'src/core/media/player_implementation.cpp'
--- src/core/media/player_implementation.cpp	2015-09-29 11:07:54 +0000
+++ src/core/media/player_implementation.cpp	2015-10-22 17:36:10 +0000
@@ -547,6 +547,18 @@
         d->update_mpris_properties();
     });
 
+    d->track_list->on_tracks_added().connect([this](const media::TrackList::ContainerURI& tracks)
+    {
+        std::cout << "** Track was added, handling in PlayerImplementation" << std::endl;
+        // If the two sizes are the same, that means the TrackList was previously empty and we need
+        // to open the first track in the TrackList so that is_audio_source() and is_video_source()
+        // will function correctly.
+        if (tracks.size() >= 1 and d->track_list->tracks()->size() == tracks.size())
+            d->open_first_track_from_tracklist(tracks.front());
+
+        d->update_mpris_properties();
+    });
+
     d->track_list->on_track_removed().connect([this](const media::Track::Id&)
     {
         d->update_mpris_properties();

=== modified file 'src/core/media/track_list_implementation.cpp'
--- src/core/media/track_list_implementation.cpp	2015-09-28 13:20:01 +0000
+++ src/core/media/track_list_implementation.cpp	2015-10-22 17:36:10 +0000
@@ -20,6 +20,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <tuple>
+#include <unistd.h>
+
+#include <dbus/dbus.h>
 
 #include "track_list_implementation.h"
 
@@ -39,6 +42,30 @@
     // Used for caching the original tracklist order to be used to restore the order
     // to the live TrackList after shuffle is turned off
     media::TrackList::Container original_tracklist;
+
+    void updateCachedTrackMetadata(const media::Track::Id& id, const media::Track::UriType& uri)
+    {
+        if (meta_data_cache.count(id) == 0)
+        {
+            // FIXME: This code seems to conflict badly when called multiple times in a row: causes segfaults
+#if 0
+            try {
+                meta_data_cache[id] = std::make_tuple(
+                            uri,
+                            extractor->meta_data_for_track_with_uri(uri));
+            } catch (const std::runtime_error &e) {
+                std::cerr << "Failed to retrieve metadata for track '" << uri << "' (" << e.what() << ")" << std::endl;
+            }
+#else
+            meta_data_cache[id] = std::make_tuple(
+                    uri,
+                    core::ubuntu::media::Track::MetaData{});
+#endif
+        } else
+        {
+            std::get<0>(meta_data_cache[id]) = uri;
+        }
+    }
 };
 
 media::TrackListImplementation::TrackListImplementation(
@@ -101,26 +128,7 @@
 
     if (result)
     {
-        if (d->meta_data_cache.count(id) == 0)
-        {
-            // FIXME: This code seems to conflict badly when called multiple times in a row: causes segfaults
-#if 0
-            try {
-                d->meta_data_cache[id] = std::make_tuple(
-                            uri,
-                            d->extractor->meta_data_for_track_with_uri(uri));
-            } catch (const std::runtime_error &e) {
-                std::cerr << "Failed to retrieve metadata for track '" << uri << "' (" << e.what() << ")" << std::endl;
-            }
-#else
-            d->meta_data_cache[id] = std::make_tuple(
-                    uri,
-                    core::ubuntu::media::Track::MetaData{});
-#endif
-        } else
-        {
-            std::get<0>(d->meta_data_cache[id]) = uri;
-        }
+        d->updateCachedTrackMetadata(id, uri);
 
         if (make_current)
         {
@@ -138,8 +146,49 @@
         std::cout << "Signaling that we just added track id: " << id << std::endl;
         // Signal to the client that a track was added to the TrackList
         on_track_added()(id);
-        std::cout << "Signaled that we just added track id: " << id << std::endl;
-    }
+    }
+}
+
+void media::TrackListImplementation::add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position)
+{
+    std::cout << __PRETTY_FUNCTION__ << std::endl;
+
+    ContainerURI tmp;
+    for (const auto uri : uris)
+    {
+        // TODO: Refactor this code to use a smaller common function shared with add_track_with_uri_at()
+        std::stringstream ss; ss << d->object->path().as_string() << "/" << d->track_counter++;
+        Track::Id id{ss.str()};
+        std::cout << "Adding Track::Id: " << id << std::endl;
+        std::cout << "\tURI: " << uri << std::endl;
+
+        tmp.push_back(id);
+
+        Track::Id insert_position = position;
+
+        auto it = std::find(tracks().get().begin(), tracks().get().end(), insert_position);
+        auto result = tracks().update([this, id, position, it, &insert_position](TrackList::Container& container)
+        {
+            container.insert(it, id);
+            // Make sure the next insert position is after the current insert position
+            // Update the Track::Id after which to insert the next one from uris
+            insert_position = id;
+
+            return true;
+        });
+
+        if (result)
+        {
+            d->updateCachedTrackMetadata(id, uri);
+
+            // Signal to the client that the current track has changed for the first track added to the TrackList
+            if (tracks().get().size() == 1)
+                on_track_changed()(id);
+        }
+    }
+
+    std::cout << "Signaling that we just added " << tmp.size() << " tracks to the TrackList" << std::endl;
+    on_tracks_added()(tmp);
 }
 
 void media::TrackListImplementation::remove_track(const media::Track::Id& id)

=== modified file 'src/core/media/track_list_implementation.h'
--- src/core/media/track_list_implementation.h	2015-05-22 21:19:28 +0000
+++ src/core/media/track_list_implementation.h	2015-10-22 17:36:10 +0000
@@ -43,6 +43,7 @@
     Track::MetaData query_meta_data_for_track(const Track::Id& id);
 
     void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);
+    void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position);
     void remove_track(const Track::Id& id);
 
     void go_to(const Track::Id& track, bool toggle_player_state);

=== modified file 'src/core/media/track_list_skeleton.cpp'
--- src/core/media/track_list_skeleton.cpp	2015-09-28 15:31:46 +0000
+++ src/core/media/track_list_skeleton.cpp	2015-10-22 17:36:10 +0000
@@ -59,6 +59,7 @@
           signals
           {
               skeleton.signals.track_added,
+              skeleton.signals.tracks_added,
               skeleton.signals.track_removed,
               skeleton.signals.track_changed,
               skeleton.signals.tracklist_replaced
@@ -113,6 +114,36 @@
         });
     }
 
+    void handle_add_tracks_with_uri_at(const core::dbus::Message::Ptr& msg)
+    {
+        std::cout << "*** " << __PRETTY_FUNCTION__ << std::endl;
+        request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(), [this, msg](const media::apparmor::ubuntu::Context& context)
+        {
+            ContainerURI uris; media::Track::Id after;
+            msg->reader() >> uris >> after;
+
+            media::apparmor::ubuntu::RequestAuthenticator::Result result;
+            for (const auto uri : uris)
+            {
+                // Make sure the client has adequate apparmor permissions to open the URI
+                result = request_authenticator->authenticate_open_uri_request(context, uri);
+                if (not std::get<0>(result))
+                {
+                    std::cerr << "Warning: Not adding track " << uri <<
+                        " to TrackList because of inadequate client apparmor permissions." << std::endl;
+                    break;
+                }
+            }
+
+            auto reply = dbus::Message::make_method_return(msg);
+            // Only add the track to the TrackList if it passes the apparmor permissions check
+            if (std::get<0>(result))
+                impl->add_tracks_with_uri_at(uris, after);
+
+            bus->send(reply);
+        });
+    }
+
     void handle_remove_track(const core::dbus::Message::Ptr& msg)
     {
         media::Track::Id track;
@@ -159,11 +190,13 @@
     struct Signals
     {
         typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal;
+        typedef core::dbus::Signal<mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType> DBusTracksAddedSignal;
         typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal;
         typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal;
         typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal;
 
         Signals(const std::shared_ptr<DBusTrackAddedSignal>& remote_track_added,
+                const std::shared_ptr<DBusTracksAddedSignal>& remote_tracks_added,
                 const std::shared_ptr<DBusTrackRemovedSignal>& remote_track_removed,
                 const std::shared_ptr<DBusTrackChangedSignal>& remote_track_changed,
                 const std::shared_ptr<DBusTrackListReplacedSignal>& remote_track_list_replaced)
@@ -174,6 +207,11 @@
                 remote_track_added->emit(id);
             });
 
+            on_tracks_added.connect([remote_tracks_added](const media::TrackList::ContainerURI &tracks)
+            {
+                remote_tracks_added->emit(tracks);
+            });
+
             on_track_removed.connect([remote_track_removed](const media::Track::Id &id)
             {
                 remote_track_removed->emit(id);
@@ -191,6 +229,7 @@
         }
 
         core::Signal<Track::Id> on_track_added;
+        core::Signal<TrackList::ContainerURI> on_tracks_added;
         core::Signal<Track::Id> on_track_removed;
         core::Signal<Track::Id> on_track_changed;
         core::Signal<TrackList::ContainerTrackIdTuple> on_track_list_replaced;
@@ -219,6 +258,11 @@
                   std::ref(d),
                   std::placeholders::_1));
 
+    d->object->install_method_handler<mpris::TrackList::AddTracks>(
+        std::bind(&Private::handle_add_tracks_with_uri_at,
+                  std::ref(d),
+                  std::placeholders::_1));
+
     d->object->install_method_handler<mpris::TrackList::RemoveTrack>(
         std::bind(&Private::handle_remove_track,
                   std::ref(d),
@@ -490,6 +534,11 @@
     return d->signals.on_track_added;
 }
 
+const core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added() const
+{
+    return d->signals.on_tracks_added;
+}
+
 const core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed() const
 {
     return d->signals.on_track_removed;
@@ -520,6 +569,11 @@
     return d->signals.on_track_added;
 }
 
+core::Signal<media::TrackList::ContainerURI>& media::TrackListSkeleton::on_tracks_added()
+{
+    return d->signals.on_tracks_added;
+}
+
 core::Signal<media::Track::Id>& media::TrackListSkeleton::on_track_removed()
 {
     return d->signals.on_track_removed;

=== modified file 'src/core/media/track_list_skeleton.h'
--- src/core/media/track_list_skeleton.h	2015-09-28 13:20:01 +0000
+++ src/core/media/track_list_skeleton.h	2015-10-22 17:36:10 +0000
@@ -54,6 +54,8 @@
     core::Signal<ContainerTrackIdTuple>& on_track_list_replaced();
     const core::Signal<Track::Id>& on_track_added() const;
     core::Signal<Track::Id>& on_track_added();
+    const core::Signal<ContainerURI>& on_tracks_added() const;
+    core::Signal<ContainerURI>& on_tracks_added();
     const core::Signal<Track::Id>& on_track_removed() const;
     const core::Signal<Track::Id>& on_track_changed() const;
     core::Signal<Track::Id>& on_track_changed();

=== modified file 'src/core/media/track_list_stub.cpp'
--- src/core/media/track_list_stub.cpp	2015-07-27 22:15:33 +0000
+++ src/core/media/track_list_stub.cpp	2015-10-22 17:36:10 +0000
@@ -52,6 +52,7 @@
           signals
           {
               object->get_signal<mpris::TrackList::Signals::TrackAdded>(),
+              object->get_signal<mpris::TrackList::Signals::TracksAdded>(),
               object->get_signal<mpris::TrackList::Signals::TrackRemoved>(),
               object->get_signal<mpris::TrackList::Signals::TrackListReplaced>(),
               object->get_signal<mpris::TrackList::Signals::TrackChanged>()
@@ -70,21 +71,25 @@
     struct Signals
     {
         typedef core::dbus::Signal<mpris::TrackList::Signals::TrackAdded, mpris::TrackList::Signals::TrackAdded::ArgumentType> DBusTrackAddedSignal;
+        typedef core::dbus::Signal<mpris::TrackList::Signals::TracksAdded, mpris::TrackList::Signals::TracksAdded::ArgumentType> DBusTracksAddedSignal;
         typedef core::dbus::Signal<mpris::TrackList::Signals::TrackRemoved, mpris::TrackList::Signals::TrackRemoved::ArgumentType> DBusTrackRemovedSignal;
         typedef core::dbus::Signal<mpris::TrackList::Signals::TrackListReplaced, mpris::TrackList::Signals::TrackListReplaced::ArgumentType> DBusTrackListReplacedSignal;
         typedef core::dbus::Signal<mpris::TrackList::Signals::TrackChanged, mpris::TrackList::Signals::TrackChanged::ArgumentType> DBusTrackChangedSignal;
 
         Signals(const std::shared_ptr<DBusTrackAddedSignal>& track_added,
+                const std::shared_ptr<DBusTracksAddedSignal>& tracks_added,
                 const std::shared_ptr<DBusTrackRemovedSignal>& track_removed,
                 const std::shared_ptr<DBusTrackListReplacedSignal>& track_list_replaced,
                 const std::shared_ptr<DBusTrackChangedSignal>& track_changed)
             : on_track_added(),
+              on_tracks_added(),
               on_track_removed(),
               on_track_list_replaced(),
               on_track_changed(),
               dbus
               {
                   track_added,
+                  tracks_added,
                   track_removed,
                   track_list_replaced,
                   track_changed,
@@ -96,6 +101,12 @@
                 on_track_added(id);
             });
 
+            dbus.on_tracks_added->connect([this](const media::TrackList::ContainerURI& tracks)
+            {
+                std::cout << "OnTracksAdded signal arrived via the bus." << std::endl;
+                on_tracks_added(tracks);
+            });
+
             dbus.on_track_removed->connect([this](const Track::Id& id)
             {
                 std::cout << "OnTrackRemoved signal arrived via the bus." << std::endl;
@@ -116,6 +127,7 @@
         }
 
         core::Signal<Track::Id> on_track_added;
+        core::Signal<media::TrackList::ContainerURI> on_tracks_added;
         core::Signal<Track::Id> on_track_removed;
         core::Signal<media::TrackList::ContainerTrackIdTuple> on_track_list_replaced;
         core::Signal<Track::Id> on_track_changed;
@@ -125,6 +137,7 @@
         struct DBus
         {
             std::shared_ptr<DBusTrackAddedSignal> on_track_added;
+            std::shared_ptr<DBusTracksAddedSignal> on_tracks_added;
             std::shared_ptr<DBusTrackRemovedSignal> on_track_removed;
             std::shared_ptr<DBusTrackListReplacedSignal> on_track_list_replaced;
             std::shared_ptr<DBusTrackChangedSignal> on_track_changed;
@@ -184,18 +197,28 @@
 
 void media::TrackListStub::add_track_with_uri_at(
         const media::Track::UriType& uri,
-        const media::Track::Id& id,
+        const media::Track::Id& position,
         bool make_current)
 {
     auto op = d->object->invoke_method_synchronously<mpris::TrackList::AddTrack, void>(
                 uri,
-                id,
+                position,
                 make_current);
 
     if (op.is_error())
         throw std::runtime_error("Problem adding track: " + op.error());
 }
 
+void media::TrackListStub::add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position)
+{
+    auto op = d->object->invoke_method_synchronously<mpris::TrackList::AddTracks, void>(
+                uris,
+                position);
+
+    if (op.is_error())
+        throw std::runtime_error("Problem adding tracks: " + op.error());
+}
+
 void media::TrackListStub::remove_track(const media::Track::Id& track)
 {
     auto op = d->object->invoke_method_synchronously<mpris::TrackList::RemoveTrack, void>(
@@ -255,6 +278,11 @@
     return d->signals.on_track_added;
 }
 
+const core::Signal<media::TrackList::ContainerURI>& media::TrackListStub::on_tracks_added() const
+{
+    return d->signals.on_tracks_added;
+}
+
 const core::Signal<media::Track::Id>& media::TrackListStub::on_track_removed() const
 {
     return d->signals.on_track_removed;

=== modified file 'src/core/media/track_list_stub.h'
--- src/core/media/track_list_stub.h	2015-07-20 20:39:42 +0000
+++ src/core/media/track_list_stub.h	2015-10-22 17:36:10 +0000
@@ -48,6 +48,7 @@
     Track::UriType query_uri_for_track(const Track::Id& id);
 
     void add_track_with_uri_at(const Track::UriType& uri, const Track::Id& position, bool make_current);
+    void add_tracks_with_uri_at(const ContainerURI& uris, const Track::Id& position);
     void remove_track(const Track::Id& id);
 
     void go_to(const Track::Id& track, bool toggle_player_state);
@@ -62,6 +63,7 @@
 
     const core::Signal<ContainerTrackIdTuple>& on_track_list_replaced() const;
     const core::Signal<Track::Id>& on_track_added() const;
+    const core::Signal<ContainerURI>& on_tracks_added() const;
     const core::Signal<Track::Id>& on_track_removed() const;
     const core::Signal<Track::Id>& on_track_changed() const;
     const core::Signal<std::pair<Track::Id, bool>>& on_go_to_track() const;

