{"action":"created","comment":{"url":"https://api.github.com/repos/SoCo/SoCo/pulls/comments/39137504","id":39137504,"diff_hunk":"@@ -1833,6 +1873,185 @@ def add_item_to_sonos_playlist(self, queueable_item, sonos_playlist):\n             ('AddAtIndex', 4294967295)\n         ])\n \n+    def reorder_sonos_playlist(self, object_id, tracks, new_pos, update_id=0):\n+        \"\"\" Reorder and/or Remove tracks in a Sonos playlist. The underlying\n+        call is quite complex as it both can move a track within the list or\n+        delete a track from the playlist.  All of this depends on what tracks\n+        and new_pos specify.\n+\n+        See the helper methods\n+        :py:meth:`clear_sonos_playlist`, :py:meth:`move_in_sonos_playlist`,\n+        :py:meth:`remove_from_sonos_playlist` for simplified usage.\n+\n+        update_id - If you have a series of operations, tracking the update_id\n+        and setting it, will save a lookup operation.\n+\n+        Examples:\n+          To reorder the first two tracks::\n+\n+            # object_id specified by the item_id\n+            obj.reorder_sonos_playlist('SQ:0', tracks=[0, ], new_pos=[1, ])\n+            # OR object_id specified by the DidlPlaylistContainer object\n+            spl = obj.get_sonos_playlists()[0]\n+            obj.reorder_sonos_playlist(spl, tracks=[0, ], new_pos=[1, ])\n+\n+          To delete the second track::\n+\n+            # tracks/new_pos are a list of int\n+            obj.reorder_sonos_playlist('SQ:0', tracks=[1, ], new_pos=[None, ])\n+            # OR tracks/new_pos are a list of int-like\n+            obj.reorder_sonos_playlist('SQ:0', tracks=['1', ], new_pos=['', ])\n+            # OR tracks/new_pos are strings - no transform is done\n+            obj.reorder_sonos_playlist('SQ:0', tracks='1', new_pos='')\n+\n+        Args:\n+            object_id (str): The item_id of the Sonos playlist or the Sonos\n+                playlist object\n+                (:py:class:`~.soco.data_structures.DidlPlaylistContainer`).\n+            tracks: (list): list of track indices(int) to reorder. May also be\n+                a list of int like things. i.e. ``['0', '1',]`` OR it may be a\n+                str of comma separated int like things. ``\"0,1\"``.  Tracks are\n+                **0**-based. Meaning the first track is track 0, just like\n+                indexing into a Python list.\n+            new_pos (list): list of new positions (int|None)\n+                corresponding to track_list. MUST be the same type as\n+                ``tracks``. **0**-based, see tracks above. ``None`` is the\n+                indicator to remove the track. If using a list of strings,\n+                then a remove is indicated by an empty string.\n+            update_id (int): operation id (default: 0) If set to 0, a lookup\n+                is done to find the correct value.\n+\n+        Returns:\n+            tuple: A  tuple(int, int, int) containing 3 values:\n+                Change in size between original playlist and the resulting\n+                playlist, the length of resulting playlist, and the new\n+                update_id.\n+\n+        Raises:\n+            SoCoUPnPException: If object_id does not exist.\n+        \"\"\"\n+        # allow either a string 'SQ:10' or an object with item_id attribute.\n+        object_id = getattr(object_id, 'item_id', object_id)\n+\n+        if isinstance(tracks, UnicodeType):\n+            track_list = [tracks, ]\n+            position_list = [new_pos, ]\n+        elif type(tracks) is int:\n+            track_list = [tracks, ]\n+            position_list = [new_pos or '', ]\n+        else:\n+            track_list = [str(x) for x in tracks]\n+            position_list = [str(x) if x is not None else '' for x in new_pos]\n+\n+        if update_id == 0:  # retrieve the update id for the object\n+            response, _ = self._music_lib_search(object_id, 0, 1)\n+            update_id = response['UpdateID']\n+        change = 0\n+\n+        for track, position in zip(track_list, position_list):\n+            if track == position:   # there is no move, a no-op\n+                continue\n+            response = self.avTransport.ReorderTracksInSavedQueue([\n+                (\"InstanceID\", 0),\n+                (\"ObjectID\", object_id),\n+                (\"UpdateID\", update_id),\n+                (\"TrackList\", track),\n+                (\"NewPositionList\", position),\n+            ])\n+            change += int(response['QueueLengthChange'])\n+            update_id = int(response['NewUpdateID'])\n+        length = int(response['NewQueueLength'])\n+\n+        return change, length, update_id\n+\n+    def clear_sonos_playlist(self, object_id, update_id=0):\n+        \"\"\" Clear all tracks from a Sonos playlist.\n+        This is a convenience function for :py:meth:`reorder_sonos_playlist`.\n+\n+        Example:\n+            ``obj.clear_sonos_playlist('SQ:0')``\n+\n+        Args:\n+            object_id (str): The item_id of the Sonos playlist or\n+                the Sonos playlist object\n+                (:py:class:`~.soco.data_structures.DidlPlaylistContainer`).\n+            update_id (int): Optional update counter for the object. If left\n+                at the default of 0, it will be looked up.\n+\n+        Returns:\n+            tuple: See :py:meth:`reorder_sonos_playlist`\n+\n+        Raises:\n+            SoCoUPnPException: See :py:meth:`reorder_sonos_playlist`\n+        \"\"\"\n+        sonos_pl = None\n+        if type(object_id) is DidlPlaylistContainer:\n+            sonos_pl = object_id\n+        else:\n+            for spl in self.get_sonos_playlists():\n+                if spl.item_id == object_id:\n+                    sonos_pl = spl\n+        if sonos_pl is None:\n+            raise ValueError('\"{0}\" is not a valid Sonos Playlist'\n+                             ' item_id'.format(object_id))\n+        num_tracks = self.browse(ml_item=sonos_pl).total_matches\n+        tracks = ','.join([str(x) for x in range(num_tracks)])\n+        return self.reorder_sonos_playlist(sonos_pl, tracks=tracks,\n+                                           new_pos='', update_id=update_id)\n+\n+    def move_in_sonos_playlist(self, object_id, track, new_pos, update_id=0):\n+        \"\"\" Move a track to a new position within a Sonos Playlist.\n+        This is a convenience function for :py:meth:`reorder_sonos_playlist`.\n+\n+        Example::\n+\n+            obj.move_in_sonos_playlist('SQ:0', track=0, new_pos=1)\n+\n+        Args:\n+            object_id (str): The item_id of the Sonos playlist or\n+                the Sonos playlist object\n+                (:py:class:`~.soco.data_structures.DidlPlaylistContainer`).\n+            track (int): **0**-based position of the track to move. The first\n+                track is track 0, just like indexing into a Python list.\n+            new_pos (int): **0**-based location to move the track.\n+            update_id (int): Optional update counter for the object. If left\n+                at the default of 0, it will be looked up.\n+\n+        Returns:\n+            tuple: See :py:meth:`reorder_sonos_playlist`\n+\n+        Raises:\n+            SoCoUPnPException: See :py:meth:`reorder_sonos_playlist`\n+        \"\"\"\n+        return self.reorder_sonos_playlist(object_id, int(track),\n+                                           int(new_pos), update_id)\n+\n+    def remove_from_sonos_playlist(self, object_id, track, update_id=0):\n+        \"\"\" Remove a track from a Sonos Playlist.\n+        This is a convenience function for :py:meth:`reorder_sonos_playlist`.\n+\n+        Example::\n+\n+            obj.remove_from_sonos_playlist('SQ:0', track=0)\n+\n+        Args:\n+            object_id (str): The item_id of the Sonos playlist or","path":"soco/core.py","position":236,"original_position":236,"commit_id":"73e2523e8ea7ff6c61715e62afcda2fbc6674040","original_commit_id":"73e2523e8ea7ff6c61715e62afcda2fbc6674040","user":{"login":"KennethNielsen","id":1611076,"avatar_url":"https://avatars.githubusercontent.com/u/1611076?v=3","gravatar_id":"","url":"https://api.github.com/users/KennethNielsen","html_url":"https://github.com/KennethNielsen","followers_url":"https://api.github.com/users/KennethNielsen/followers","following_url":"https://api.github.com/users/KennethNielsen/following{/other_user}","gists_url":"https://api.github.com/users/KennethNielsen/gists{/gist_id}","starred_url":"https://api.github.com/users/KennethNielsen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KennethNielsen/subscriptions","organizations_url":"https://api.github.com/users/KennethNielsen/orgs","repos_url":"https://api.github.com/users/KennethNielsen/repos","events_url":"https://api.github.com/users/KennethNielsen/events{/privacy}","received_events_url":"https://api.github.com/users/KennethNielsen/received_events","type":"User","site_admin":false},"body":"playlist_object_first","created_at":"2015-09-10T08:48:35Z","updated_at":"2015-09-10T08:48:35Z","html_url":"https://github.com/SoCo/SoCo/pull/352#discussion_r39137504","pull_request_url":"https://api.github.com/repos/SoCo/SoCo/pulls/352","_links":{"self":{"href":"https://api.github.com/repos/SoCo/SoCo/pulls/comments/39137504"},"html":{"href":"https://github.com/SoCo/SoCo/pull/352#discussion_r39137504"},"pull_request":{"href":"https://api.github.com/repos/SoCo/SoCo/pulls/352"}}},"pull_request":{"url":"https://api.github.com/repos/SoCo/SoCo/pulls/352","id":44319329,"html_url":"https://github.com/SoCo/SoCo/pull/352","diff_url":"https://github.com/SoCo/SoCo/pull/352.diff","patch_url":"https://github.com/SoCo/SoCo/pull/352.patch","issue_url":"https://api.github.com/repos/SoCo/SoCo/issues/352","number":352,"state":"open","locked":false,"title":"Major PR for Sonos Playlist Management.","user":{"login":"dundeemt","id":969926,"avatar_url":"https://avatars.githubusercontent.com/u/969926?v=3","gravatar_id":"","url":"https://api.github.com/users/dundeemt","html_url":"https://github.com/dundeemt","followers_url":"https://api.github.com/users/dundeemt/followers","following_url":"https://api.github.com/users/dundeemt/following{/other_user}","gists_url":"https://api.github.com/users/dundeemt/gists{/gist_id}","starred_url":"https://api.github.com/users/dundeemt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dundeemt/subscriptions","organizations_url":"https://api.github.com/users/dundeemt/orgs","repos_url":"https://api.github.com/users/dundeemt/repos","events_url":"https://api.github.com/users/dundeemt/events{/privacy}","received_events_url":"https://api.github.com/users/dundeemt/received_events","type":"User","site_admin":false},"body":" Add base method reorder_sonos_playlist and a number of helpers, documentation and tests.","created_at":"2015-09-07T04:28:32Z","updated_at":"2015-09-10T08:48:35Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8f76354f120f9149358b1a270dd4cc833fda9501","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/SoCo/SoCo/pulls/352/commits","review_comments_url":"https://api.github.com/repos/SoCo/SoCo/pulls/352/comments","review_comment_url":"https://api.github.com/repos/SoCo/SoCo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/SoCo/SoCo/issues/352/comments","statuses_url":"https://api.github.com/repos/SoCo/SoCo/statuses/73e2523e8ea7ff6c61715e62afcda2fbc6674040","head":{"label":"dundeemt:spl-reorder","ref":"spl-reorder","sha":"73e2523e8ea7ff6c61715e62afcda2fbc6674040","user":{"login":"dundeemt","id":969926,"avatar_url":"https://avatars.githubusercontent.com/u/969926?v=3","gravatar_id":"","url":"https://api.github.com/users/dundeemt","html_url":"https://github.com/dundeemt","followers_url":"https://api.github.com/users/dundeemt/followers","following_url":"https://api.github.com/users/dundeemt/following{/other_user}","gists_url":"https://api.github.com/users/dundeemt/gists{/gist_id}","starred_url":"https://api.github.com/users/dundeemt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dundeemt/subscriptions","organizations_url":"https://api.github.com/users/dundeemt/orgs","repos_url":"https://api.github.com/users/dundeemt/repos","events_url":"https://api.github.com/users/dundeemt/events{/privacy}","received_events_url":"https://api.github.com/users/dundeemt/received_events","type":"User","site_admin":false},"repo":{"id":38574975,"name":"SoCo","full_name":"dundeemt/SoCo","owner":{"login":"dundeemt","id":969926,"avatar_url":"https://avatars.githubusercontent.com/u/969926?v=3","gravatar_id":"","url":"https://api.github.com/users/dundeemt","html_url":"https://github.com/dundeemt","followers_url":"https://api.github.com/users/dundeemt/followers","following_url":"https://api.github.com/users/dundeemt/following{/other_user}","gists_url":"https://api.github.com/users/dundeemt/gists{/gist_id}","starred_url":"https://api.github.com/users/dundeemt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dundeemt/subscriptions","organizations_url":"https://api.github.com/users/dundeemt/orgs","repos_url":"https://api.github.com/users/dundeemt/repos","events_url":"https://api.github.com/users/dundeemt/events{/privacy}","received_events_url":"https://api.github.com/users/dundeemt/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dundeemt/SoCo","description":"SoCo (Sonos Controller) is a simple Python class that allows you to programmatically control Sonos speakers.","fork":true,"url":"https://api.github.com/repos/dundeemt/SoCo","forks_url":"https://api.github.com/repos/dundeemt/SoCo/forks","keys_url":"https://api.github.com/repos/dundeemt/SoCo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dundeemt/SoCo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dundeemt/SoCo/teams","hooks_url":"https://api.github.com/repos/dundeemt/SoCo/hooks","issue_events_url":"https://api.github.com/repos/dundeemt/SoCo/issues/events{/number}","events_url":"https://api.github.com/repos/dundeemt/SoCo/events","assignees_url":"https://api.github.com/repos/dundeemt/SoCo/assignees{/user}","branches_url":"https://api.github.com/repos/dundeemt/SoCo/branches{/branch}","tags_url":"https://api.github.com/repos/dundeemt/SoCo/tags","blobs_url":"https://api.github.com/repos/dundeemt/SoCo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dundeemt/SoCo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dundeemt/SoCo/git/refs{/sha}","trees_url":"https://api.github.com/repos/dundeemt/SoCo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dundeemt/SoCo/statuses/{sha}","languages_url":"https://api.github.com/repos/dundeemt/SoCo/languages","stargazers_url":"https://api.github.com/repos/dundeemt/SoCo/stargazers","contributors_url":"https://api.github.com/repos/dundeemt/SoCo/contributors","subscribers_url":"https://api.github.com/repos/dundeemt/SoCo/subscribers","subscription_url":"https://api.github.com/repos/dundeemt/SoCo/subscription","commits_url":"https://api.github.com/repos/dundeemt/SoCo/commits{/sha}","git_commits_url":"https://api.github.com/repos/dundeemt/SoCo/git/commits{/sha}","comments_url":"https://api.github.com/repos/dundeemt/SoCo/comments{/number}","issue_comment_url":"https://api.github.com/repos/dundeemt/SoCo/issues/comments{/number}","contents_url":"https://api.github.com/repos/dundeemt/SoCo/contents/{+path}","compare_url":"https://api.github.com/repos/dundeemt/SoCo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dundeemt/SoCo/merges","archive_url":"https://api.github.com/repos/dundeemt/SoCo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dundeemt/SoCo/downloads","issues_url":"https://api.github.com/repos/dundeemt/SoCo/issues{/number}","pulls_url":"https://api.github.com/repos/dundeemt/SoCo/pulls{/number}","milestones_url":"https://api.github.com/repos/dundeemt/SoCo/milestones{/number}","notifications_url":"https://api.github.com/repos/dundeemt/SoCo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dundeemt/SoCo/labels{/name}","releases_url":"https://api.github.com/repos/dundeemt/SoCo/releases{/id}","created_at":"2015-07-05T16:04:05Z","updated_at":"2015-07-05T16:04:06Z","pushed_at":"2015-09-09T23:13:13Z","git_url":"git://github.com/dundeemt/SoCo.git","ssh_url":"git@github.com:dundeemt/SoCo.git","clone_url":"https://github.com/dundeemt/SoCo.git","svn_url":"https://github.com/dundeemt/SoCo","homepage":"http://python-soco.com/","size":1941,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"SoCo:master","ref":"master","sha":"4b0926529a3ac139b183ff398e792f715e82d60b","user":{"login":"SoCo","id":3826649,"avatar_url":"https://avatars.githubusercontent.com/u/3826649?v=3","gravatar_id":"","url":"https://api.github.com/users/SoCo","html_url":"https://github.com/SoCo","followers_url":"https://api.github.com/users/SoCo/followers","following_url":"https://api.github.com/users/SoCo/following{/other_user}","gists_url":"https://api.github.com/users/SoCo/gists{/gist_id}","starred_url":"https://api.github.com/users/SoCo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SoCo/subscriptions","organizations_url":"https://api.github.com/users/SoCo/orgs","repos_url":"https://api.github.com/users/SoCo/repos","events_url":"https://api.github.com/users/SoCo/events{/privacy}","received_events_url":"https://api.github.com/users/SoCo/received_events","type":"Organization","site_admin":false},"repo":{"id":8763405,"name":"SoCo","full_name":"SoCo/SoCo","owner":{"login":"SoCo","id":3826649,"avatar_url":"https://avatars.githubusercontent.com/u/3826649?v=3","gravatar_id":"","url":"https://api.github.com/users/SoCo","html_url":"https://github.com/SoCo","followers_url":"https://api.github.com/users/SoCo/followers","following_url":"https://api.github.com/users/SoCo/following{/other_user}","gists_url":"https://api.github.com/users/SoCo/gists{/gist_id}","starred_url":"https://api.github.com/users/SoCo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SoCo/subscriptions","organizations_url":"https://api.github.com/users/SoCo/orgs","repos_url":"https://api.github.com/users/SoCo/repos","events_url":"https://api.github.com/users/SoCo/events{/privacy}","received_events_url":"https://api.github.com/users/SoCo/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SoCo/SoCo","description":"SoCo (Sonos Controller) is a simple Python class that allows you to programmatically control Sonos speakers.","fork":false,"url":"https://api.github.com/repos/SoCo/SoCo","forks_url":"https://api.github.com/repos/SoCo/SoCo/forks","keys_url":"https://api.github.com/repos/SoCo/SoCo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SoCo/SoCo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SoCo/SoCo/teams","hooks_url":"https://api.github.com/repos/SoCo/SoCo/hooks","issue_events_url":"https://api.github.com/repos/SoCo/SoCo/issues/events{/number}","events_url":"https://api.github.com/repos/SoCo/SoCo/events","assignees_url":"https://api.github.com/repos/SoCo/SoCo/assignees{/user}","branches_url":"https://api.github.com/repos/SoCo/SoCo/branches{/branch}","tags_url":"https://api.github.com/repos/SoCo/SoCo/tags","blobs_url":"https://api.github.com/repos/SoCo/SoCo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SoCo/SoCo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SoCo/SoCo/git/refs{/sha}","trees_url":"https://api.github.com/repos/SoCo/SoCo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SoCo/SoCo/statuses/{sha}","languages_url":"https://api.github.com/repos/SoCo/SoCo/languages","stargazers_url":"https://api.github.com/repos/SoCo/SoCo/stargazers","contributors_url":"https://api.github.com/repos/SoCo/SoCo/contributors","subscribers_url":"https://api.github.com/repos/SoCo/SoCo/subscribers","subscription_url":"https://api.github.com/repos/SoCo/SoCo/subscription","commits_url":"https://api.github.com/repos/SoCo/SoCo/commits{/sha}","git_commits_url":"https://api.github.com/repos/SoCo/SoCo/git/commits{/sha}","comments_url":"https://api.github.com/repos/SoCo/SoCo/comments{/number}","issue_comment_url":"https://api.github.com/repos/SoCo/SoCo/issues/comments{/number}","contents_url":"https://api.github.com/repos/SoCo/SoCo/contents/{+path}","compare_url":"https://api.github.com/repos/SoCo/SoCo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SoCo/SoCo/merges","archive_url":"https://api.github.com/repos/SoCo/SoCo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SoCo/SoCo/downloads","issues_url":"https://api.github.com/repos/SoCo/SoCo/issues{/number}","pulls_url":"https://api.github.com/repos/SoCo/SoCo/pulls{/number}","milestones_url":"https://api.github.com/repos/SoCo/SoCo/milestones{/number}","notifications_url":"https://api.github.com/repos/SoCo/SoCo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SoCo/SoCo/labels{/name}","releases_url":"https://api.github.com/repos/SoCo/SoCo/releases{/id}","created_at":"2013-03-13T22:56:39Z","updated_at":"2015-09-10T08:39:41Z","pushed_at":"2015-09-09T23:13:14Z","git_url":"git://github.com/SoCo/SoCo.git","ssh_url":"git@github.com:SoCo/SoCo.git","clone_url":"https://github.com/SoCo/SoCo.git","svn_url":"https://github.com/SoCo/SoCo","homepage":"http://python-soco.com/","size":4338,"stargazers_count":379,"watchers_count":379,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":97,"mirror_url":null,"open_issues_count":41,"forks":97,"open_issues":41,"watchers":379,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/SoCo/SoCo/pulls/352"},"html":{"href":"https://github.com/SoCo/SoCo/pull/352"},"issue":{"href":"https://api.github.com/repos/SoCo/SoCo/issues/352"},"comments":{"href":"https://api.github.com/repos/SoCo/SoCo/issues/352/comments"},"review_comments":{"href":"https://api.github.com/repos/SoCo/SoCo/pulls/352/comments"},"review_comment":{"href":"https://api.github.com/repos/SoCo/SoCo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/SoCo/SoCo/pulls/352/commits"},"statuses":{"href":"https://api.github.com/repos/SoCo/SoCo/statuses/73e2523e8ea7ff6c61715e62afcda2fbc6674040"}}}}