From 0817085adae1a9d58a786b116aadd8ba9df725f9 Mon Sep 17 00:00:00 2001
From: marcelveldt <m.vanderveldt@outlook.com>
Date: Mon, 11 Jan 2016 16:53:41 +0100
Subject: [PATCH] Add contextmenu for emby settings (used to update ratings)
 Add ratings sync for music files (get rating details from music files and
 sync back to emby)

---
 addon.xml                                     |    7 +
 contextmenu.py                                |  125 ++
 resources/language/English/strings.xml        |   11 +-
 resources/lib/api.py                          |   44 +-
 resources/lib/embydb_functions.py             |   12 +-
 resources/lib/itemtypes.py                    |   98 +-
 resources/lib/musicutils.py                   |  131 ++
 resources/lib/mutagen/__init__.py             |   43 +
 .../__pycache__/__init__.cpython-35.pyc       |  Bin 0 -> 914 bytes
 .../__pycache__/_compat.cpython-35.pyc        |  Bin 0 -> 2754 bytes
 .../__pycache__/_constants.cpython-35.pyc     |  Bin 0 -> 3099 bytes
 .../mutagen/__pycache__/_file.cpython-35.pyc  |  Bin 0 -> 7763 bytes
 .../__pycache__/_mp3util.cpython-35.pyc       |  Bin 0 -> 8626 bytes
 .../mutagen/__pycache__/_tags.cpython-35.pyc  |  Bin 0 -> 2970 bytes
 .../__pycache__/_toolsutil.cpython-35.pyc     |  Bin 0 -> 6102 bytes
 .../mutagen/__pycache__/_util.cpython-35.pyc  |  Bin 0 -> 17420 bytes
 .../__pycache__/_vorbis.cpython-35.pyc        |  Bin 0 -> 10304 bytes
 .../mutagen/__pycache__/aac.cpython-35.pyc    |  Bin 0 -> 10050 bytes
 .../mutagen/__pycache__/aiff.cpython-35.pyc   |  Bin 0 -> 10216 bytes
 .../mutagen/__pycache__/apev2.cpython-35.pyc  |  Bin 0 -> 20902 bytes
 .../__pycache__/easyid3.cpython-35.pyc        |  Bin 0 -> 17989 bytes
 .../__pycache__/easymp4.cpython-35.pyc        |  Bin 0 -> 10727 bytes
 .../mutagen/__pycache__/flac.cpython-35.pyc   |  Bin 0 -> 28214 bytes
 .../mutagen/__pycache__/m4a.cpython-35.pyc    |  Bin 0 -> 3561 bytes
 .../__pycache__/monkeysaudio.cpython-35.pyc   |  Bin 0 -> 2904 bytes
 .../mutagen/__pycache__/mp3.cpython-35.pyc    |  Bin 0 -> 9514 bytes
 .../__pycache__/musepack.cpython-35.pyc       |  Bin 0 -> 8017 bytes
 .../mutagen/__pycache__/ogg.cpython-35.pyc    |  Bin 0 -> 16965 bytes
 .../__pycache__/oggflac.cpython-35.pyc        |  Bin 0 -> 4862 bytes
 .../__pycache__/oggopus.cpython-35.pyc        |  Bin 0 -> 4763 bytes
 .../__pycache__/oggspeex.cpython-35.pyc       |  Bin 0 -> 4600 bytes
 .../__pycache__/oggtheora.cpython-35.pyc      |  Bin 0 -> 4781 bytes
 .../__pycache__/oggvorbis.cpython-35.pyc      |  Bin 0 -> 4602 bytes
 .../__pycache__/optimfrog.cpython-35.pyc      |  Bin 0 -> 2545 bytes
 .../__pycache__/trueaudio.cpython-35.pyc      |  Bin 0 -> 2924 bytes
 .../__pycache__/wavpack.cpython-35.pyc        |  Bin 0 -> 3738 bytes
 resources/lib/mutagen/_compat.py              |   86 +
 resources/lib/mutagen/_constants.py           |  199 ++
 resources/lib/mutagen/_file.py                |  253 +++
 resources/lib/mutagen/_mp3util.py             |  420 ++++
 resources/lib/mutagen/_tags.py                |  101 +
 resources/lib/mutagen/_toolsutil.py           |  231 ++
 resources/lib/mutagen/_util.py                |  550 +++++
 resources/lib/mutagen/_vorbis.py              |  330 +++
 resources/lib/mutagen/aac.py                  |  410 ++++
 resources/lib/mutagen/aiff.py                 |  357 +++
 resources/lib/mutagen/apev2.py                |  710 ++++++
 resources/lib/mutagen/asf/__init__.py         |  319 +++
 .../asf/__pycache__/__init__.cpython-35.pyc   |  Bin 0 -> 8567 bytes
 .../asf/__pycache__/_attrs.cpython-35.pyc     |  Bin 0 -> 15131 bytes
 .../asf/__pycache__/_objects.cpython-35.pyc   |  Bin 0 -> 15903 bytes
 .../asf/__pycache__/_util.cpython-35.pyc      |  Bin 0 -> 10603 bytes
 resources/lib/mutagen/asf/_attrs.py           |  438 ++++
 resources/lib/mutagen/asf/_objects.py         |  437 ++++
 resources/lib/mutagen/asf/_util.py            |  315 +++
 resources/lib/mutagen/easyid3.py              |  534 +++++
 resources/lib/mutagen/easymp4.py              |  285 +++
 resources/lib/mutagen/flac.py                 |  876 ++++++++
 resources/lib/mutagen/id3/__init__.py         | 1093 ++++++++++
 .../id3/__pycache__/__init__.cpython-35.pyc   |  Bin 0 -> 27785 bytes
 .../id3/__pycache__/_frames.cpython-35.pyc    |  Bin 0 -> 64560 bytes
 .../id3/__pycache__/_specs.cpython-35.pyc     |  Bin 0 -> 22816 bytes
 .../id3/__pycache__/_util.cpython-35.pyc      |  Bin 0 -> 4933 bytes
 resources/lib/mutagen/id3/_frames.py          | 1925 +++++++++++++++++
 resources/lib/mutagen/id3/_specs.py           |  635 ++++++
 resources/lib/mutagen/id3/_util.py            |  167 ++
 resources/lib/mutagen/m4a.py                  |  101 +
 resources/lib/mutagen/monkeysaudio.py         |   86 +
 resources/lib/mutagen/mp3.py                  |  362 ++++
 resources/lib/mutagen/mp4/__init__.py         | 1010 +++++++++
 .../mp4/__pycache__/__init__.cpython-35.pyc   |  Bin 0 -> 31145 bytes
 .../mp4/__pycache__/_as_entry.cpython-35.pyc  |  Bin 0 -> 14269 bytes
 .../mp4/__pycache__/_atom.cpython-35.pyc      |  Bin 0 -> 6248 bytes
 .../mp4/__pycache__/_util.cpython-35.pyc      |  Bin 0 -> 590 bytes
 resources/lib/mutagen/mp4/_as_entry.py        |  542 +++++
 resources/lib/mutagen/mp4/_atom.py            |  194 ++
 resources/lib/mutagen/mp4/_util.py            |   21 +
 resources/lib/mutagen/musepack.py             |  270 +++
 resources/lib/mutagen/ogg.py                  |  548 +++++
 resources/lib/mutagen/oggflac.py              |  161 ++
 resources/lib/mutagen/oggopus.py              |  158 ++
 resources/lib/mutagen/oggspeex.py             |  154 ++
 resources/lib/mutagen/oggtheora.py            |  148 ++
 resources/lib/mutagen/oggvorbis.py            |  159 ++
 resources/lib/mutagen/optimfrog.py            |   74 +
 resources/lib/mutagen/trueaudio.py            |   84 +
 resources/lib/mutagen/wavpack.py              |  125 ++
 resources/lib/utils.py                        |    2 +-
 88 files changed, 15314 insertions(+), 27 deletions(-)
 create mode 100644 contextmenu.py
 create mode 100644 resources/lib/musicutils.py
 create mode 100644 resources/lib/mutagen/__init__.py
 create mode 100644 resources/lib/mutagen/__pycache__/__init__.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/_compat.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/_constants.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/_file.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/_mp3util.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/_tags.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/_toolsutil.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/_util.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/_vorbis.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/aac.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/aiff.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/apev2.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/easyid3.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/easymp4.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/flac.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/m4a.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/monkeysaudio.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/mp3.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/musepack.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/ogg.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/oggflac.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/oggopus.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/oggspeex.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/oggtheora.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/oggvorbis.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/optimfrog.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/trueaudio.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/__pycache__/wavpack.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/_compat.py
 create mode 100644 resources/lib/mutagen/_constants.py
 create mode 100644 resources/lib/mutagen/_file.py
 create mode 100644 resources/lib/mutagen/_mp3util.py
 create mode 100644 resources/lib/mutagen/_tags.py
 create mode 100644 resources/lib/mutagen/_toolsutil.py
 create mode 100644 resources/lib/mutagen/_util.py
 create mode 100644 resources/lib/mutagen/_vorbis.py
 create mode 100644 resources/lib/mutagen/aac.py
 create mode 100644 resources/lib/mutagen/aiff.py
 create mode 100644 resources/lib/mutagen/apev2.py
 create mode 100644 resources/lib/mutagen/asf/__init__.py
 create mode 100644 resources/lib/mutagen/asf/__pycache__/__init__.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/asf/__pycache__/_attrs.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/asf/__pycache__/_objects.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/asf/__pycache__/_util.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/asf/_attrs.py
 create mode 100644 resources/lib/mutagen/asf/_objects.py
 create mode 100644 resources/lib/mutagen/asf/_util.py
 create mode 100644 resources/lib/mutagen/easyid3.py
 create mode 100644 resources/lib/mutagen/easymp4.py
 create mode 100644 resources/lib/mutagen/flac.py
 create mode 100644 resources/lib/mutagen/id3/__init__.py
 create mode 100644 resources/lib/mutagen/id3/__pycache__/__init__.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/id3/__pycache__/_frames.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/id3/__pycache__/_specs.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/id3/__pycache__/_util.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/id3/_frames.py
 create mode 100644 resources/lib/mutagen/id3/_specs.py
 create mode 100644 resources/lib/mutagen/id3/_util.py
 create mode 100644 resources/lib/mutagen/m4a.py
 create mode 100644 resources/lib/mutagen/monkeysaudio.py
 create mode 100644 resources/lib/mutagen/mp3.py
 create mode 100644 resources/lib/mutagen/mp4/__init__.py
 create mode 100644 resources/lib/mutagen/mp4/__pycache__/__init__.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/mp4/__pycache__/_as_entry.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/mp4/__pycache__/_atom.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/mp4/__pycache__/_util.cpython-35.pyc
 create mode 100644 resources/lib/mutagen/mp4/_as_entry.py
 create mode 100644 resources/lib/mutagen/mp4/_atom.py
 create mode 100644 resources/lib/mutagen/mp4/_util.py
 create mode 100644 resources/lib/mutagen/musepack.py
 create mode 100644 resources/lib/mutagen/ogg.py
 create mode 100644 resources/lib/mutagen/oggflac.py
 create mode 100644 resources/lib/mutagen/oggopus.py
 create mode 100644 resources/lib/mutagen/oggspeex.py
 create mode 100644 resources/lib/mutagen/oggtheora.py
 create mode 100644 resources/lib/mutagen/oggvorbis.py
 create mode 100644 resources/lib/mutagen/optimfrog.py
 create mode 100644 resources/lib/mutagen/trueaudio.py
 create mode 100644 resources/lib/mutagen/wavpack.py

diff --git a/addon.xml b/addon.xml
index 69e48b98..45aa64a7 100644
--- a/addon.xml
+++ b/addon.xml
@@ -16,6 +16,13 @@
   </extension>
   <extension point="xbmc.service" library="service.py" start="login">
   </extension>
+  <extension point="kodi.context.item" library="contextmenu.py">
+		<item>
+			<label>30401</label>
+            <description>Settings for the Emby Server</description>
+            <visible>!IsEmpty(ListItem.DBID)</visible>
+		</item>
+	</extension>
   <extension point="xbmc.addon.metadata">
     <platform>all</platform>
     <language>en</language>
diff --git a/contextmenu.py b/contextmenu.py
new file mode 100644
index 00000000..48fc2c87
--- /dev/null
+++ b/contextmenu.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+
+#################################################################################################
+
+import os
+import sys
+import urlparse
+
+import xbmc
+import xbmcaddon
+import xbmcgui
+
+addon_ = xbmcaddon.Addon(id='plugin.video.emby')
+addon_path = addon_.getAddonInfo('path').decode('utf-8')
+base_resource = xbmc.translatePath(os.path.join(addon_path, 'resources', 'lib')).decode('utf-8')
+sys.path.append(base_resource)
+
+import artwork
+import utils
+import clientinfo
+import downloadutils
+import librarysync
+import read_embyserver as embyserver
+import embydb_functions as embydb
+import kodidb_functions as kodidb
+import musicutils as musicutils
+import api
+
+def logMsg(msg, lvl=1):
+    utils.logMsg("%s %s" % ("Emby", "Contextmenu"), msg, lvl)
+
+
+#Kodi contextmenu item to configure the emby settings
+#for now used to set ratings but can later be used to sync individual items etc.
+if __name__ == '__main__':
+    
+    
+    itemid = xbmc.getInfoLabel("ListItem.DBID").decode("utf-8")
+    itemtype = xbmc.getInfoLabel("ListItem.DBTYPE").decode("utf-8")
+    if not itemtype and xbmc.getCondVisibility("Container.Content(albums)"): itemtype = "album"
+    if not itemtype and xbmc.getCondVisibility("Container.Content(artists)"): itemtype = "artist"
+    if not itemtype and xbmc.getCondVisibility("Container.Content(songs)"): itemtype = "song"
+    
+    logMsg("Contextmenu opened for itemid: %s  - itemtype: %s" %(itemid,itemtype),0)
+    
+    userid = utils.window('emby_currUser')
+    server = utils.window('emby_server%s' % userid)
+    embyconn = utils.kodiSQL('emby')
+    embycursor = embyconn.cursor()
+    kodiconn = utils.kodiSQL('music')
+    kodicursor = kodiconn.cursor()
+    
+    emby = embyserver.Read_EmbyServer()
+    emby_db = embydb.Embydb_Functions(embycursor)
+    kodi_db = kodidb.Kodidb_Functions(kodicursor)
+    
+    item = emby_db.getItem_byKodiId(itemid, itemtype)
+    if item:
+        embyid = item[0]
+        
+        item = emby.getItem(embyid)
+        
+        print item
+        
+        API = api.API(item)
+        userdata = API.getUserData()
+        likes = userdata['Likes']
+        favourite = userdata['Favorite']
+        
+        options=[]
+        if likes == True:
+            #clear like for the item
+            options.append(utils.language(30402))
+        if likes == False or likes == None:
+            #Like the item
+            options.append(utils.language(30403))
+        if likes == True or likes == None:
+            #Dislike the item
+            options.append(utils.language(30404)) 
+        if favourite == False:
+            #Add to emby favourites
+            options.append(utils.language(30405)) 
+        if favourite == True:
+            #Remove from emby favourites
+            options.append(utils.language(30406))
+        if itemtype == "song":
+            #Set custom song rating
+            options.append(utils.language(30407))
+        
+        #addon settings
+        options.append(utils.language(30408))
+        
+        #display select dialog and process results
+        header = utils.language(30401)
+        ret = xbmcgui.Dialog().select(header, options)
+        if ret != -1:
+            if options[ret] == utils.language(30402):
+                API.updateUserRating(embyid, deletelike=True)
+            if options[ret] == utils.language(30403):
+                API.updateUserRating(embyid, like=True)
+            if options[ret] == utils.language(30404):
+                API.updateUserRating(embyid, like=False)
+            if options[ret] == utils.language(30405):
+                 API.updateUserRating(embyid, favourite=True)
+            if options[ret] == utils.language(30406):
+                API.updateUserRating(embyid, favourite=False)
+            if options[ret] == utils.language(30407):
+                query = ' '.join(("SELECT rating", "FROM song", "WHERE idSong = ?" ))
+                kodicursor.execute(query, (itemid,))
+                currentvalue = int(round(float(kodicursor.fetchone()[0]),0))
+                newvalue = xbmcgui.Dialog().numeric(0, "Set custom song rating (0-5)", str(currentvalue))
+                if newvalue:
+                    newvalue = int(newvalue)
+                    if newvalue > 5: newvalue = "5"
+                    musicutils.updateRatingToFile(newvalue, API.getFilePath())
+                    like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(newvalue)
+                    API.updateUserRating(embyid, like, favourite, deletelike)
+                    query = ' '.join(( "UPDATE song","SET rating = ?", "WHERE idSong = ?" ))
+                    kodicursor.execute(query, (newvalue,itemid,))
+                    kodiconn.commit()
+
+            if options[ret] == utils.language(30408):
+                #Open addon settings
+                xbmc.executebuiltin("Addon.OpenSettings(plugin.video.emby)")
+
diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml
index 629d7d5b..303c36fc 100644
--- a/resources/language/English/strings.xml
+++ b/resources/language/English/strings.xml
@@ -258,8 +258,15 @@
     <string id="30311">Music Tracks</string>
     <string id="30312">Channels</string>  
 	
-	
-
+	<!-- contextmenu -->
+    <string id="30401">Emby options</string>
+    <string id="30402">Clear like for this item</string>
+    <string id="30403">Like this item</string>
+    <string id="30404">Dislike this item</string>
+    <string id="30405">Add to Emby favorites</string>
+    <string id="30406">Remove from Emby favorites</string>
+    <string id="30407">Set custom song rating</string>
+    <string id="30408">Emby addon settings</string>
     
 
 </strings>
diff --git a/resources/lib/api.py b/resources/lib/api.py
index 4dbdcbfc..ea651a6e 100644
--- a/resources/lib/api.py
+++ b/resources/lib/api.py
@@ -29,7 +29,7 @@ class API():
         played = False
         lastPlayedDate = None
         resume = 0
-        rating = 0
+        userrating = 0
 
         try:
             userdata = self.item['UserData']
@@ -40,15 +40,15 @@ class API():
         else:
             favorite = userdata['IsFavorite']
             likes = userdata.get('Likes')
-            # Rating for album and songs
+            # Userrating is based on likes and favourite
             if favorite:
-                rating = 5
+                userrating = 5
             elif likes:
-                rating = 3
+                userrating = 3
             elif likes == False:
-                rating = 1
+                userrating = 0
             else:
-                rating = 0
+                userrating = 1
 
             lastPlayedDate = userdata.get('LastPlayedDate')
             if lastPlayedDate:
@@ -71,11 +71,12 @@ class API():
         return {
 
             'Favorite': favorite,
+            'Likes': likes,
             'PlayCount': playcount,
             'Played': played,
             'LastPlayedDate': lastPlayedDate,
             'Resume': resume,
-            'Rating': rating
+            'UserRating': userrating
         }
 
     def getPeople(self):
@@ -259,11 +260,12 @@ class API():
         item = self.item
         userdata = item['UserData']
 
-        checksum = "%s%s%s%s%s%s" % (
+        checksum = "%s%s%s%s%s%s%s" % (
             
             item['Etag'], 
             userdata['Played'],
             userdata['IsFavorite'],
+            userdata.get('Likes',''),
             userdata['PlaybackPositionTicks'],
             userdata.get('UnplayedItemCount', ""),
             userdata.get('LastPlayedDate', "")
@@ -377,4 +379,28 @@ class API():
                 # Local path scenario, with special videotype
                 filepath = filepath.replace("/", "\\")
 
-        return filepath
\ No newline at end of file
+        return filepath
+    
+    def updateUserRating(self, itemid, like=None, favourite=None, deletelike=False):
+        #updates the userrating to Emby
+        import downloadutils
+        doUtils = downloadutils.DownloadUtils()
+        
+        if favourite != None and favourite==True:
+            url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
+            doUtils.downloadUrl(url, type="POST")
+        elif favourite != None and favourite==False:
+            url = "{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid
+            doUtils.downloadUrl(url, type="DELETE")
+            
+        if not deletelike and like != None and like==True:
+            url = "{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=true&format=json" % itemid
+            doUtils.downloadUrl(url, type="POST")
+        if not deletelike and like != None and like==False:
+            url = "{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=false&format=json" % itemid
+            doUtils.downloadUrl(url, type="POST")
+        if deletelike:
+            url = "{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid
+            doUtils.downloadUrl(url, type="DELETE")
+
+        self.logMsg( "updateUserRating on embyserver for embyId: %s - like: %s - favourite: %s - deletelike: %s" %(itemid, like, favourite, deletelike), 0)
diff --git a/resources/lib/embydb_functions.py b/resources/lib/embydb_functions.py
index 1afa0d84..df6cc54c 100644
--- a/resources/lib/embydb_functions.py
+++ b/resources/lib/embydb_functions.py
@@ -128,10 +128,11 @@ class Embydb_Functions():
             "FROM emby",
             "WHERE emby_id = ?"
         ))
-        embycursor.execute(query, (embyid,))
-        item = embycursor.fetchone()
-
-        return item
+        try:
+            embycursor.execute(query, (embyid,))
+            item = embycursor.fetchone()
+            return item
+        except: return None
 
     def getItem_byView(self, mediafolderid):
 
@@ -291,4 +292,5 @@ class Embydb_Functions():
     def removeItem(self, embyid):
 
         query = "DELETE FROM emby WHERE emby_id = ?"
-        self.embycursor.execute(query, (embyid,))
\ No newline at end of file
+        self.embycursor.execute(query, (embyid,))
+        
\ No newline at end of file
diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py
index e69c5b8c..3a6f9060 100644
--- a/resources/lib/itemtypes.py
+++ b/resources/lib/itemtypes.py
@@ -18,7 +18,7 @@ import utils
 import embydb_functions as embydb
 import kodidb_functions as kodidb
 import read_embyserver as embyserver
-
+import musicutils as musicutils
 ##################################################################################################
 
 
@@ -1931,7 +1931,6 @@ class Music(Items):
             if not pdialog and self.contentmsg:
                 self.contentPop(title)
 
-
     def add_updateArtist(self, item, artisttype="MusicArtist"):
         # Process a single artist
         kodiversion = self.kodiversion
@@ -2045,7 +2044,7 @@ class Music(Items):
         genres = item.get('Genres')
         genre = " / ".join(genres)
         bio = API.getOverview()
-        rating = userdata['Rating']
+        rating = userdata['UserRating']
         artists = item['AlbumArtists']
         if not artists:
             artists = item['ArtistItems']
@@ -2213,11 +2212,18 @@ class Music(Items):
         else:
             track = disc*2**16 + tracknumber
         year = item.get('ProductionYear')
-        bio = API.getOverview()
         duration = API.getRuntime()
-        rating = userdata['Rating']
-
-
+        
+        #the server only returns the rating based on like/love and not the actual rating from the song
+        rating = userdata['UserRating']
+        
+        #the server doesn't support comment on songs so this will always be empty
+        comment = API.getOverview()
+        
+        #if enabled, try to get the rating and comment value from the file itself
+        if not self.directstream:
+            rating, comment = self.getSongRatingAndComment(itemid, rating, API)
+        
         ##### GET THE FILE AND PATH #####
         if self.directstream:
             path = "%s/emby/Audio/%s/" % (self.server, itemid)
@@ -2264,11 +2270,11 @@ class Music(Items):
                 "UPDATE song",
                 "SET idAlbum = ?, strArtists = ?, strGenres = ?, strTitle = ?, iTrack = ?,",
                     "iDuration = ?, iYear = ?, strFilename = ?, iTimesPlayed = ?, lastplayed = ?,",
-                    "rating = ?",
+                    "rating = ?, comment = ?",
                 "WHERE idSong = ?"
             ))
             kodicursor.execute(query, (albumid, artists, genre, title, track, duration, year,
-                filename, playcount, dateplayed, rating, songid))
+                filename, playcount, dateplayed, rating, comment, songid))
 
             # Update the checksum in emby table
             emby_db.updateReference(itemid, checksum)
@@ -2435,7 +2441,7 @@ class Music(Items):
         checksum = API.getChecksum()
         userdata = API.getUserData()
         runtime = API.getRuntime()
-        rating = userdata['Rating']
+        rating = userdata['UserRating']
 
         # Get Kodi information
         emby_dbitem = emby_db.getItem_byId(itemid)
@@ -2450,6 +2456,7 @@ class Music(Items):
             # Process playstates
             playcount = userdata['PlayCount']
             dateplayed = userdata['LastPlayedDate']
+            rating, comment = self.getSongRatingAndComment(itemid, rating, API)
             
             query = "UPDATE song SET iTimesPlayed = ?, lastplayed = ?, rating = ? WHERE idSong = ?"
             kodicursor.execute(query, (playcount, dateplayed, rating, kodiid))
@@ -2461,6 +2468,77 @@ class Music(Items):
 
         emby_db.updateReference(itemid, checksum)
 
+    def getSongRatingAndComment(self, embyid, emby_rating, API):
+        previous_values = None
+        filename = API.getFilePath()
+        rating = 0
+        emby_rating = int(round(emby_rating,0))
+        file_rating, comment = musicutils.getSongTags(filename)
+        
+        currentvalue = None
+        kodiid = self.emby_db.getItem_byId(embyid)[0]
+        query = ' '.join(("SELECT rating", "FROM song", "WHERE idSong = ?" ))
+        self.kodicursor.execute(query, (kodiid,))
+        currentvalue = int(round(float(self.kodicursor.fetchone()[0]),0))
+        
+        #only proceed if we actually have a rating from the file
+        if file_rating == None and currentvalue:
+            return (currentvalue, comment)
+        elif file_rating == None and not currentvalue:
+            return (emby_rating, comment)
+        
+        file_rating = int(round(file_rating,0))
+        self.logMsg("getSongRatingAndComment --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue))
+        
+        updateFileRating = False
+        updateEmbyRating = False
+
+        if currentvalue != None:
+            # we need to translate the emby values...
+            if emby_rating == 1 and currentvalue == 2:
+                emby_rating = 2
+            if emby_rating == 3 and currentvalue == 4:
+                emby_rating = 4
+                
+            if (emby_rating == file_rating) and (file_rating != currentvalue):
+                #the rating has been updated from kodi itself, update change to both emby ands file
+                rating = currentvalue
+                updateFileRating = True
+                updateEmbyRating = True
+            elif (emby_rating != currentvalue) and (file_rating == currentvalue):
+                #emby rating changed - update the file
+                rating = emby_rating
+                updateFileRating = True  
+            elif (file_rating != currentvalue) and (emby_rating == currentvalue):
+                #file rating was updated, sync change to emby
+                rating = file_rating
+                updateEmbyRating = True
+            elif (emby_rating != currentvalue) and (file_rating != currentvalue):
+                #both ratings have changed (corner case) - the highest rating wins...
+                if emby_rating > file_rating:
+                    rating = emby_rating
+                    updateFileRating = True
+                else:
+                    rating = file_rating
+                    updateEmbyRating = True
+            else:
+                #nothing has changed, just return the current value
+                rating = currentvalue
+        else:      
+            # no rating yet in DB, always prefer file details...
+            rating = file_rating
+            updateEmbyRating = True
+            
+        if updateFileRating:
+            musicutils.updateRatingToFile(rating, filename)
+            
+        if updateEmbyRating:
+            # sync details to emby server. Translation needed between ID3 rating and emby likes/favourites:
+            like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(rating)
+            API.updateUserRating(embyid, like, favourite, deletelike)
+        
+        return (rating, comment)
+        
     def remove(self, itemid):
         # Remove kodiid, fileid, pathid, emby reference
         emby_db = self.emby_db
diff --git a/resources/lib/musicutils.py b/resources/lib/musicutils.py
new file mode 100644
index 00000000..a9add080
--- /dev/null
+++ b/resources/lib/musicutils.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+
+#################################################################################################
+
+import os
+import xbmc, xbmcaddon, xbmcvfs
+import utils
+from mutagen.flac import FLAC
+from mutagen.id3 import ID3
+from mutagen import id3
+
+#################################################################################################
+
+# Helper for the music library, intended to fix missing song ID3 tags on Emby
+
+def logMsg(msg, lvl=1):
+    utils.logMsg("%s %s" % ("Emby", "musictools"), msg, lvl)
+
+def getRealFileName(filename):
+    #get the filename path accessible by python if possible...
+    isTemp = False
+    
+    if not xbmcvfs.exists(filename):
+        logMsg( "File does not exist! %s" %(filename), 0)
+        return (False, "")
+    
+    # determine if our python module is able to access the file directly...
+    if os.path.exists(filename):
+        filename = filename
+    elif os.path.exists(filename.replace("smb://","\\\\").replace("/","\\")):
+        filename = filename.replace("smb://","\\\\").replace("/","\\")
+    else:
+        #file can not be accessed by python directly, we copy it for processing...
+        isTemp = True
+        if "/" in filename: filepart = filename.split("/")[-1]
+        else: filepart = filename.split("\\")[-1]
+        tempfile = "special://temp/"+filepart
+        xbmcvfs.copy(filename, tempfile)
+        filename = xbmc.translatePath(tempfile).decode("utf-8")
+        
+    return (isTemp,filename)
+
+def getEmbyRatingFromKodiRating(rating):
+    # Translation needed between Kodi/ID3 rating and emby likes/favourites:
+    # 3+ rating in ID3 = emby like
+    # 5+ rating in ID3 = emby favourite
+    # rating 0 = emby dislike
+    # rating 1-2 = emby no likes or dislikes (returns 1 in results)
+    favourite = False
+    deletelike = False
+    like = False
+    if (rating >= 3): like = True
+    if (rating == 0): like = False
+    if (rating == 1 or rating == 2): deletelike = True
+    if (rating >= 5): favourite = True
+    return(like, favourite, deletelike)
+    
+def getSongTags(file):
+    # Get the actual ID3 tags for music songs as the server is lacking that info
+    rating = None
+    comment = ""
+    
+    isTemp,filename = getRealFileName(file)
+    logMsg( "getting song ID3 tags for " + filename, 0)
+    
+    try:
+        if filename.lower().endswith(".flac"):
+            audio = FLAC(filename)
+            if audio.get("comment"):
+                comment = audio.get("comment")[0]
+            if audio.get("rating"):
+                rating = float(audio.get("rating")[0])
+                #flac rating is 0-100 and needs to be converted to 0-5 range
+                if rating > 5: rating = (rating / 100) * 5
+        elif filename.lower().endswith(".mp3"):
+            audio = ID3(filename)
+            if audio.get("comment"):
+                comment = audio.get("comment")[0]
+            if audio.get("POPM:Windows Media Player 9 Series"):
+                if audio.get("POPM:Windows Media Player 9 Series").rating:
+                    rating = float(audio.get("POPM:Windows Media Player 9 Series").rating)
+                    #POPM rating is 0-255 and needs to be converted to 0-5 range
+                    if rating > 5: rating = (rating / 255) * 5
+        else:
+            logMsg( "Not supported fileformat or unable to access file: %s" %(filename), 0)
+        rating = int(round(rating,0))
+    
+    except Exception as e:
+        #file in use ?
+        logMsg("Exception in getSongTags %s" %e,0)
+        
+    #remove tempfile if needed....
+    if isTemp: xbmcvfs.delete(filename)
+        
+    return (rating, comment)
+
+def updateRatingToFile(rating, file):
+    #update the rating from Emby to the file
+    
+    isTemp,filename = getRealFileName(file)
+    logMsg( "setting song rating: %s for filename: %s" %(rating,filename), 0)
+    
+    if not filename:
+        return
+    
+    try:
+        if filename.lower().endswith(".flac"):
+            audio = FLAC(filename)
+            calcrating = int(round((float(rating) / 5) * 100, 0))
+            audio["rating"] = str(calcrating)
+            audio.save()
+        elif filename.lower().endswith(".mp3"):
+            audio = ID3(filename)
+            calcrating = int(round((float(rating) / 5) * 255, 0))
+            audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1))
+            audio.save()
+        else:
+            logMsg( "Not supported fileformat: %s" %(filename), 0)
+            
+        #remove tempfile if needed....
+        if isTemp:
+            xbmcvfs.delete(file)
+            xbmcvfs.copy(filename,file)
+            xbmcvfs.delete(filename)
+            
+    except Exception as e:
+        #file in use ?
+        logMsg("Exception in updateRatingToFile %s" %e,0)
+        
+    
+    
\ No newline at end of file
diff --git a/resources/lib/mutagen/__init__.py b/resources/lib/mutagen/__init__.py
new file mode 100644
index 00000000..03ad7aee
--- /dev/null
+++ b/resources/lib/mutagen/__init__.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2005  Michael Urman
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+
+"""Mutagen aims to be an all purpose multimedia tagging library.
+
+::
+
+    import mutagen.[format]
+    metadata = mutagen.[format].Open(filename)
+
+`metadata` acts like a dictionary of tags in the file. Tags are generally a
+list of string-like values, but may have additional methods available
+depending on tag or format. They may also be entirely different objects
+for certain keys, again depending on format.
+"""
+
+from mutagen._util import MutagenError
+from mutagen._file import FileType, StreamInfo, File
+from mutagen._tags import Metadata, PaddingInfo
+
+version = (1, 31)
+"""Version tuple."""
+
+version_string = ".".join(map(str, version))
+"""Version string."""
+
+MutagenError
+
+FileType
+
+StreamInfo
+
+File
+
+Metadata
+
+PaddingInfo
diff --git a/resources/lib/mutagen/__pycache__/__init__.cpython-35.pyc b/resources/lib/mutagen/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0d767fdced0b459f7462e04b1308d99da0fe85f6
GIT binary patch
literal 914
zcmZWn&2AGh5T5->vUQt+1XLW5@R3L~5<*-mKu8EABnp*UE=$nX$xaft_Il;@M%pWV
z2i}3_;pQu+9=UR2oTh?`UCr2@8GrN5jQ55^c4zYI(~kt;C;aoJw0}j?d?i%^<fJ1g
zV^9(381fjRa85uapi{_G&^^d|p!<;buY4(}0q6|!4D=S{TV$n$K{<f4ZNMGCAs~Z`
z7%n2ncL50#Q<5GDVPHlQZe?&hV&Yo_aM(Jrz={j08}1CBA{TU0ir1~JO@myw%1Mo-
z6r4gAvRZH@r&ic*%-F#JW1K#zYhxY7uF=My=f-N`zN~#3ohXG9{KbF%@y8mgM{}vL
z5*kO0on8l@axrraWjZH9yp%I1O+`7mnFksTmlbzQ<l*j^p9G7r$f*>x#J=N#DcQIX
z-Z)F7_t#J>p;~Mn^Jz<DMaP$7MfhbY*YJu{`lTreYbB%-Q-!QVqF9Ei4P_LxSbkL<
zrCnmTh7qb+w+<^OEmHPU&gW?9$j$T&sS-wa_zbNRMEo2(;vg1be*=F5%r5DPz$o%N
zoAJD{*4R<vv$s_4Nmrw1$IhbA@2a`+X|RrBpB-*G?YECYP1Pc}FKNnf`H+;4#vewp
z&1h@4LV{a<P!y$^6@?#OkFIE)R5lLbe{G@5mL4?N@QQ8krZ>#P_Jd;(9D^0whNk7y
zGb1aXXi@uwhTwNMj^b(}-@)DumxB8J<n_VisH5grPoGWR%ju+%4)<#@JE!N_Og8K$
nMIkHcieg-MK6|cB*(!W_j{urIQvLXL^dQ=c`^o(zjj#Vd+O!M@

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/_compat.cpython-35.pyc b/resources/lib/mutagen/__pycache__/_compat.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..93f423d567c397fd76892bf3e94725f87947d426
GIT binary patch
literal 2754
zcmbtW-EQ1O6h32nf8)*OuOUCBq#z_Ll_;Wuizo_J!%d~Kjfh%_CCKvHlf=!gcNsfr
z62T4O33v-0fD7J$XP8^AdV$1sulUY*y-Aidsf0H(bA0CboAW=JZ#3$^cK&(ZtP=f2
zmmUlBBaHM9K@{OnP?xApB#B-M<}5}Q9a_}3NLu8!ZC=#hM%JdTLp_Ju4he~+RB+3N
zTOq-S4t1;4t5LfKsXDbMNV)`K51<W_lO&slo?@asO>&0XGbCrJJxg+q+IJ`daG8FA
zUh@=HD5@IzJ&I}ty+BdDpcg5cDCi}U%M`hEE}%<;qDk}RT}u8WX_NZhC<rs&ziftM
z%xD^JiLDY6ONG_WmERLX&6$AOlSb`(Y@z&sm5-(h1!gIlE@&8nt&t(v8W}QI&@cpI
zIUk0=-ke^d2=*=*^;amuR$&Svw&N|m=9so#b2vSrt2>#Bll>>pKs)hSJP3J%A6|KE
zVWbUAKO)YU1-od+6D|H)CQ<q-&JH|FJDEJmv<=R0q>w!HW^HSIcl$Iu=qC?9-+dZ)
zcGEbMpALfXFxZ#rZtp0=n(X>vzc&c7)xoJM;j?>uo>4-W_Ymo39$tAEh3X)3h0dwa
zrUrP0(MzG4h>Q~XRD-mxZghiQCki(2<1NJ?<zjkw{5s<w2i+ry1Jncqu$>o27`zX`
zGU)FPTxH-xd3qD<N7urR0>u8vj7u(}<1j3{qH~>|9~XdUYmwYrQBnnQDnH2v4_1tg
z4_2~{w_aZ{?3F9*E0&E^SO*|dERV`znbfkO4ac_#&EaC}3q#vH`$k~Lvs=32`)Q_p
zU)OxUbDGK2_dQ2feLsrB%=gtaPH0c~{!tQ#{YZi{0}d>-!)`ieqn=D(1p`0pGbA$H
zV{_R{B$~ptc&I!1*OkY7){|#fQs2g&$+S5TP!oB7=oAFe0#|@J5~(i&H3g|L4NNwC
z3I2bxVdOKet2}J`m+Puztmn|3TnGAS1j<1C@H*1?JG|te89>p4*UYP+{55T{F)tAj
zbL3Bgl6!@K&GJJ5G`B*416LZAzSYvDAPwW#E9-ik#z~q5Nhq}qPwR3dIW}qmO6amo
z%nR4GXQ?t4RpDu@Zg~GOM#=?A2u@SXnEColV`t|E;^H}{QA4s+6Fa&z>;Z2r(Wj8Q
zAt<-Nmv3zWU;i(B=ax>;x*45|1c5`j;TlBXzA_ELZjD@7<O+Kr<!%e1@8<}4z&;g}
zXW|R-gUz=a=Q+9^hZC7@t*&WtJQlt;wGH%fc`{I&+l<Kvl<91_=(npI-F_H!)6Lb9
zoIwEW+sek^0ER-&YJPZS;4O@F5mR21Fpe)Ji*w*-^Iu4=u%6^=$<I^}$_{QE&(>Bp
z&?OT)kmD^;SB4Gn8^-5@NGR?ZBez|-ejMyTfr};})Ero2jxi19Iru|B?>=heG}Z30
zRB7B#{5aX`YkT|K&v<&M*hV#vsa*?;ds$b*VSCc%e#`oO^g2FYJKcV=uS-1p+K!V<
zH!#V4sr>A8AX9ahp(?{}solI0=6kxv*R`Q_`9uZDKI%8@4KN)j?O^iF?W?$DDsEG1
zlFu|cnC_$<c_Kq&qPAj~fqx!!$sifWUGjyg_Te4e>qkdjxyf%S``Wc<Ma3$MDZyh3
il#5>(D}I)1^P6kAR^4jW3Mt;L;g?I9{dyti+W!Ksf!+}S

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/_constants.cpython-35.pyc b/resources/lib/mutagen/__pycache__/_constants.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..368544dcafdaf72965a51adee92899dd834cab69
GIT binary patch
literal 3099
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
Fe*pD4cI^NF

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/_file.cpython-35.pyc b/resources/lib/mutagen/__pycache__/_file.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2e8bbe118279fb735d7106993b1282ab526e22c2
GIT binary patch
literal 7763
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/_mp3util.cpython-35.pyc b/resources/lib/mutagen/__pycache__/_mp3util.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e242cafa64776176be78a6c7ef2f18a25b8a3e70
GIT binary patch
literal 8626
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/_tags.cpython-35.pyc b/resources/lib/mutagen/__pycache__/_tags.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b49b1dfcbd2a2fce928e9ac92ebfbdbf514f2518
GIT binary patch
literal 2970
zcma)8OK;mo5MGM3Br9?aC#jRBuRSz?d5938=tW4<7;aFcP1GQ28q_e#tjLu_n-6ul
zvLPV_iYDj&kN%sUdF`b+_S#dwSyD7(yG3d5aJ4%-JCAQ>xV^E_*?a!iv*(u>`-9aU
z2lM+F*=>+71I7Z2y*5-b_R41a7FHW9u+6H?%2lj5Z0N4}I8{9j%<f}kPeH(lvk<rs
zUW@J93}W^jL)S<i1mL3mCJXShY33~!;J<C=Z5FJvpkwBkT*7VL{L&K<1Yt6Hn2b~L
z2>0!dGOdKK`K;LBA$C+ObeJYQ9UV$v_c|Sp=i6zR@slu$_(*~~%VfZH%Jo!oUqsPJ
z_(yy^PkeJyPld*ELcDWu8VL~0GjWXTxfYYGl-utY^68F1j(NQBR`Pa8nLe-S?DmZ$
z;-yDPJy(g*xi^v8!+9}}v{xFl<E~V%>6lH^c@(UUlKLV}=ZWU&xN-vQg4TTJ-d%p%
z<s(71rir=Q*qbI=;zSlMaXNt?AZ6u1h@qe3Fv8wg%azSNcn~YTo{Q+zC+F{u!^u?h
zW1$vs$4n*^E?j;pw1^Mi3FZ<3FiMFioFp-+B(>kGSzS7Whw&_u#83pbjEwMoY2rA~
zOi-rMNbAQ^Px~(}aOew8TWJ7~zZLkNIrj#rWxh=m54{P{dl=a^$V-d8WX!{)VI~IL
zL)RL(jl5NQFL%hNu8p^h#)@Pp2jn-Vnt1))+v^`ZSwMy4)6Whbg`<Nk)biF0*b@_(
z9mI10OC|@N!Pjh&w>>XRLhX4saXKSO%v!gWS8I^!typ}Ew(-!dG>qm$OpsL2{16kH
z-MID>6gQ^Of<p_&o<l6Jv7<GnzBb%ovDlHr)E8v%kOWwGVTxoOy6xrfgol3#(8)CP
zr(CO0W|U-b1`?4ApjKpQB1TaWDawKK2qHul0tzhoQ;|$0#cVuBzDRy7RfhatWgtoE
z7pc_eyXTUj4q_AMf?qFYvJauUS_>H*{H6nky$L|VKjB105Ez7PhcUNdLTQ5P+G-6O
zxibaYb2~~;OneK{qMa)jz|X}-_6`VZZ&_Q7P5e2Hmc9JA9?lES`J@Cr^sX@Z34S0%
zQ1z(I)OH!-R}4Wi;eCa0VbdVgQXAlg1xD+26bI!|u{*1E*Q4u8Fn^9C8NrrWU8~br
zUazZJXRMG%2y`$E@;D{{nueC>Bo@JkKvaOuW@DRe58cLKdF{^4%#@<LH-l9b!t|c&
z<PHUik{}VW^t_t?En~@9^}(s+OnKe|++C)NeJ))JmVw(*TVTsOo)@RVJi-Ne*Ykcv
zeJifZTb>uBK4bz2MINg*&B&K(lgMQv<f0m(@GJ7fI)({%_j0q*Tx;50r@P&4nmr$T
z)kEU%WBf<oM4YRRi5^;YOiWD2M6Y~}CW~f@pB-wK^wacH>YMGyQj0)nu{_*OxL7sK
zAoTUEC_IvHx5^bWN5`aIKn;ZZ5qc)49vF*cLEVP7P^ikY871iBqJS0kG;fvltilHw
zO5+<cmV*GPS(oaI`k=%wVYk741?DR3=1!D~ph`KRCRyG*I-&Pjw^MlN<D-my*II6^
zjJdc@Qk;x7l=gt?FD9XioXM{YQvGn%d1UZFsZ^C}sQ19HZV)*Qzlr>HtbK=(nWVLr
z8<hfN#o(=6rrY<3w{|<S)@>}W4aG5hZYrNCp~7_J-oUlg`#7;m<u>(cg6kPI(10M|
z`$`YHrU&i(Pc?piD&rK2P(R{hmBzf(<Mf;7VZ!4yqpzimiamX0#Zn|0J}egK00J$#
z4wlsym@<Nk4=XCjMmQmg3@Y$L%V+EbOwM^cZw6>DTK-TO+gff`PA~S{w#jvMg^ph(
z@-7jR%@i>8A(4-WoQBiPOiZc$e}w_8GS+pF%?;$Ty}5?6i}rwi-z>g0O>UdTi{#|(
PJ4L0rXG#MBWPA4?BbT<w

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/_toolsutil.cpython-35.pyc b/resources/lib/mutagen/__pycache__/_toolsutil.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cabceb1e10e6403e5472a8689571666cd647a182
GIT binary patch
literal 6102
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
UpK+#}b6(?2qc(M<(P&)xALiS7%>V!Z

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/_util.cpython-35.pyc b/resources/lib/mutagen/__pycache__/_util.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..199c081ba21657517d7297d2389319f50067db88
GIT binary patch
literal 17420
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/_vorbis.cpython-35.pyc b/resources/lib/mutagen/__pycache__/_vorbis.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5a667067893e573ea9145da73a1180c5996a0eb6
GIT binary patch
literal 10304
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
PTb(Rqr*rh*#lQSNYB63I

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/aac.cpython-35.pyc b/resources/lib/mutagen/__pycache__/aac.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c976f6f4650780c5745425975ff709a31937ca5d
GIT binary patch
literal 10050
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
SUi`)48;ka0Z}9@N^#28zz&&mN

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/aiff.cpython-35.pyc b/resources/lib/mutagen/__pycache__/aiff.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f9a42314d9662e22c2aa38fff07809f81db17f50
GIT binary patch
literal 10216
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/apev2.cpython-35.pyc b/resources/lib/mutagen/__pycache__/apev2.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..943ab32c895bf467e60af7587acf090672c40d08
GIT binary patch
literal 20902
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/easyid3.cpython-35.pyc b/resources/lib/mutagen/__pycache__/easyid3.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a1031ba9cedbd41184ef7bbc08feb167385d2f96
GIT binary patch
literal 17989
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
R_9DM4v$yzceW8!s{|97&yEgy;

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/easymp4.cpython-35.pyc b/resources/lib/mutagen/__pycache__/easymp4.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f970678fbfeb6d5c63c1baf98504447dddb60746
GIT binary patch
literal 10727
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
QqHwsdw}#2fO7JZH7m7(;rT_o{

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/flac.cpython-35.pyc b/resources/lib/mutagen/__pycache__/flac.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..703d588251c17e036a3bfde0b1b41be9202d3afd
GIT binary patch
literal 28214
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
Cy}z6Q

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/m4a.cpython-35.pyc b/resources/lib/mutagen/__pycache__/m4a.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..43ede4e2c474159647db96d95d9be8b501954a47
GIT binary patch
literal 3561
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/monkeysaudio.cpython-35.pyc b/resources/lib/mutagen/__pycache__/monkeysaudio.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8c719f0ede6e8b0bf573377a20e0fd3b1884bb06
GIT binary patch
literal 2904
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
W0eH@;<2yakLuvKgwZ__Dt@A&tI-YX?

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/mp3.cpython-35.pyc b/resources/lib/mutagen/__pycache__/mp3.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8a0be64316ee96d34ca96f91a8414a6ead76495b
GIT binary patch
literal 9514
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
RoN{S$GBf_4<41Fu{{bT;EF=H`

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/musepack.cpython-35.pyc b/resources/lib/mutagen/__pycache__/musepack.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cf4ca9e5582986eebe0a4d9b7941a217ba4bd059
GIT binary patch
literal 8017
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/ogg.cpython-35.pyc b/resources/lib/mutagen/__pycache__/ogg.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0eb57b0c6c9310509e2fb8e83ed55f48342796ce
GIT binary patch
literal 16965
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
Q(WBPs^jvP{f%@_P0W}0OjQ{`u

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/oggflac.cpython-35.pyc b/resources/lib/mutagen/__pycache__/oggflac.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ab7dadf0f0e45087906650fa6558056a002dc4ab
GIT binary patch
literal 4862
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
F>%R{d_Jsfd

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/oggopus.cpython-35.pyc b/resources/lib/mutagen/__pycache__/oggopus.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fa6e46fa18b4658ac155f038b62d93a06acf2aee
GIT binary patch
literal 4763
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/oggspeex.cpython-35.pyc b/resources/lib/mutagen/__pycache__/oggspeex.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9cb1566580dc286aebb8d1aaf8e69c4600458c69
GIT binary patch
literal 4600
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
N;`i3-hpQ`=`9J>^?#=)J

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/oggtheora.cpython-35.pyc b/resources/lib/mutagen/__pycache__/oggtheora.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a7a4e55736d3e214718eae3c2d039db2a76f609c
GIT binary patch
literal 4781
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
Wc_iH{cs6n?dHmg2xw*3J<o*kMv;RQ=

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/oggvorbis.cpython-35.pyc b/resources/lib/mutagen/__pycache__/oggvorbis.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9acf6dc536fafd567cf5ae3c705815bf35d5b8ea
GIT binary patch
literal 4602
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/optimfrog.cpython-35.pyc b/resources/lib/mutagen/__pycache__/optimfrog.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0bcbbb85864a5bceedba00e8f3b89b42776baef1
GIT binary patch
literal 2545
zcmZuyTW=dh6h6DQ*Y?J)LurFbp$vs0T&1o^sVbGKN?VaOQk7JV5Rw&XwegPaP4{AE
z#tpHO7gGKJZ~TP*3H}6c`^p2#8_zuPo$)1U#Y%H_&UMb5+pIL3{%?E#{P1Uu=r20=
zm9byP(r*DGqAs9AkxNmD1|{lZU#6%+iA!A&J&LN7lqfAzw@eQ6YLrwc^{DGj@j4|{
zN^8`uQCg>Nol>8=J~>Y#pQ0stg9DaG*69S+m*{EKpr~o7O_H9aHbA|=Zm_IFvSz7Z
zH>nGHg?8Gl@OOu3dt~Bt<K^~K5ssrc7uqNprdk}tW+)!L`2O{pFyTOVeE&=`)*=*1
zu5}_cwpOo~ry~V4&NDGm`9OtfM{F7`(lE}%AP*A(HIw^FM&q6op#kUTI31_L45iTV
zM2fsWGuDq`c~u+?@fRbh(%6_d8;HHw_;DmN6Zc>b$C+G(hL7y$N`wi{$VOws2lHt<
zPU9q0)4T8~-OU&NIY(E;-WY;bb{G$a$)V6A*^9#j-b8R#%!OxuMWG2f5*Z|4OU0Mf
z;@i$$5oVEK3ld%^+-5u)<;uti!W-}`%|VPaIGI{^SA~{R42>DxzkU1Q;Gi=Prj2DM
zSA*MrQC|eSC~uEsR(O$2q>-nnGTJ&t?x^h)HJL@4lZCy%W0SXugW9E{ZnL+65Ed1w
zRIXGBDm~Voe{5qNOD_XRR4z&t5Jl>sK-}We!i8rtjHLRW&1}JTEeNtOl|fMWL6GLr
zIN^OG2!0xe$&~Y!)CE|z;NP_;_jg|$nqi*Zxx4#3-rLo&k+()+?^QUEdN&=T#$>je
zb20W&J)O~^@}Yf=opS*ij>X^cp6Tl>&;fYQ`)+C0fT(|T88=yld!bw52Ha^^i{|2P
z9IweA3v|{3=ktX$3Ve@@Q87wJ>ibi|P0<^MStb)LZi#H1?jiq+BA&CX>P?ZzY=D-!
zCAhff7+OQiUXB#5ijcdipX8yLY3MK=A+D%!3AI^!GL2ioQlb8AVx@Z=4E{W<3f2=W
z{WrjoL;H3HoH%ssl5yzBCG3&nS0&OvGP;k9TVeyaDbYT{Rbs|8Zoj&8<-|^|D^ZJH
zRY~>8Adnb94*lZHHGHc9`V!4Fb}MvTqT@0hSLlc+@GPv-5sc%A20gq7J~-ulOvNVM
zbEhKvIHDj^V?i7N0q_}C>C#m;RHv*?7;w`)YIdi+ymk7V?_37h-gqe{?t{%!MouxW
zN!eY4(H6kua+Vu(tvJg%r)A<j*nIZ*@#g6z78C%AV>!UGU)xp8QI`SQeo@A#jEWL6
zTlm_9%GgFMJj~E>&lKJ`;|jNh+pW#Zp;$VX*P>z*UU<{$XnRFj%cNga*_(WCzwm~(
z@w!cun=lEcs=9me9W>z4)1BKjhnSd))>C|4?$<brO%SXC(<=bvw4CeCS5CwI#<_rf
z9qR*kva$#WcSUEe*5gURAOA~Ow!AnNC?ie`>I~4KzDp~Z99xrX3qz~oMrW;mR_8Y&
z6fGk88MM`>0BuJJ-oNLwHyWupGv9$_lS=S$ayF5ye^ZgIwx^cZ0_RsX0g7r6M0qa=
z)E7+plEImIyW_Yho_p7^G`CHo=DCfQrS*U=KECfdmi`*x{G5?^e#<0L@%FiYLtLzQ
z!_hlC?Xu!n^;nrd{&NjJ!P0jCrcpZ~I>sI?Xgd=6#$^__qe~yn5Oe_$*xb@=2jy)e
z(#iX3zR{S<l_WpFr2iNqiZ8^2nhS{svih8<eAjb<u(eUqJv>j=FIFPA;a+hjtwsJi
zmcP}mz87N~sG<^ZB8mp;AFpyMG2<h}fjW20S-d$U>sZ$7hU2;Imbw9)^M&Ud`MNsA
z_~8wWy+u)-JrAnNyygt+gd_QSP1)wIgJEAc+iTV_8-3+6<9`xg)E-P<t%n~%LSF>%
U+!k)W>?}K<lvc_s*H--h0H%^zDF6Tf

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/trueaudio.cpython-35.pyc b/resources/lib/mutagen/__pycache__/trueaudio.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..629b654b9c5709ab5e164296d91a430a126fcd4a
GIT binary patch
literal 2924
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
F_AgA^o~Hl+

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/__pycache__/wavpack.cpython-35.pyc b/resources/lib/mutagen/__pycache__/wavpack.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2b9be21492c00f302d9b204dced56a8dc8852b9b
GIT binary patch
literal 3738
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
DmOdQc

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/_compat.py b/resources/lib/mutagen/_compat.py
new file mode 100644
index 00000000..77c465f1
--- /dev/null
+++ b/resources/lib/mutagen/_compat.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2013  Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+import sys
+
+
+PY2 = sys.version_info[0] == 2
+PY3 = not PY2
+
+if PY2:
+    from StringIO import StringIO
+    BytesIO = StringIO
+    from cStringIO import StringIO as cBytesIO
+    from itertools import izip
+
+    long_ = long
+    integer_types = (int, long)
+    string_types = (str, unicode)
+    text_type = unicode
+
+    xrange = xrange
+    cmp = cmp
+    chr_ = chr
+
+    def endswith(text, end):
+        return text.endswith(end)
+
+    iteritems = lambda d: d.iteritems()
+    itervalues = lambda d: d.itervalues()
+    iterkeys = lambda d: d.iterkeys()
+
+    iterbytes = lambda b: iter(b)
+
+    exec("def reraise(tp, value, tb):\n raise tp, value, tb")
+
+    def swap_to_string(cls):
+        if "__str__" in cls.__dict__:
+            cls.__unicode__ = cls.__str__
+
+        if "__bytes__" in cls.__dict__:
+            cls.__str__ = cls.__bytes__
+
+        return cls
+
+elif PY3:
+    from io import StringIO
+    StringIO = StringIO
+    from io import BytesIO
+    cBytesIO = BytesIO
+
+    long_ = int
+    integer_types = (int,)
+    string_types = (str,)
+    text_type = str
+
+    izip = zip
+    xrange = range
+    cmp = lambda a, b: (a > b) - (a < b)
+    chr_ = lambda x: bytes([x])
+
+    def endswith(text, end):
+        # usefull for paths which can be both, str and bytes
+        if isinstance(text, str):
+            if not isinstance(end, str):
+                end = end.decode("ascii")
+        else:
+            if not isinstance(end, bytes):
+                end = end.encode("ascii")
+        return text.endswith(end)
+
+    iteritems = lambda d: iter(d.items())
+    itervalues = lambda d: iter(d.values())
+    iterkeys = lambda d: iter(d.keys())
+
+    iterbytes = lambda b: (bytes([v]) for v in b)
+
+    def reraise(tp, value, tb):
+        raise tp(value).with_traceback(tb)
+
+    def swap_to_string(cls):
+        return cls
diff --git a/resources/lib/mutagen/_constants.py b/resources/lib/mutagen/_constants.py
new file mode 100644
index 00000000..62c1ce02
--- /dev/null
+++ b/resources/lib/mutagen/_constants.py
@@ -0,0 +1,199 @@
+# -*- coding: utf-8 -*-
+
+"""Constants used by Mutagen."""
+
+GENRES = [
+    u"Blues",
+    u"Classic Rock",
+    u"Country",
+    u"Dance",
+    u"Disco",
+    u"Funk",
+    u"Grunge",
+    u"Hip-Hop",
+    u"Jazz",
+    u"Metal",
+    u"New Age",
+    u"Oldies",
+    u"Other",
+    u"Pop",
+    u"R&B",
+    u"Rap",
+    u"Reggae",
+    u"Rock",
+    u"Techno",
+    u"Industrial",
+    u"Alternative",
+    u"Ska",
+    u"Death Metal",
+    u"Pranks",
+    u"Soundtrack",
+    u"Euro-Techno",
+    u"Ambient",
+    u"Trip-Hop",
+    u"Vocal",
+    u"Jazz+Funk",
+    u"Fusion",
+    u"Trance",
+    u"Classical",
+    u"Instrumental",
+    u"Acid",
+    u"House",
+    u"Game",
+    u"Sound Clip",
+    u"Gospel",
+    u"Noise",
+    u"Alt. Rock",
+    u"Bass",
+    u"Soul",
+    u"Punk",
+    u"Space",
+    u"Meditative",
+    u"Instrumental Pop",
+    u"Instrumental Rock",
+    u"Ethnic",
+    u"Gothic",
+    u"Darkwave",
+    u"Techno-Industrial",
+    u"Electronic",
+    u"Pop-Folk",
+    u"Eurodance",
+    u"Dream",
+    u"Southern Rock",
+    u"Comedy",
+    u"Cult",
+    u"Gangsta Rap",
+    u"Top 40",
+    u"Christian Rap",
+    u"Pop/Funk",
+    u"Jungle",
+    u"Native American",
+    u"Cabaret",
+    u"New Wave",
+    u"Psychedelic",
+    u"Rave",
+    u"Showtunes",
+    u"Trailer",
+    u"Lo-Fi",
+    u"Tribal",
+    u"Acid Punk",
+    u"Acid Jazz",
+    u"Polka",
+    u"Retro",
+    u"Musical",
+    u"Rock & Roll",
+    u"Hard Rock",
+    u"Folk",
+    u"Folk-Rock",
+    u"National Folk",
+    u"Swing",
+    u"Fast-Fusion",
+    u"Bebop",
+    u"Latin",
+    u"Revival",
+    u"Celtic",
+    u"Bluegrass",
+    u"Avantgarde",
+    u"Gothic Rock",
+    u"Progressive Rock",
+    u"Psychedelic Rock",
+    u"Symphonic Rock",
+    u"Slow Rock",
+    u"Big Band",
+    u"Chorus",
+    u"Easy Listening",
+    u"Acoustic",
+    u"Humour",
+    u"Speech",
+    u"Chanson",
+    u"Opera",
+    u"Chamber Music",
+    u"Sonata",
+    u"Symphony",
+    u"Booty Bass",
+    u"Primus",
+    u"Porn Groove",
+    u"Satire",
+    u"Slow Jam",
+    u"Club",
+    u"Tango",
+    u"Samba",
+    u"Folklore",
+    u"Ballad",
+    u"Power Ballad",
+    u"Rhythmic Soul",
+    u"Freestyle",
+    u"Duet",
+    u"Punk Rock",
+    u"Drum Solo",
+    u"A Cappella",
+    u"Euro-House",
+    u"Dance Hall",
+    u"Goa",
+    u"Drum & Bass",
+    u"Club-House",
+    u"Hardcore",
+    u"Terror",
+    u"Indie",
+    u"BritPop",
+    u"Afro-Punk",
+    u"Polsk Punk",
+    u"Beat",
+    u"Christian Gangsta Rap",
+    u"Heavy Metal",
+    u"Black Metal",
+    u"Crossover",
+    u"Contemporary Christian",
+    u"Christian Rock",
+    u"Merengue",
+    u"Salsa",
+    u"Thrash Metal",
+    u"Anime",
+    u"JPop",
+    u"Synthpop",
+    u"Abstract",
+    u"Art Rock",
+    u"Baroque",
+    u"Bhangra",
+    u"Big Beat",
+    u"Breakbeat",
+    u"Chillout",
+    u"Downtempo",
+    u"Dub",
+    u"EBM",
+    u"Eclectic",
+    u"Electro",
+    u"Electroclash",
+    u"Emo",
+    u"Experimental",
+    u"Garage",
+    u"Global",
+    u"IDM",
+    u"Illbient",
+    u"Industro-Goth",
+    u"Jam Band",
+    u"Krautrock",
+    u"Leftfield",
+    u"Lounge",
+    u"Math Rock",
+    u"New Romantic",
+    u"Nu-Breakz",
+    u"Post-Punk",
+    u"Post-Rock",
+    u"Psytrance",
+    u"Shoegaze",
+    u"Space Rock",
+    u"Trop Rock",
+    u"World Music",
+    u"Neoclassical",
+    u"Audiobook",
+    u"Audio Theatre",
+    u"Neue Deutsche Welle",
+    u"Podcast",
+    u"Indie Rock",
+    u"G-Funk",
+    u"Dubstep",
+    u"Garage Rock",
+    u"Psybient",
+]
+"""The ID3v1 genre list."""
diff --git a/resources/lib/mutagen/_file.py b/resources/lib/mutagen/_file.py
new file mode 100644
index 00000000..5daa2521
--- /dev/null
+++ b/resources/lib/mutagen/_file.py
@@ -0,0 +1,253 @@
+# Copyright (C) 2005  Michael Urman
+# -*- coding: utf-8 -*-
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+import warnings
+
+from mutagen._util import DictMixin
+from mutagen._compat import izip
+
+
+class FileType(DictMixin):
+    """An abstract object wrapping tags and audio stream information.
+
+    Attributes:
+
+    * info -- stream information (length, bitrate, sample rate)
+    * tags -- metadata tags, if any
+
+    Each file format has different potential tags and stream
+    information.
+
+    FileTypes implement an interface very similar to Metadata; the
+    dict interface, save, load, and delete calls on a FileType call
+    the appropriate methods on its tag data.
+    """
+
+    __module__ = "mutagen"
+
+    info = None
+    tags = None
+    filename = None
+    _mimes = ["application/octet-stream"]
+
+    def __init__(self, filename=None, *args, **kwargs):
+        if filename is None:
+            warnings.warn("FileType constructor requires a filename",
+                          DeprecationWarning)
+        else:
+            self.load(filename, *args, **kwargs)
+
+    def load(self, filename, *args, **kwargs):
+        raise NotImplementedError
+
+    def __getitem__(self, key):
+        """Look up a metadata tag key.
+
+        If the file has no tags at all, a KeyError is raised.
+        """
+
+        if self.tags is None:
+            raise KeyError(key)
+        else:
+            return self.tags[key]
+
+    def __setitem__(self, key, value):
+        """Set a metadata tag.
+
+        If the file has no tags, an appropriate format is added (but
+        not written until save is called).
+        """
+
+        if self.tags is None:
+            self.add_tags()
+        self.tags[key] = value
+
+    def __delitem__(self, key):
+        """Delete a metadata tag key.
+
+        If the file has no tags at all, a KeyError is raised.
+        """
+
+        if self.tags is None:
+            raise KeyError(key)
+        else:
+            del(self.tags[key])
+
+    def keys(self):
+        """Return a list of keys in the metadata tag.
+
+        If the file has no tags at all, an empty list is returned.
+        """
+
+        if self.tags is None:
+            return []
+        else:
+            return self.tags.keys()
+
+    def delete(self, filename=None):
+        """Remove tags from a file.
+
+        In cases where the tagging format is independent of the file type
+        (for example `mutagen.ID3`) all traces of the tagging format will
+        be removed.
+        In cases where the tag is part of the file type, all tags and
+        padding will be removed.
+
+        The tags attribute will be cleared as well if there is one.
+
+        Does nothing if the file has no tags.
+
+        :raises mutagen.MutagenError: if deleting wasn't possible
+        """
+
+        if self.tags is not None:
+            if filename is None:
+                filename = self.filename
+            else:
+                warnings.warn(
+                    "delete(filename=...) is deprecated, reload the file",
+                    DeprecationWarning)
+            return self.tags.delete(filename)
+
+    def save(self, filename=None, **kwargs):
+        """Save metadata tags.
+
+        :raises mutagen.MutagenError: if saving wasn't possible
+        """
+
+        if filename is None:
+            filename = self.filename
+        else:
+            warnings.warn(
+                "save(filename=...) is deprecated, reload the file",
+                DeprecationWarning)
+
+        if self.tags is not None:
+            return self.tags.save(filename, **kwargs)
+
+    def pprint(self):
+        """Print stream information and comment key=value pairs."""
+
+        stream = "%s (%s)" % (self.info.pprint(), self.mime[0])
+        try:
+            tags = self.tags.pprint()
+        except AttributeError:
+            return stream
+        else:
+            return stream + ((tags and "\n" + tags) or "")
+
+    def add_tags(self):
+        """Adds new tags to the file.
+
+        :raises mutagen.MutagenError: if tags already exist or adding is not
+            possible.
+        """
+
+        raise NotImplementedError
+
+    @property
+    def mime(self):
+        """A list of mime types"""
+
+        mimes = []
+        for Kind in type(self).__mro__:
+            for mime in getattr(Kind, '_mimes', []):
+                if mime not in mimes:
+                    mimes.append(mime)
+        return mimes
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        raise NotImplementedError
+
+
+class StreamInfo(object):
+    """Abstract stream information object.
+
+    Provides attributes for length, bitrate, sample rate etc.
+
+    See the implementations for details.
+    """
+
+    __module__ = "mutagen"
+
+    def pprint(self):
+        """Print stream information"""
+
+        raise NotImplementedError
+
+
+def File(filename, options=None, easy=False):
+    """Guess the type of the file and try to open it.
+
+    The file type is decided by several things, such as the first 128
+    bytes (which usually contains a file type identifier), the
+    filename extension, and the presence of existing tags.
+
+    If no appropriate type could be found, None is returned.
+
+    :param options: Sequence of :class:`FileType` implementations, defaults to
+                    all included ones.
+
+    :param easy: If the easy wrappers should be returnd if available.
+                 For example :class:`EasyMP3 <mp3.EasyMP3>` instead
+                 of :class:`MP3 <mp3.MP3>`.
+    """
+
+    if options is None:
+        from mutagen.asf import ASF
+        from mutagen.apev2 import APEv2File
+        from mutagen.flac import FLAC
+        if easy:
+            from mutagen.easyid3 import EasyID3FileType as ID3FileType
+        else:
+            from mutagen.id3 import ID3FileType
+        if easy:
+            from mutagen.mp3 import EasyMP3 as MP3
+        else:
+            from mutagen.mp3 import MP3
+        from mutagen.oggflac import OggFLAC
+        from mutagen.oggspeex import OggSpeex
+        from mutagen.oggtheora import OggTheora
+        from mutagen.oggvorbis import OggVorbis
+        from mutagen.oggopus import OggOpus
+        if easy:
+            from mutagen.trueaudio import EasyTrueAudio as TrueAudio
+        else:
+            from mutagen.trueaudio import TrueAudio
+        from mutagen.wavpack import WavPack
+        if easy:
+            from mutagen.easymp4 import EasyMP4 as MP4
+        else:
+            from mutagen.mp4 import MP4
+        from mutagen.musepack import Musepack
+        from mutagen.monkeysaudio import MonkeysAudio
+        from mutagen.optimfrog import OptimFROG
+        from mutagen.aiff import AIFF
+        from mutagen.aac import AAC
+        options = [MP3, TrueAudio, OggTheora, OggSpeex, OggVorbis, OggFLAC,
+                   FLAC, AIFF, APEv2File, MP4, ID3FileType, WavPack,
+                   Musepack, MonkeysAudio, OptimFROG, ASF, OggOpus, AAC]
+
+    if not options:
+        return None
+
+    with open(filename, "rb") as fileobj:
+        header = fileobj.read(128)
+        # Sort by name after score. Otherwise import order affects
+        # Kind sort order, which affects treatment of things with
+        # equals scores.
+        results = [(Kind.score(filename, fileobj, header), Kind.__name__)
+                   for Kind in options]
+
+    results = list(izip(results, options))
+    results.sort()
+    (score, name), Kind = results[-1]
+    if score > 0:
+        return Kind(filename)
+    else:
+        return None
diff --git a/resources/lib/mutagen/_mp3util.py b/resources/lib/mutagen/_mp3util.py
new file mode 100644
index 00000000..409cadcb
--- /dev/null
+++ b/resources/lib/mutagen/_mp3util.py
@@ -0,0 +1,420 @@
+# -*- coding: utf-8 -*-
+# Copyright 2015 Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""
+http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
+http://wiki.hydrogenaud.io/index.php?title=MP3
+"""
+
+from functools import partial
+
+from ._util import cdata, BitReader
+from ._compat import xrange, iterbytes, cBytesIO
+
+
+class LAMEError(Exception):
+    pass
+
+
+class LAMEHeader(object):
+    """http://gabriel.mp3-tech.org/mp3infotag.html"""
+
+    vbr_method = 0
+    """0: unknown, 1: CBR, 2: ABR, 3/4/5: VBR, others: see the docs"""
+
+    lowpass_filter = 0
+    """lowpass filter value in Hz. 0 means unknown"""
+
+    quality = -1
+    """Encoding quality: 0..9"""
+
+    vbr_quality = -1
+    """VBR quality: 0..9"""
+
+    track_peak = None
+    """Peak signal amplitude as float. None if unknown."""
+
+    track_gain_origin = 0
+    """see the docs"""
+
+    track_gain_adjustment = None
+    """Track gain adjustment as float (for 89db replay gain) or None"""
+
+    album_gain_origin = 0
+    """see the docs"""
+
+    album_gain_adjustment = None
+    """Album gain adjustment as float (for 89db replay gain) or None"""
+
+    encoding_flags = 0
+    """see docs"""
+
+    ath_type = -1
+    """see docs"""
+
+    bitrate = -1
+    """Bitrate in kbps. For VBR the minimum bitrate, for anything else
+    (CBR, ABR, ..) the target bitrate.
+    """
+
+    encoder_delay_start = 0
+    """Encoder delay in samples"""
+
+    encoder_padding_end = 0
+    """Padding in samples added at the end"""
+
+    source_sample_frequency_enum = -1
+    """see docs"""
+
+    unwise_setting_used = False
+    """see docs"""
+
+    stereo_mode = 0
+    """see docs"""
+
+    noise_shaping = 0
+    """see docs"""
+
+    mp3_gain = 0
+    """Applied MP3 gain -127..127. Factor is 2 ** (mp3_gain / 4)"""
+
+    surround_info = 0
+    """see docs"""
+
+    preset_used = 0
+    """lame preset"""
+
+    music_length = 0
+    """Length in bytes excluding any ID3 tags"""
+
+    music_crc = -1
+    """CRC16 of the data specified by music_length"""
+
+    header_crc = -1
+    """CRC16 of this header and everything before (not checked)"""
+
+    def __init__(self, xing, fileobj):
+        """Raises LAMEError if parsing fails"""
+
+        payload = fileobj.read(27)
+        if len(payload) != 27:
+            raise LAMEError("Not enough data")
+
+        # extended lame header
+        r = BitReader(cBytesIO(payload))
+        revision = r.bits(4)
+        if revision != 0:
+            raise LAMEError("unsupported header revision %d" % revision)
+
+        self.vbr_method = r.bits(4)
+        self.lowpass_filter = r.bits(8) * 100
+
+        # these have a different meaning for lame; expose them again here
+        self.quality = (100 - xing.vbr_scale) % 10
+        self.vbr_quality = (100 - xing.vbr_scale) // 10
+
+        track_peak_data = r.bytes(4)
+        if track_peak_data == b"\x00\x00\x00\x00":
+            self.track_peak = None
+        else:
+            # see PutLameVBR() in LAME's VbrTag.c
+            self.track_peak = (
+                cdata.uint32_be(track_peak_data) - 0.5) / 2 ** 23
+        track_gain_type = r.bits(3)
+        self.track_gain_origin = r.bits(3)
+        sign = r.bits(1)
+        gain_adj = r.bits(9) / 10.0
+        if sign:
+            gain_adj *= -1
+        if track_gain_type == 1:
+            self.track_gain_adjustment = gain_adj
+        else:
+            self.track_gain_adjustment = None
+        assert r.is_aligned()
+
+        album_gain_type = r.bits(3)
+        self.album_gain_origin = r.bits(3)
+        sign = r.bits(1)
+        album_gain_adj = r.bits(9) / 10.0
+        if album_gain_type == 2:
+            self.album_gain_adjustment = album_gain_adj
+        else:
+            self.album_gain_adjustment = None
+
+        self.encoding_flags = r.bits(4)
+        self.ath_type = r.bits(4)
+
+        self.bitrate = r.bits(8)
+
+        self.encoder_delay_start = r.bits(12)
+        self.encoder_padding_end = r.bits(12)
+
+        self.source_sample_frequency_enum = r.bits(2)
+        self.unwise_setting_used = r.bits(1)
+        self.stereo_mode = r.bits(3)
+        self.noise_shaping = r.bits(2)
+
+        sign = r.bits(1)
+        mp3_gain = r.bits(7)
+        if sign:
+            mp3_gain *= -1
+        self.mp3_gain = mp3_gain
+
+        r.skip(2)
+        self.surround_info = r.bits(3)
+        self.preset_used = r.bits(11)
+        self.music_length = r.bits(32)
+        self.music_crc = r.bits(16)
+
+        self.header_crc = r.bits(16)
+        assert r.is_aligned()
+
+    @classmethod
+    def parse_version(cls, fileobj):
+        """Returns a version string and True if a LAMEHeader follows.
+        The passed file object will be positioned right before the
+        lame header if True.
+
+        Raises LAMEError if there is no lame version info.
+        """
+
+        # http://wiki.hydrogenaud.io/index.php?title=LAME_version_string
+
+        data = fileobj.read(20)
+        if len(data) != 20:
+            raise LAMEError("Not a lame header")
+        if not data.startswith((b"LAME", b"L3.99")):
+            raise LAMEError("Not a lame header")
+
+        data = data.lstrip(b"EMAL")
+        major, data = data[0:1], data[1:].lstrip(b".")
+        minor = b""
+        for c in iterbytes(data):
+            if not c.isdigit():
+                break
+            minor += c
+        data = data[len(minor):]
+
+        try:
+            major = int(major.decode("ascii"))
+            minor = int(minor.decode("ascii"))
+        except ValueError:
+            raise LAMEError
+
+        # the extended header was added sometimes in the 3.90 cycle
+        # e.g. "LAME3.90 (alpha)" should still stop here.
+        # (I have seen such a file)
+        if (major, minor) < (3, 90) or (
+                (major, minor) == (3, 90) and data[-11:-10] == b"("):
+            flag = data.strip(b"\x00").rstrip().decode("ascii")
+            return u"%d.%d%s" % (major, minor, flag), False
+
+        if len(data) <= 11:
+            raise LAMEError("Invalid version: too long")
+
+        flag = data[:-11].rstrip(b"\x00")
+
+        flag_string = u""
+        patch = u""
+        if flag == b"a":
+            flag_string = u" (alpha)"
+        elif flag == b"b":
+            flag_string = u" (beta)"
+        elif flag == b"r":
+            patch = u".1+"
+        elif flag == b" ":
+            if (major, minor) > (3, 96):
+                patch = u".0"
+            else:
+                patch = u".0+"
+        elif flag == b"" or flag == b".":
+            patch = u".0+"
+        else:
+            flag_string = u" (?)"
+
+        # extended header, seek back to 9 bytes for the caller
+        fileobj.seek(-11, 1)
+
+        return u"%d.%d%s%s" % (major, minor, patch, flag_string), True
+
+
+class XingHeaderError(Exception):
+    pass
+
+
+class XingHeaderFlags(object):
+    FRAMES = 0x1
+    BYTES = 0x2
+    TOC = 0x4
+    VBR_SCALE = 0x8
+
+
+class XingHeader(object):
+
+    frames = -1
+    """Number of frames, -1 if unknown"""
+
+    bytes = -1
+    """Number of bytes, -1 if unknown"""
+
+    toc = []
+    """List of 100 file offsets in percent encoded as 0-255. E.g. entry
+    50 contains the file offset in percent at 50% play time.
+    Empty if unknown.
+    """
+
+    vbr_scale = -1
+    """VBR quality indicator 0-100. -1 if unknown"""
+
+    lame_header = None
+    """A LAMEHeader instance or None"""
+
+    lame_version = u""
+    """The version of the LAME encoder e.g. '3.99.0'. Empty if unknown"""
+
+    is_info = False
+    """If the header started with 'Info' and not 'Xing'"""
+
+    def __init__(self, fileobj):
+        """Parses the Xing header or raises XingHeaderError.
+
+        The file position after this returns is undefined.
+        """
+
+        data = fileobj.read(8)
+        if len(data) != 8 or data[:4] not in (b"Xing", b"Info"):
+            raise XingHeaderError("Not a Xing header")
+
+        self.is_info = (data[:4] == b"Info")
+
+        flags = cdata.uint32_be_from(data, 4)[0]
+
+        if flags & XingHeaderFlags.FRAMES:
+            data = fileobj.read(4)
+            if len(data) != 4:
+                raise XingHeaderError("Xing header truncated")
+            self.frames = cdata.uint32_be(data)
+
+        if flags & XingHeaderFlags.BYTES:
+            data = fileobj.read(4)
+            if len(data) != 4:
+                raise XingHeaderError("Xing header truncated")
+            self.bytes = cdata.uint32_be(data)
+
+        if flags & XingHeaderFlags.TOC:
+            data = fileobj.read(100)
+            if len(data) != 100:
+                raise XingHeaderError("Xing header truncated")
+            self.toc = list(bytearray(data))
+
+        if flags & XingHeaderFlags.VBR_SCALE:
+            data = fileobj.read(4)
+            if len(data) != 4:
+                raise XingHeaderError("Xing header truncated")
+            self.vbr_scale = cdata.uint32_be(data)
+
+        try:
+            self.lame_version, has_header = LAMEHeader.parse_version(fileobj)
+            if has_header:
+                self.lame_header = LAMEHeader(self, fileobj)
+        except LAMEError:
+            pass
+
+    @classmethod
+    def get_offset(cls, info):
+        """Calculate the offset to the Xing header from the start of the
+        MPEG header including sync based on the MPEG header's content.
+        """
+
+        assert info.layer == 3
+
+        if info.version == 1:
+            if info.mode != 3:
+                return 36
+            else:
+                return 21
+        else:
+            if info.mode != 3:
+                return 21
+            else:
+                return 13
+
+
+class VBRIHeaderError(Exception):
+    pass
+
+
+class VBRIHeader(object):
+
+    version = 0
+    """VBRI header version"""
+
+    quality = 0
+    """Quality indicator"""
+
+    bytes = 0
+    """Number of bytes"""
+
+    frames = 0
+    """Number of frames"""
+
+    toc_scale_factor = 0
+    """Scale factor of TOC entries"""
+
+    toc_frames = 0
+    """Number of frames per table entry"""
+
+    toc = []
+    """TOC"""
+
+    def __init__(self, fileobj):
+        """Reads the VBRI header or raises VBRIHeaderError.
+
+        The file position is undefined after this returns
+        """
+
+        data = fileobj.read(26)
+        if len(data) != 26 or not data.startswith(b"VBRI"):
+            raise VBRIHeaderError("Not a VBRI header")
+
+        offset = 4
+        self.version, offset = cdata.uint16_be_from(data, offset)
+        if self.version != 1:
+            raise VBRIHeaderError(
+                "Unsupported header version: %r" % self.version)
+
+        offset += 2  # float16.. can't do
+        self.quality, offset = cdata.uint16_be_from(data, offset)
+        self.bytes, offset = cdata.uint32_be_from(data, offset)
+        self.frames, offset = cdata.uint32_be_from(data, offset)
+
+        toc_num_entries, offset = cdata.uint16_be_from(data, offset)
+        self.toc_scale_factor, offset = cdata.uint16_be_from(data, offset)
+        toc_entry_size, offset = cdata.uint16_be_from(data, offset)
+        self.toc_frames, offset = cdata.uint16_be_from(data, offset)
+        toc_size = toc_entry_size * toc_num_entries
+        toc_data = fileobj.read(toc_size)
+        if len(toc_data) != toc_size:
+            raise VBRIHeaderError("VBRI header truncated")
+
+        self.toc = []
+        if toc_entry_size == 2:
+            unpack = partial(cdata.uint16_be_from, toc_data)
+        elif toc_entry_size == 4:
+            unpack = partial(cdata.uint32_be_from, toc_data)
+        else:
+            raise VBRIHeaderError("Invalid TOC entry size")
+
+        self.toc = [unpack(i)[0] for i in xrange(0, toc_size, toc_entry_size)]
+
+    @classmethod
+    def get_offset(cls, info):
+        """Offset in bytes from the start of the MPEG header including sync"""
+
+        assert info.layer == 3
+
+        return 36
diff --git a/resources/lib/mutagen/_tags.py b/resources/lib/mutagen/_tags.py
new file mode 100644
index 00000000..ce250adf
--- /dev/null
+++ b/resources/lib/mutagen/_tags.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2005  Michael Urman
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+
+class PaddingInfo(object):
+    """Abstract padding information object.
+
+    This will be passed to the callback function that can be used
+    for saving tags.
+
+    ::
+
+        def my_callback(info: PaddingInfo):
+            return info.get_default_padding()
+
+    The callback should return the amount of padding to use (>= 0) based on
+    the content size and the padding of the file after saving. The actual used
+    amount of padding might vary depending on the file format (due to
+    alignment etc.)
+
+    The default implementation can be accessed using the
+    :meth:`get_default_padding` method in the callback.
+    """
+
+    padding = 0
+    """The amount of padding left after saving in bytes (can be negative if
+    more data needs to be added as padding is available)
+    """
+
+    size = 0
+    """The amount of data following the padding"""
+
+    def __init__(self, padding, size):
+        self.padding = padding
+        self.size = size
+
+    def get_default_padding(self):
+        """The default implementation which tries to select a reasonable
+        amount of padding and which might change in future versions.
+
+        :return: Amount of padding after saving
+        :rtype: int
+        """
+
+        high = 1024 * 10 + self.size // 100  # 10 KiB + 1% of trailing data
+        low = 1024 + self.size // 1000  # 1 KiB + 0.1% of trailing data
+
+        if self.padding >= 0:
+            # enough padding left
+            if self.padding > high:
+                # padding too large, reduce
+                return low
+            # just use existing padding as is
+            return self.padding
+        else:
+            # not enough padding, add some
+            return low
+
+    def _get_padding(self, user_func):
+        if user_func is None:
+            return self.get_default_padding()
+        else:
+            return user_func(self)
+
+    def __repr__(self):
+        return "<%s size=%d padding=%d>" % (
+            type(self).__name__, self.size, self.padding)
+
+
+class Metadata(object):
+    """An abstract dict-like object.
+
+    Metadata is the base class for many of the tag objects in Mutagen.
+    """
+
+    __module__ = "mutagen"
+
+    def __init__(self, *args, **kwargs):
+        if args or kwargs:
+            self.load(*args, **kwargs)
+
+    def load(self, *args, **kwargs):
+        raise NotImplementedError
+
+    def save(self, filename=None):
+        """Save changes to a file."""
+
+        raise NotImplementedError
+
+    def delete(self, filename=None):
+        """Remove tags from a file.
+
+        In most cases this means any traces of the tag will be removed
+        from the file.
+        """
+
+        raise NotImplementedError
diff --git a/resources/lib/mutagen/_toolsutil.py b/resources/lib/mutagen/_toolsutil.py
new file mode 100644
index 00000000..e9074b71
--- /dev/null
+++ b/resources/lib/mutagen/_toolsutil.py
@@ -0,0 +1,231 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2015 Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+import os
+import sys
+import signal
+import locale
+import contextlib
+import optparse
+import ctypes
+
+from ._compat import text_type, PY2, PY3, iterbytes
+
+
+def split_escape(string, sep, maxsplit=None, escape_char="\\"):
+    """Like unicode/str/bytes.split but allows for the separator to be escaped
+
+    If passed unicode/str/bytes will only return list of unicode/str/bytes.
+    """
+
+    assert len(sep) == 1
+    assert len(escape_char) == 1
+
+    if isinstance(string, bytes):
+        if isinstance(escape_char, text_type):
+            escape_char = escape_char.encode("ascii")
+        iter_ = iterbytes
+    else:
+        iter_ = iter
+
+    if maxsplit is None:
+        maxsplit = len(string)
+
+    empty = string[:0]
+    result = []
+    current = empty
+    escaped = False
+    for char in iter_(string):
+        if escaped:
+            if char != escape_char and char != sep:
+                current += escape_char
+            current += char
+            escaped = False
+        else:
+            if char == escape_char:
+                escaped = True
+            elif char == sep and len(result) < maxsplit:
+                result.append(current)
+                current = empty
+            else:
+                current += char
+    result.append(current)
+    return result
+
+
+class SignalHandler(object):
+
+    def __init__(self):
+        self._interrupted = False
+        self._nosig = False
+        self._init = False
+
+    def init(self):
+        signal.signal(signal.SIGINT, self._handler)
+        signal.signal(signal.SIGTERM, self._handler)
+        if os.name != "nt":
+            signal.signal(signal.SIGHUP, self._handler)
+
+    def _handler(self, signum, frame):
+        self._interrupted = True
+        if not self._nosig:
+            raise SystemExit("Aborted...")
+
+    @contextlib.contextmanager
+    def block(self):
+        """While this context manager is active any signals for aborting
+        the process will be queued and exit the program once the context
+        is left.
+        """
+
+        self._nosig = True
+        yield
+        self._nosig = False
+        if self._interrupted:
+            raise SystemExit("Aborted...")
+
+
+def get_win32_unicode_argv():
+    """Returns a unicode argv under Windows and standard sys.argv otherwise"""
+
+    if os.name != "nt" or not PY2:
+        return sys.argv
+
+    import ctypes
+    from ctypes import cdll, windll, wintypes
+
+    GetCommandLineW = cdll.kernel32.GetCommandLineW
+    GetCommandLineW.argtypes = []
+    GetCommandLineW.restype = wintypes.LPCWSTR
+
+    CommandLineToArgvW = windll.shell32.CommandLineToArgvW
+    CommandLineToArgvW.argtypes = [
+        wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_int)]
+    CommandLineToArgvW.restype = ctypes.POINTER(wintypes.LPWSTR)
+
+    LocalFree = windll.kernel32.LocalFree
+    LocalFree.argtypes = [wintypes.HLOCAL]
+    LocalFree.restype = wintypes.HLOCAL
+
+    argc = ctypes.c_int()
+    argv = CommandLineToArgvW(GetCommandLineW(), ctypes.byref(argc))
+    if not argv:
+        return
+
+    res = argv[max(0, argc.value - len(sys.argv)):argc.value]
+
+    LocalFree(argv)
+
+    return res
+
+
+def fsencoding():
+    """The encoding used for paths, argv, environ, stdout and stdin"""
+
+    if os.name == "nt":
+        return ""
+
+    return locale.getpreferredencoding() or "utf-8"
+
+
+def fsnative(text=u""):
+    """Returns the passed text converted to the preferred path type
+    for each platform.
+    """
+
+    assert isinstance(text, text_type)
+
+    if os.name == "nt" or PY3:
+        return text
+    else:
+        return text.encode(fsencoding(), "replace")
+    return text
+
+
+def is_fsnative(arg):
+    """If the passed value is of the preferred path type for each platform.
+    Note that on Python3+linux, paths can be bytes or str but this returns
+    False for bytes there.
+    """
+
+    if PY3 or os.name == "nt":
+        return isinstance(arg, text_type)
+    else:
+        return isinstance(arg, bytes)
+
+
+def print_(*objects, **kwargs):
+    """A print which supports bytes and str+surrogates under python3.
+
+    Needed so we can print anything passed to us through argv and environ.
+    Under Windows only text_type is allowed.
+
+    Arguments:
+        objects: one or more bytes/text
+        linesep (bool): whether a line separator should be appended
+        sep (bool): whether objects should be printed separated by spaces
+    """
+
+    linesep = kwargs.pop("linesep", True)
+    sep = kwargs.pop("sep", True)
+    file_ = kwargs.pop("file", None)
+    if file_ is None:
+        file_ = sys.stdout
+
+    old_cp = None
+    if os.name == "nt":
+        # Try to force the output to cp65001 aka utf-8.
+        # If that fails use the current one (most likely cp850, so
+        # most of unicode will be replaced with '?')
+        encoding = "utf-8"
+        old_cp = ctypes.windll.kernel32.GetConsoleOutputCP()
+        if ctypes.windll.kernel32.SetConsoleOutputCP(65001) == 0:
+            encoding = getattr(sys.stdout, "encoding", None) or "utf-8"
+            old_cp = None
+    else:
+        encoding = fsencoding()
+
+    try:
+        if linesep:
+            objects = list(objects) + [os.linesep]
+
+        parts = []
+        for text in objects:
+            if isinstance(text, text_type):
+                if PY3:
+                    try:
+                        text = text.encode(encoding, 'surrogateescape')
+                    except UnicodeEncodeError:
+                        text = text.encode(encoding, 'replace')
+                else:
+                    text = text.encode(encoding, 'replace')
+            parts.append(text)
+
+        data = (b" " if sep else b"").join(parts)
+        try:
+            fileno = file_.fileno()
+        except (AttributeError, OSError, ValueError):
+            # for tests when stdout is replaced
+            try:
+                file_.write(data)
+            except TypeError:
+                file_.write(data.decode(encoding, "replace"))
+        else:
+            file_.flush()
+            os.write(fileno, data)
+    finally:
+        # reset the code page to what we had before
+        if old_cp is not None:
+            ctypes.windll.kernel32.SetConsoleOutputCP(old_cp)
+
+
+class OptionParser(optparse.OptionParser):
+    """OptionParser subclass which supports printing Unicode under Windows"""
+
+    def print_help(self, file=None):
+        print_(self.format_help(), file=file)
diff --git a/resources/lib/mutagen/_util.py b/resources/lib/mutagen/_util.py
new file mode 100644
index 00000000..f05ff454
--- /dev/null
+++ b/resources/lib/mutagen/_util.py
@@ -0,0 +1,550 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Utility classes for Mutagen.
+
+You should not rely on the interfaces here being stable. They are
+intended for internal use in Mutagen only.
+"""
+
+import struct
+import codecs
+
+from fnmatch import fnmatchcase
+
+from ._compat import chr_, PY2, iteritems, iterbytes, integer_types, xrange, \
+    izip
+
+
+class MutagenError(Exception):
+    """Base class for all custom exceptions in mutagen
+
+    .. versionadded:: 1.25
+    """
+
+    __module__ = "mutagen"
+
+
+def total_ordering(cls):
+    assert "__eq__" in cls.__dict__
+    assert "__lt__" in cls.__dict__
+
+    cls.__le__ = lambda self, other: self == other or self < other
+    cls.__gt__ = lambda self, other: not (self == other or self < other)
+    cls.__ge__ = lambda self, other: not self < other
+    cls.__ne__ = lambda self, other: not self.__eq__(other)
+
+    return cls
+
+
+def hashable(cls):
+    """Makes sure the class is hashable.
+
+    Needs a working __eq__ and __hash__ and will add a __ne__.
+    """
+
+    # py2
+    assert "__hash__" in cls.__dict__
+    # py3
+    assert cls.__dict__["__hash__"] is not None
+    assert "__eq__" in cls.__dict__
+
+    cls.__ne__ = lambda self, other: not self.__eq__(other)
+
+    return cls
+
+
+def enum(cls):
+    assert cls.__bases__ == (object,)
+
+    d = dict(cls.__dict__)
+    new_type = type(cls.__name__, (int,), d)
+    new_type.__module__ = cls.__module__
+
+    map_ = {}
+    for key, value in iteritems(d):
+        if key.upper() == key and isinstance(value, integer_types):
+            value_instance = new_type(value)
+            setattr(new_type, key, value_instance)
+            map_[value] = key
+
+    def str_(self):
+        if self in map_:
+            return "%s.%s" % (type(self).__name__, map_[self])
+        return "%d" % int(self)
+
+    def repr_(self):
+        if self in map_:
+            return "<%s.%s: %d>" % (type(self).__name__, map_[self], int(self))
+        return "%d" % int(self)
+
+    setattr(new_type, "__repr__", repr_)
+    setattr(new_type, "__str__", str_)
+
+    return new_type
+
+
+@total_ordering
+class DictMixin(object):
+    """Implement the dict API using keys() and __*item__ methods.
+
+    Similar to UserDict.DictMixin, this takes a class that defines
+    __getitem__, __setitem__, __delitem__, and keys(), and turns it
+    into a full dict-like object.
+
+    UserDict.DictMixin is not suitable for this purpose because it's
+    an old-style class.
+
+    This class is not optimized for very large dictionaries; many
+    functions have linear memory requirements. I recommend you
+    override some of these functions if speed is required.
+    """
+
+    def __iter__(self):
+        return iter(self.keys())
+
+    def __has_key(self, key):
+        try:
+            self[key]
+        except KeyError:
+            return False
+        else:
+            return True
+
+    if PY2:
+        has_key = __has_key
+
+    __contains__ = __has_key
+
+    if PY2:
+        iterkeys = lambda self: iter(self.keys())
+
+    def values(self):
+        return [self[k] for k in self.keys()]
+
+    if PY2:
+        itervalues = lambda self: iter(self.values())
+
+    def items(self):
+        return list(izip(self.keys(), self.values()))
+
+    if PY2:
+        iteritems = lambda s: iter(s.items())
+
+    def clear(self):
+        for key in list(self.keys()):
+            self.__delitem__(key)
+
+    def pop(self, key, *args):
+        if len(args) > 1:
+            raise TypeError("pop takes at most two arguments")
+        try:
+            value = self[key]
+        except KeyError:
+            if args:
+                return args[0]
+            else:
+                raise
+        del(self[key])
+        return value
+
+    def popitem(self):
+        for key in self.keys():
+            break
+        else:
+            raise KeyError("dictionary is empty")
+        return key, self.pop(key)
+
+    def update(self, other=None, **kwargs):
+        if other is None:
+            self.update(kwargs)
+            other = {}
+
+        try:
+            for key, value in other.items():
+                self.__setitem__(key, value)
+        except AttributeError:
+            for key, value in other:
+                self[key] = value
+
+    def setdefault(self, key, default=None):
+        try:
+            return self[key]
+        except KeyError:
+            self[key] = default
+            return default
+
+    def get(self, key, default=None):
+        try:
+            return self[key]
+        except KeyError:
+            return default
+
+    def __repr__(self):
+        return repr(dict(self.items()))
+
+    def __eq__(self, other):
+        return dict(self.items()) == other
+
+    def __lt__(self, other):
+        return dict(self.items()) < other
+
+    __hash__ = object.__hash__
+
+    def __len__(self):
+        return len(self.keys())
+
+
+class DictProxy(DictMixin):
+    def __init__(self, *args, **kwargs):
+        self.__dict = {}
+        super(DictProxy, self).__init__(*args, **kwargs)
+
+    def __getitem__(self, key):
+        return self.__dict[key]
+
+    def __setitem__(self, key, value):
+        self.__dict[key] = value
+
+    def __delitem__(self, key):
+        del(self.__dict[key])
+
+    def keys(self):
+        return self.__dict.keys()
+
+
+def _fill_cdata(cls):
+    """Add struct pack/unpack functions"""
+
+    funcs = {}
+    for key, name in [("b", "char"), ("h", "short"),
+                      ("i", "int"), ("q", "longlong")]:
+        for echar, esuffix in [("<", "le"), (">", "be")]:
+            esuffix = "_" + esuffix
+            for unsigned in [True, False]:
+                s = struct.Struct(echar + (key.upper() if unsigned else key))
+                get_wrapper = lambda f: lambda *a, **k: f(*a, **k)[0]
+                unpack = get_wrapper(s.unpack)
+                unpack_from = get_wrapper(s.unpack_from)
+
+                def get_unpack_from(s):
+                    def unpack_from(data, offset=0):
+                        return s.unpack_from(data, offset)[0], offset + s.size
+                    return unpack_from
+
+                unpack_from = get_unpack_from(s)
+                pack = s.pack
+
+                prefix = "u" if unsigned else ""
+                if s.size == 1:
+                    esuffix = ""
+                bits = str(s.size * 8)
+                funcs["%s%s%s" % (prefix, name, esuffix)] = unpack
+                funcs["%sint%s%s" % (prefix, bits, esuffix)] = unpack
+                funcs["%s%s%s_from" % (prefix, name, esuffix)] = unpack_from
+                funcs["%sint%s%s_from" % (prefix, bits, esuffix)] = unpack_from
+                funcs["to_%s%s%s" % (prefix, name, esuffix)] = pack
+                funcs["to_%sint%s%s" % (prefix, bits, esuffix)] = pack
+
+    for key, func in iteritems(funcs):
+        setattr(cls, key, staticmethod(func))
+
+
+class cdata(object):
+    """C character buffer to Python numeric type conversions.
+
+    For each size/sign/endianness:
+    uint32_le(data)/to_uint32_le(num)/uint32_le_from(data, offset=0)
+    """
+
+    from struct import error
+    error = error
+
+    bitswap = b''.join(
+        chr_(sum(((val >> i) & 1) << (7 - i) for i in xrange(8)))
+        for val in xrange(256))
+
+    test_bit = staticmethod(lambda value, n: bool((value >> n) & 1))
+
+
+_fill_cdata(cdata)
+
+
+def get_size(fileobj):
+    """Returns the size of the file object. The position when passed in will
+    be preserved if no error occurs.
+
+    In case of an error raises IOError.
+    """
+
+    old_pos = fileobj.tell()
+    try:
+        fileobj.seek(0, 2)
+        return fileobj.tell()
+    finally:
+        fileobj.seek(old_pos, 0)
+
+
+def insert_bytes(fobj, size, offset, BUFFER_SIZE=2 ** 16):
+    """Insert size bytes of empty space starting at offset.
+
+    fobj must be an open file object, open rb+ or
+    equivalent. Mutagen tries to use mmap to resize the file, but
+    falls back to a significantly slower method if mmap fails.
+    """
+
+    assert 0 < size
+    assert 0 <= offset
+
+    fobj.seek(0, 2)
+    filesize = fobj.tell()
+    movesize = filesize - offset
+    fobj.write(b'\x00' * size)
+    fobj.flush()
+
+    try:
+        import mmap
+        file_map = mmap.mmap(fobj.fileno(), filesize + size)
+        try:
+            file_map.move(offset + size, offset, movesize)
+        finally:
+            file_map.close()
+    except (ValueError, EnvironmentError, ImportError, AttributeError):
+        # handle broken mmap scenarios, BytesIO()
+        fobj.truncate(filesize)
+
+        fobj.seek(0, 2)
+        padsize = size
+        # Don't generate an enormous string if we need to pad
+        # the file out several megs.
+        while padsize:
+            addsize = min(BUFFER_SIZE, padsize)
+            fobj.write(b"\x00" * addsize)
+            padsize -= addsize
+
+        fobj.seek(filesize, 0)
+        while movesize:
+            # At the start of this loop, fobj is pointing at the end
+            # of the data we need to move, which is of movesize length.
+            thismove = min(BUFFER_SIZE, movesize)
+            # Seek back however much we're going to read this frame.
+            fobj.seek(-thismove, 1)
+            nextpos = fobj.tell()
+            # Read it, so we're back at the end.
+            data = fobj.read(thismove)
+            # Seek back to where we need to write it.
+            fobj.seek(-thismove + size, 1)
+            # Write it.
+            fobj.write(data)
+            # And seek back to the end of the unmoved data.
+            fobj.seek(nextpos)
+            movesize -= thismove
+
+        fobj.flush()
+
+
+def delete_bytes(fobj, size, offset, BUFFER_SIZE=2 ** 16):
+    """Delete size bytes of empty space starting at offset.
+
+    fobj must be an open file object, open rb+ or
+    equivalent. Mutagen tries to use mmap to resize the file, but
+    falls back to a significantly slower method if mmap fails.
+    """
+
+    assert 0 < size
+    assert 0 <= offset
+
+    fobj.seek(0, 2)
+    filesize = fobj.tell()
+    movesize = filesize - offset - size
+    assert 0 <= movesize
+
+    if movesize > 0:
+        fobj.flush()
+        try:
+            import mmap
+            file_map = mmap.mmap(fobj.fileno(), filesize)
+            try:
+                file_map.move(offset, offset + size, movesize)
+            finally:
+                file_map.close()
+        except (ValueError, EnvironmentError, ImportError, AttributeError):
+            # handle broken mmap scenarios, BytesIO()
+            fobj.seek(offset + size)
+            buf = fobj.read(BUFFER_SIZE)
+            while buf:
+                fobj.seek(offset)
+                fobj.write(buf)
+                offset += len(buf)
+                fobj.seek(offset + size)
+                buf = fobj.read(BUFFER_SIZE)
+    fobj.truncate(filesize - size)
+    fobj.flush()
+
+
+def resize_bytes(fobj, old_size, new_size, offset):
+    """Resize an area in a file adding and deleting at the end of it.
+    Does nothing if no resizing is needed.
+    """
+
+    if new_size < old_size:
+        delete_size = old_size - new_size
+        delete_at = offset + new_size
+        delete_bytes(fobj, delete_size, delete_at)
+    elif new_size > old_size:
+        insert_size = new_size - old_size
+        insert_at = offset + old_size
+        insert_bytes(fobj, insert_size, insert_at)
+
+
+def dict_match(d, key, default=None):
+    """Like __getitem__ but works as if the keys() are all filename patterns.
+    Returns the value of any dict key that matches the passed key.
+    """
+
+    if key in d and "[" not in key:
+        return d[key]
+    else:
+        for pattern, value in iteritems(d):
+            if fnmatchcase(key, pattern):
+                return value
+    return default
+
+
+def decode_terminated(data, encoding, strict=True):
+    """Returns the decoded data until the first NULL terminator
+    and all data after it.
+
+    In case the data can't be decoded raises UnicodeError.
+    In case the encoding is not found raises LookupError.
+    In case the data isn't null terminated (even if it is encoded correctly)
+    raises ValueError except if strict is False, then the decoded string
+    will be returned anyway.
+    """
+
+    codec_info = codecs.lookup(encoding)
+
+    # normalize encoding name so we can compare by name
+    encoding = codec_info.name
+
+    # fast path
+    if encoding in ("utf-8", "iso8859-1"):
+        index = data.find(b"\x00")
+        if index == -1:
+            # make sure we raise UnicodeError first, like in the slow path
+            res = data.decode(encoding), b""
+            if strict:
+                raise ValueError("not null terminated")
+            else:
+                return res
+        return data[:index].decode(encoding), data[index + 1:]
+
+    # slow path
+    decoder = codec_info.incrementaldecoder()
+    r = []
+    for i, b in enumerate(iterbytes(data)):
+        c = decoder.decode(b)
+        if c == u"\x00":
+            return u"".join(r), data[i + 1:]
+        r.append(c)
+    else:
+        # make sure the decoder is finished
+        r.append(decoder.decode(b"", True))
+        if strict:
+            raise ValueError("not null terminated")
+        return u"".join(r), b""
+
+
+class BitReaderError(Exception):
+    pass
+
+
+class BitReader(object):
+
+    def __init__(self, fileobj):
+        self._fileobj = fileobj
+        self._buffer = 0
+        self._bits = 0
+        self._pos = fileobj.tell()
+
+    def bits(self, count):
+        """Reads `count` bits and returns an uint, MSB read first.
+
+        May raise BitReaderError if not enough data could be read or
+        IOError by the underlying file object.
+        """
+
+        if count < 0:
+            raise ValueError
+
+        if count > self._bits:
+            n_bytes = (count - self._bits + 7) // 8
+            data = self._fileobj.read(n_bytes)
+            if len(data) != n_bytes:
+                raise BitReaderError("not enough data")
+            for b in bytearray(data):
+                self._buffer = (self._buffer << 8) | b
+            self._bits += n_bytes * 8
+
+        self._bits -= count
+        value = self._buffer >> self._bits
+        self._buffer &= (1 << self._bits) - 1
+        assert self._bits < 8
+        return value
+
+    def bytes(self, count):
+        """Returns a bytearray of length `count`. Works unaligned."""
+
+        if count < 0:
+            raise ValueError
+
+        # fast path
+        if self._bits == 0:
+            data = self._fileobj.read(count)
+            if len(data) != count:
+                raise BitReaderError("not enough data")
+            return data
+
+        return bytes(bytearray(self.bits(8) for _ in xrange(count)))
+
+    def skip(self, count):
+        """Skip `count` bits.
+
+        Might raise BitReaderError if there wasn't enough data to skip,
+        but might also fail on the next bits() instead.
+        """
+
+        if count < 0:
+            raise ValueError
+
+        if count <= self._bits:
+            self.bits(count)
+        else:
+            count -= self.align()
+            n_bytes = count // 8
+            self._fileobj.seek(n_bytes, 1)
+            count -= n_bytes * 8
+            self.bits(count)
+
+    def get_position(self):
+        """Returns the amount of bits read or skipped so far"""
+
+        return (self._fileobj.tell() - self._pos) * 8 - self._bits
+
+    def align(self):
+        """Align to the next byte, returns the amount of bits skipped"""
+
+        bits = self._bits
+        self._buffer = 0
+        self._bits = 0
+        return bits
+
+    def is_aligned(self):
+        """If we are currently aligned to bytes and nothing is buffered"""
+
+        return self._bits == 0
diff --git a/resources/lib/mutagen/_vorbis.py b/resources/lib/mutagen/_vorbis.py
new file mode 100644
index 00000000..da202400
--- /dev/null
+++ b/resources/lib/mutagen/_vorbis.py
@@ -0,0 +1,330 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2005-2006  Joe Wreschnig
+#                    2013  Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""Read and write Vorbis comment data.
+
+Vorbis comments are freeform key/value pairs; keys are
+case-insensitive ASCII and values are Unicode strings. A key may have
+multiple values.
+
+The specification is at http://www.xiph.org/vorbis/doc/v-comment.html.
+"""
+
+import sys
+
+import mutagen
+from ._compat import reraise, BytesIO, text_type, xrange, PY3, PY2
+from mutagen._util import DictMixin, cdata
+
+
+def is_valid_key(key):
+    """Return true if a string is a valid Vorbis comment key.
+
+    Valid Vorbis comment keys are printable ASCII between 0x20 (space)
+    and 0x7D ('}'), excluding '='.
+
+    Takes str/unicode in Python 2, unicode in Python 3
+    """
+
+    if PY3 and isinstance(key, bytes):
+        raise TypeError("needs to be str not bytes")
+
+    for c in key:
+        if c < " " or c > "}" or c == "=":
+            return False
+    else:
+        return bool(key)
+
+
+istag = is_valid_key
+
+
+class error(IOError):
+    pass
+
+
+class VorbisUnsetFrameError(error):
+    pass
+
+
+class VorbisEncodingError(error):
+    pass
+
+
+class VComment(mutagen.Metadata, list):
+    """A Vorbis comment parser, accessor, and renderer.
+
+    All comment ordering is preserved. A VComment is a list of
+    key/value pairs, and so any Python list method can be used on it.
+
+    Vorbis comments are always wrapped in something like an Ogg Vorbis
+    bitstream or a FLAC metadata block, so this loads string data or a
+    file-like object, not a filename.
+
+    Attributes:
+
+    * vendor -- the stream 'vendor' (i.e. writer); default 'Mutagen'
+    """
+
+    vendor = u"Mutagen " + mutagen.version_string
+
+    def __init__(self, data=None, *args, **kwargs):
+        self._size = 0
+        # Collect the args to pass to load, this lets child classes
+        # override just load and get equivalent magic for the
+        # constructor.
+        if data is not None:
+            if isinstance(data, bytes):
+                data = BytesIO(data)
+            elif not hasattr(data, 'read'):
+                raise TypeError("VComment requires bytes or a file-like")
+            start = data.tell()
+            self.load(data, *args, **kwargs)
+            self._size = data.tell() - start
+
+    def load(self, fileobj, errors='replace', framing=True):
+        """Parse a Vorbis comment from a file-like object.
+
+        Keyword arguments:
+
+        * errors:
+            'strict', 'replace', or 'ignore'. This affects Unicode decoding
+            and how other malformed content is interpreted.
+        * framing -- if true, fail if a framing bit is not present
+
+        Framing bits are required by the Vorbis comment specification,
+        but are not used in FLAC Vorbis comment blocks.
+        """
+
+        try:
+            vendor_length = cdata.uint_le(fileobj.read(4))
+            self.vendor = fileobj.read(vendor_length).decode('utf-8', errors)
+            count = cdata.uint_le(fileobj.read(4))
+            for i in xrange(count):
+                length = cdata.uint_le(fileobj.read(4))
+                try:
+                    string = fileobj.read(length).decode('utf-8', errors)
+                except (OverflowError, MemoryError):
+                    raise error("cannot read %d bytes, too large" % length)
+                try:
+                    tag, value = string.split('=', 1)
+                except ValueError as err:
+                    if errors == "ignore":
+                        continue
+                    elif errors == "replace":
+                        tag, value = u"unknown%d" % i, string
+                    else:
+                        reraise(VorbisEncodingError, err, sys.exc_info()[2])
+                try:
+                    tag = tag.encode('ascii', errors)
+                except UnicodeEncodeError:
+                    raise VorbisEncodingError("invalid tag name %r" % tag)
+                else:
+                    # string keys in py3k
+                    if PY3:
+                        tag = tag.decode("ascii")
+                    if is_valid_key(tag):
+                        self.append((tag, value))
+
+            if framing and not bytearray(fileobj.read(1))[0] & 0x01:
+                raise VorbisUnsetFrameError("framing bit was unset")
+        except (cdata.error, TypeError):
+            raise error("file is not a valid Vorbis comment")
+
+    def validate(self):
+        """Validate keys and values.
+
+        Check to make sure every key used is a valid Vorbis key, and
+        that every value used is a valid Unicode or UTF-8 string. If
+        any invalid keys or values are found, a ValueError is raised.
+
+        In Python 3 all keys and values have to be a string.
+        """
+
+        if not isinstance(self.vendor, text_type):
+            if PY3:
+                raise ValueError("vendor needs to be str")
+
+            try:
+                self.vendor.decode('utf-8')
+            except UnicodeDecodeError:
+                raise ValueError
+
+        for key, value in self:
+            try:
+                if not is_valid_key(key):
+                    raise ValueError
+            except TypeError:
+                raise ValueError("%r is not a valid key" % key)
+
+            if not isinstance(value, text_type):
+                if PY3:
+                    raise ValueError("%r needs to be str" % key)
+
+                try:
+                    value.decode("utf-8")
+                except:
+                    raise ValueError("%r is not a valid value" % value)
+
+        return True
+
+    def clear(self):
+        """Clear all keys from the comment."""
+
+        for i in list(self):
+            self.remove(i)
+
+    def write(self, framing=True):
+        """Return a string representation of the data.
+
+        Validation is always performed, so calling this function on
+        invalid data may raise a ValueError.
+
+        Keyword arguments:
+
+        * framing -- if true, append a framing bit (see load)
+        """
+
+        self.validate()
+
+        def _encode(value):
+            if not isinstance(value, bytes):
+                return value.encode('utf-8')
+            return value
+
+        f = BytesIO()
+        vendor = _encode(self.vendor)
+        f.write(cdata.to_uint_le(len(vendor)))
+        f.write(vendor)
+        f.write(cdata.to_uint_le(len(self)))
+        for tag, value in self:
+            tag = _encode(tag)
+            value = _encode(value)
+            comment = tag + b"=" + value
+            f.write(cdata.to_uint_le(len(comment)))
+            f.write(comment)
+        if framing:
+            f.write(b"\x01")
+        return f.getvalue()
+
+    def pprint(self):
+
+        def _decode(value):
+            if not isinstance(value, text_type):
+                return value.decode('utf-8', 'replace')
+            return value
+
+        tags = [u"%s=%s" % (_decode(k), _decode(v)) for k, v in self]
+        return u"\n".join(tags)
+
+
+class VCommentDict(VComment, DictMixin):
+    """A VComment that looks like a dictionary.
+
+    This object differs from a dictionary in two ways. First,
+    len(comment) will still return the number of values, not the
+    number of keys. Secondly, iterating through the object will
+    iterate over (key, value) pairs, not keys. Since a key may have
+    multiple values, the same value may appear multiple times while
+    iterating.
+
+    Since Vorbis comment keys are case-insensitive, all keys are
+    normalized to lowercase ASCII.
+    """
+
+    def __getitem__(self, key):
+        """A list of values for the key.
+
+        This is a copy, so comment['title'].append('a title') will not
+        work.
+        """
+
+        # PY3 only
+        if isinstance(key, slice):
+            return VComment.__getitem__(self, key)
+
+        if not is_valid_key(key):
+            raise ValueError
+
+        key = key.lower()
+
+        values = [value for (k, value) in self if k.lower() == key]
+        if not values:
+            raise KeyError(key)
+        else:
+            return values
+
+    def __delitem__(self, key):
+        """Delete all values associated with the key."""
+
+        # PY3 only
+        if isinstance(key, slice):
+            return VComment.__delitem__(self, key)
+
+        if not is_valid_key(key):
+            raise ValueError
+
+        key = key.lower()
+        to_delete = [x for x in self if x[0].lower() == key]
+        if not to_delete:
+            raise KeyError(key)
+        else:
+            for item in to_delete:
+                self.remove(item)
+
+    def __contains__(self, key):
+        """Return true if the key has any values."""
+
+        if not is_valid_key(key):
+            raise ValueError
+
+        key = key.lower()
+        for k, value in self:
+            if k.lower() == key:
+                return True
+        else:
+            return False
+
+    def __setitem__(self, key, values):
+        """Set a key's value or values.
+
+        Setting a value overwrites all old ones. The value may be a
+        list of Unicode or UTF-8 strings, or a single Unicode or UTF-8
+        string.
+        """
+
+        # PY3 only
+        if isinstance(key, slice):
+            return VComment.__setitem__(self, key, values)
+
+        if not is_valid_key(key):
+            raise ValueError
+
+        if not isinstance(values, list):
+            values = [values]
+        try:
+            del(self[key])
+        except KeyError:
+            pass
+
+        if PY2:
+            key = key.encode('ascii')
+
+        for value in values:
+            self.append((key, value))
+
+    def keys(self):
+        """Return all keys in the comment."""
+
+        return list(set([k.lower() for k, v in self]))
+
+    def as_dict(self):
+        """Return a copy of the comment data in a real dict."""
+
+        return dict([(key, self[key]) for key in self.keys()])
diff --git a/resources/lib/mutagen/aac.py b/resources/lib/mutagen/aac.py
new file mode 100644
index 00000000..83968a05
--- /dev/null
+++ b/resources/lib/mutagen/aac.py
@@ -0,0 +1,410 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""
+* ADTS - Audio Data Transport Stream
+* ADIF - Audio Data Interchange Format
+* See ISO/IEC 13818-7 / 14496-03
+"""
+
+from mutagen import StreamInfo
+from mutagen._file import FileType
+from mutagen._util import BitReader, BitReaderError, MutagenError
+from mutagen._compat import endswith, xrange
+
+
+_FREQS = [
+    96000, 88200, 64000, 48000,
+    44100, 32000, 24000, 22050,
+    16000, 12000, 11025, 8000,
+    7350,
+]
+
+
+class _ADTSStream(object):
+    """Represents a series of frames belonging to the same stream"""
+
+    parsed_frames = 0
+    """Number of successfully parsed frames"""
+
+    offset = 0
+    """offset in bytes at which the stream starts (the first sync word)"""
+
+    @classmethod
+    def find_stream(cls, fileobj, max_bytes):
+        """Returns a possibly valid _ADTSStream or None.
+
+        Args:
+            max_bytes (int): maximum bytes to read
+        """
+
+        r = BitReader(fileobj)
+        stream = cls(r)
+        if stream.sync(max_bytes):
+            stream.offset = (r.get_position() - 12) // 8
+            return stream
+
+    def sync(self, max_bytes):
+        """Find the next sync.
+        Returns True if found."""
+
+        # at least 2 bytes for the sync
+        max_bytes = max(max_bytes, 2)
+
+        r = self._r
+        r.align()
+        while max_bytes > 0:
+            try:
+                b = r.bytes(1)
+                if b == b"\xff":
+                    if r.bits(4) == 0xf:
+                        return True
+                    r.align()
+                    max_bytes -= 2
+                else:
+                    max_bytes -= 1
+            except BitReaderError:
+                return False
+        return False
+
+    def __init__(self, r):
+        """Use _ADTSStream.find_stream to create a stream"""
+
+        self._fixed_header_key = None
+        self._r = r
+        self.offset = -1
+        self.parsed_frames = 0
+
+        self._samples = 0
+        self._payload = 0
+        self._start = r.get_position() / 8
+        self._last = self._start
+
+    @property
+    def bitrate(self):
+        """Bitrate of the raw aac blocks, excluding framing/crc"""
+
+        assert self.parsed_frames, "no frame parsed yet"
+
+        if self._samples == 0:
+            return 0
+
+        return (8 * self._payload * self.frequency) // self._samples
+
+    @property
+    def samples(self):
+        """samples so far"""
+
+        assert self.parsed_frames, "no frame parsed yet"
+
+        return self._samples
+
+    @property
+    def size(self):
+        """bytes read in the stream so far (including framing)"""
+
+        assert self.parsed_frames, "no frame parsed yet"
+
+        return self._last - self._start
+
+    @property
+    def channels(self):
+        """0 means unknown"""
+
+        assert self.parsed_frames, "no frame parsed yet"
+
+        b_index = self._fixed_header_key[6]
+        if b_index == 7:
+            return 8
+        elif b_index > 7:
+            return 0
+        else:
+            return b_index
+
+    @property
+    def frequency(self):
+        """0 means unknown"""
+
+        assert self.parsed_frames, "no frame parsed yet"
+
+        f_index = self._fixed_header_key[4]
+        try:
+            return _FREQS[f_index]
+        except IndexError:
+            return 0
+
+    def parse_frame(self):
+        """True if parsing was successful.
+        Fails either because the frame wasn't valid or the stream ended.
+        """
+
+        try:
+            return self._parse_frame()
+        except BitReaderError:
+            return False
+
+    def _parse_frame(self):
+        r = self._r
+        # start == position of sync word
+        start = r.get_position() - 12
+
+        # adts_fixed_header
+        id_ = r.bits(1)
+        layer = r.bits(2)
+        protection_absent = r.bits(1)
+
+        profile = r.bits(2)
+        sampling_frequency_index = r.bits(4)
+        private_bit = r.bits(1)
+        # TODO: if 0 we could parse program_config_element()
+        channel_configuration = r.bits(3)
+        original_copy = r.bits(1)
+        home = r.bits(1)
+
+        # the fixed header has to be the same for every frame in the stream
+        fixed_header_key = (
+            id_, layer, protection_absent, profile, sampling_frequency_index,
+            private_bit, channel_configuration, original_copy, home,
+        )
+
+        if self._fixed_header_key is None:
+            self._fixed_header_key = fixed_header_key
+        else:
+            if self._fixed_header_key != fixed_header_key:
+                return False
+
+        # adts_variable_header
+        r.skip(2)  # copyright_identification_bit/start
+        frame_length = r.bits(13)
+        r.skip(11)  # adts_buffer_fullness
+        nordbif = r.bits(2)
+        # adts_variable_header end
+
+        crc_overhead = 0
+        if not protection_absent:
+            crc_overhead += (nordbif + 1) * 16
+            if nordbif != 0:
+                crc_overhead *= 2
+
+        left = (frame_length * 8) - (r.get_position() - start)
+        if left < 0:
+            return False
+        r.skip(left)
+        assert r.is_aligned()
+
+        self._payload += (left - crc_overhead) / 8
+        self._samples += (nordbif + 1) * 1024
+        self._last = r.get_position() / 8
+
+        self.parsed_frames += 1
+        return True
+
+
+class ProgramConfigElement(object):
+
+    element_instance_tag = None
+    object_type = None
+    sampling_frequency_index = None
+    channels = None
+
+    def __init__(self, r):
+        """Reads the program_config_element()
+
+        Raises BitReaderError
+        """
+
+        self.element_instance_tag = r.bits(4)
+        self.object_type = r.bits(2)
+        self.sampling_frequency_index = r.bits(4)
+        num_front_channel_elements = r.bits(4)
+        num_side_channel_elements = r.bits(4)
+        num_back_channel_elements = r.bits(4)
+        num_lfe_channel_elements = r.bits(2)
+        num_assoc_data_elements = r.bits(3)
+        num_valid_cc_elements = r.bits(4)
+
+        mono_mixdown_present = r.bits(1)
+        if mono_mixdown_present == 1:
+            r.skip(4)
+        stereo_mixdown_present = r.bits(1)
+        if stereo_mixdown_present == 1:
+            r.skip(4)
+        matrix_mixdown_idx_present = r.bits(1)
+        if matrix_mixdown_idx_present == 1:
+            r.skip(3)
+
+        elms = num_front_channel_elements + num_side_channel_elements + \
+            num_back_channel_elements
+        channels = 0
+        for i in xrange(elms):
+            channels += 1
+            element_is_cpe = r.bits(1)
+            if element_is_cpe:
+                channels += 1
+            r.skip(4)
+        channels += num_lfe_channel_elements
+        self.channels = channels
+
+        r.skip(4 * num_lfe_channel_elements)
+        r.skip(4 * num_assoc_data_elements)
+        r.skip(5 * num_valid_cc_elements)
+        r.align()
+        comment_field_bytes = r.bits(8)
+        r.skip(8 * comment_field_bytes)
+
+
+class AACError(MutagenError):
+    pass
+
+
+class AACInfo(StreamInfo):
+    """AAC stream information.
+
+    Attributes:
+
+    * channels -- number of audio channels
+    * length -- file length in seconds, as a float
+    * sample_rate -- audio sampling rate in Hz
+    * bitrate -- audio bitrate, in bits per second
+
+    The length of the stream is just a guess and might not be correct.
+    """
+
+    channels = 0
+    length = 0
+    sample_rate = 0
+    bitrate = 0
+
+    def __init__(self, fileobj):
+        # skip id3v2 header
+        start_offset = 0
+        header = fileobj.read(10)
+        from mutagen.id3 import BitPaddedInt
+        if header.startswith(b"ID3"):
+            size = BitPaddedInt(header[6:])
+            start_offset = size + 10
+
+        fileobj.seek(start_offset)
+        adif = fileobj.read(4)
+        if adif == b"ADIF":
+            self._parse_adif(fileobj)
+            self._type = "ADIF"
+        else:
+            self._parse_adts(fileobj, start_offset)
+            self._type = "ADTS"
+
+    def _parse_adif(self, fileobj):
+        r = BitReader(fileobj)
+        try:
+            copyright_id_present = r.bits(1)
+            if copyright_id_present:
+                r.skip(72)  # copyright_id
+            r.skip(1 + 1)  # original_copy, home
+            bitstream_type = r.bits(1)
+            self.bitrate = r.bits(23)
+            npce = r.bits(4)
+            if bitstream_type == 0:
+                r.skip(20)  # adif_buffer_fullness
+
+            pce = ProgramConfigElement(r)
+            try:
+                self.sample_rate = _FREQS[pce.sampling_frequency_index]
+            except IndexError:
+                pass
+            self.channels = pce.channels
+
+            # other pces..
+            for i in xrange(npce):
+                ProgramConfigElement(r)
+            r.align()
+        except BitReaderError as e:
+            raise AACError(e)
+
+        # use bitrate + data size to guess length
+        start = fileobj.tell()
+        fileobj.seek(0, 2)
+        length = fileobj.tell() - start
+        if self.bitrate != 0:
+            self.length = (8.0 * length) / self.bitrate
+
+    def _parse_adts(self, fileobj, start_offset):
+        max_initial_read = 512
+        max_resync_read = 10
+        max_sync_tries = 10
+
+        frames_max = 100
+        frames_needed = 3
+
+        # Try up to X times to find a sync word and read up to Y frames.
+        # If more than Z frames are valid we assume a valid stream
+        offset = start_offset
+        for i in xrange(max_sync_tries):
+            fileobj.seek(offset)
+            s = _ADTSStream.find_stream(fileobj, max_initial_read)
+            if s is None:
+                raise AACError("sync not found")
+            # start right after the last found offset
+            offset += s.offset + 1
+
+            for i in xrange(frames_max):
+                if not s.parse_frame():
+                    break
+                if not s.sync(max_resync_read):
+                    break
+
+            if s.parsed_frames >= frames_needed:
+                break
+        else:
+            raise AACError(
+                "no valid stream found (only %d frames)" % s.parsed_frames)
+
+        self.sample_rate = s.frequency
+        self.channels = s.channels
+        self.bitrate = s.bitrate
+
+        # size from stream start to end of file
+        fileobj.seek(0, 2)
+        stream_size = fileobj.tell() - (offset + s.offset)
+        # approx
+        self.length = float(s.samples * stream_size) / (s.size * s.frequency)
+
+    def pprint(self):
+        return u"AAC (%s), %d Hz, %.2f seconds, %d channel(s), %d bps" % (
+            self._type, self.sample_rate, self.length, self.channels,
+            self.bitrate)
+
+
+class AAC(FileType):
+    """Load ADTS or ADIF streams containing AAC.
+
+    Tagging is not supported.
+    Use the ID3/APEv2 classes directly instead.
+    """
+
+    _mimes = ["audio/x-aac"]
+
+    def load(self, filename):
+        self.filename = filename
+        with open(filename, "rb") as h:
+            self.info = AACInfo(h)
+
+    def add_tags(self):
+        raise AACError("doesn't support tags")
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        filename = filename.lower()
+        s = endswith(filename, ".aac") or endswith(filename, ".adts") or \
+            endswith(filename, ".adif")
+        s += b"ADIF" in header
+        return s
+
+
+Open = AAC
+error = AACError
+
+__all__ = ["AAC", "Open"]
diff --git a/resources/lib/mutagen/aiff.py b/resources/lib/mutagen/aiff.py
new file mode 100644
index 00000000..dc580063
--- /dev/null
+++ b/resources/lib/mutagen/aiff.py
@@ -0,0 +1,357 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2014  Evan Purkhiser
+#               2014  Ben Ockmore
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""AIFF audio stream information and tags."""
+
+import sys
+import struct
+from struct import pack
+
+from ._compat import endswith, text_type, reraise
+from mutagen import StreamInfo, FileType
+
+from mutagen.id3 import ID3
+from mutagen.id3._util import ID3NoHeaderError, error as ID3Error
+from mutagen._util import resize_bytes, delete_bytes, MutagenError
+
+__all__ = ["AIFF", "Open", "delete"]
+
+
+class error(MutagenError, RuntimeError):
+    pass
+
+
+class InvalidChunk(error, IOError):
+    pass
+
+
+# based on stdlib's aifc
+_HUGE_VAL = 1.79769313486231e+308
+
+
+def is_valid_chunk_id(id):
+    assert isinstance(id, text_type)
+
+    return ((len(id) <= 4) and (min(id) >= u' ') and
+            (max(id) <= u'~'))
+
+
+def read_float(data):  # 10 bytes
+    expon, himant, lomant = struct.unpack('>hLL', data)
+    sign = 1
+    if expon < 0:
+        sign = -1
+        expon = expon + 0x8000
+    if expon == himant == lomant == 0:
+        f = 0.0
+    elif expon == 0x7FFF:
+        f = _HUGE_VAL
+    else:
+        expon = expon - 16383
+        f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63)
+    return sign * f
+
+
+class IFFChunk(object):
+    """Representation of a single IFF chunk"""
+
+    # Chunk headers are 8 bytes long (4 for ID and 4 for the size)
+    HEADER_SIZE = 8
+
+    def __init__(self, fileobj, parent_chunk=None):
+        self.__fileobj = fileobj
+        self.parent_chunk = parent_chunk
+        self.offset = fileobj.tell()
+
+        header = fileobj.read(self.HEADER_SIZE)
+        if len(header) < self.HEADER_SIZE:
+            raise InvalidChunk()
+
+        self.id, self.data_size = struct.unpack('>4si', header)
+
+        try:
+            self.id = self.id.decode('ascii')
+        except UnicodeDecodeError:
+            raise InvalidChunk()
+
+        if not is_valid_chunk_id(self.id):
+            raise InvalidChunk()
+
+        self.size = self.HEADER_SIZE + self.data_size
+        self.data_offset = fileobj.tell()
+
+    def read(self):
+        """Read the chunks data"""
+
+        self.__fileobj.seek(self.data_offset)
+        return self.__fileobj.read(self.data_size)
+
+    def write(self, data):
+        """Write the chunk data"""
+
+        if len(data) > self.data_size:
+            raise ValueError
+
+        self.__fileobj.seek(self.data_offset)
+        self.__fileobj.write(data)
+
+    def delete(self):
+        """Removes the chunk from the file"""
+
+        delete_bytes(self.__fileobj, self.size, self.offset)
+        if self.parent_chunk is not None:
+            self.parent_chunk._update_size(
+                self.parent_chunk.data_size - self.size)
+
+    def _update_size(self, data_size):
+        """Update the size of the chunk"""
+
+        self.__fileobj.seek(self.offset + 4)
+        self.__fileobj.write(pack('>I', data_size))
+        if self.parent_chunk is not None:
+            size_diff = self.data_size - data_size
+            self.parent_chunk._update_size(
+                self.parent_chunk.data_size - size_diff)
+        self.data_size = data_size
+        self.size = data_size + self.HEADER_SIZE
+
+    def resize(self, new_data_size):
+        """Resize the file and update the chunk sizes"""
+
+        resize_bytes(
+            self.__fileobj, self.data_size, new_data_size, self.data_offset)
+        self._update_size(new_data_size)
+
+
+class IFFFile(object):
+    """Representation of a IFF file"""
+
+    def __init__(self, fileobj):
+        self.__fileobj = fileobj
+        self.__chunks = {}
+
+        # AIFF Files always start with the FORM chunk which contains a 4 byte
+        # ID before the start of other chunks
+        fileobj.seek(0)
+        self.__chunks[u'FORM'] = IFFChunk(fileobj)
+
+        # Skip past the 4 byte FORM id
+        fileobj.seek(IFFChunk.HEADER_SIZE + 4)
+
+        # Where the next chunk can be located. We need to keep track of this
+        # since the size indicated in the FORM header may not match up with the
+        # offset determined from the size of the last chunk in the file
+        self.__next_offset = fileobj.tell()
+
+        # Load all of the chunks
+        while True:
+            try:
+                chunk = IFFChunk(fileobj, self[u'FORM'])
+            except InvalidChunk:
+                break
+            self.__chunks[chunk.id.strip()] = chunk
+
+            # Calculate the location of the next chunk,
+            # considering the pad byte
+            self.__next_offset = chunk.offset + chunk.size
+            self.__next_offset += self.__next_offset % 2
+            fileobj.seek(self.__next_offset)
+
+    def __contains__(self, id_):
+        """Check if the IFF file contains a specific chunk"""
+
+        assert isinstance(id_, text_type)
+
+        if not is_valid_chunk_id(id_):
+            raise KeyError("AIFF key must be four ASCII characters.")
+
+        return id_ in self.__chunks
+
+    def __getitem__(self, id_):
+        """Get a chunk from the IFF file"""
+
+        assert isinstance(id_, text_type)
+
+        if not is_valid_chunk_id(id_):
+            raise KeyError("AIFF key must be four ASCII characters.")
+
+        try:
+            return self.__chunks[id_]
+        except KeyError:
+            raise KeyError(
+                "%r has no %r chunk" % (self.__fileobj.name, id_))
+
+    def __delitem__(self, id_):
+        """Remove a chunk from the IFF file"""
+
+        assert isinstance(id_, text_type)
+
+        if not is_valid_chunk_id(id_):
+            raise KeyError("AIFF key must be four ASCII characters.")
+
+        self.__chunks.pop(id_).delete()
+
+    def insert_chunk(self, id_):
+        """Insert a new chunk at the end of the IFF file"""
+
+        assert isinstance(id_, text_type)
+
+        if not is_valid_chunk_id(id_):
+            raise KeyError("AIFF key must be four ASCII characters.")
+
+        self.__fileobj.seek(self.__next_offset)
+        self.__fileobj.write(pack('>4si', id_.ljust(4).encode('ascii'), 0))
+        self.__fileobj.seek(self.__next_offset)
+        chunk = IFFChunk(self.__fileobj, self[u'FORM'])
+        self[u'FORM']._update_size(self[u'FORM'].data_size + chunk.size)
+
+        self.__chunks[id_] = chunk
+        self.__next_offset = chunk.offset + chunk.size
+
+
+class AIFFInfo(StreamInfo):
+    """AIFF audio stream information.
+
+    Information is parsed from the COMM chunk of the AIFF file
+
+    Useful attributes:
+
+    * length -- audio length, in seconds
+    * bitrate -- audio bitrate, in bits per second
+    * channels -- The number of audio channels
+    * sample_rate -- audio sample rate, in Hz
+    * sample_size -- The audio sample size
+    """
+
+    length = 0
+    bitrate = 0
+    channels = 0
+    sample_rate = 0
+
+    def __init__(self, fileobj):
+        iff = IFFFile(fileobj)
+        try:
+            common_chunk = iff[u'COMM']
+        except KeyError as e:
+            raise error(str(e))
+
+        data = common_chunk.read()
+
+        info = struct.unpack('>hLh10s', data[:18])
+        channels, frame_count, sample_size, sample_rate = info
+
+        self.sample_rate = int(read_float(sample_rate))
+        self.sample_size = sample_size
+        self.channels = channels
+        self.bitrate = channels * sample_size * self.sample_rate
+        self.length = frame_count / float(self.sample_rate)
+
+    def pprint(self):
+        return u"%d channel AIFF @ %d bps, %s Hz, %.2f seconds" % (
+            self.channels, self.bitrate, self.sample_rate, self.length)
+
+
+class _IFFID3(ID3):
+    """A AIFF file with ID3v2 tags"""
+
+    def _pre_load_header(self, fileobj):
+        try:
+            fileobj.seek(IFFFile(fileobj)[u'ID3'].data_offset)
+        except (InvalidChunk, KeyError):
+            raise ID3NoHeaderError("No ID3 chunk")
+
+    def save(self, filename=None, v2_version=4, v23_sep='/', padding=None):
+        """Save ID3v2 data to the AIFF file"""
+
+        if filename is None:
+            filename = self.filename
+
+        # Unlike the parent ID3.save method, we won't save to a blank file
+        # since we would have to construct a empty AIFF file
+        with open(filename, 'rb+') as fileobj:
+            iff_file = IFFFile(fileobj)
+
+            if u'ID3' not in iff_file:
+                iff_file.insert_chunk(u'ID3')
+
+            chunk = iff_file[u'ID3']
+
+            try:
+                data = self._prepare_data(
+                    fileobj, chunk.data_offset, chunk.data_size, v2_version,
+                    v23_sep, padding)
+            except ID3Error as e:
+                reraise(error, e, sys.exc_info()[2])
+
+            new_size = len(data)
+            new_size += new_size % 2  # pad byte
+            assert new_size % 2 == 0
+            chunk.resize(new_size)
+            data += (new_size - len(data)) * b'\x00'
+            assert new_size == len(data)
+            chunk.write(data)
+
+    def delete(self, filename=None):
+        """Completely removes the ID3 chunk from the AIFF file"""
+
+        if filename is None:
+            filename = self.filename
+        delete(filename)
+        self.clear()
+
+
+def delete(filename):
+    """Completely removes the ID3 chunk from the AIFF file"""
+
+    with open(filename, "rb+") as file_:
+        try:
+            del IFFFile(file_)[u'ID3']
+        except KeyError:
+            pass
+
+
+class AIFF(FileType):
+    """An AIFF audio file.
+
+    :ivar info: :class:`AIFFInfo`
+    :ivar tags: :class:`ID3`
+    """
+
+    _mimes = ["audio/aiff", "audio/x-aiff"]
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        filename = filename.lower()
+
+        return (header.startswith(b"FORM") * 2 + endswith(filename, b".aif") +
+                endswith(filename, b".aiff") + endswith(filename, b".aifc"))
+
+    def add_tags(self):
+        """Add an empty ID3 tag to the file."""
+        if self.tags is None:
+            self.tags = _IFFID3()
+        else:
+            raise error("an ID3 tag already exists")
+
+    def load(self, filename, **kwargs):
+        """Load stream and tag information from a file."""
+        self.filename = filename
+
+        try:
+            self.tags = _IFFID3(filename, **kwargs)
+        except ID3NoHeaderError:
+            self.tags = None
+        except ID3Error as e:
+            raise error(e)
+
+        with open(filename, "rb") as fileobj:
+            self.info = AIFFInfo(fileobj)
+
+
+Open = AIFF
diff --git a/resources/lib/mutagen/apev2.py b/resources/lib/mutagen/apev2.py
new file mode 100644
index 00000000..3b79aba9
--- /dev/null
+++ b/resources/lib/mutagen/apev2.py
@@ -0,0 +1,710 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2005  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""APEv2 reading and writing.
+
+The APEv2 format is most commonly used with Musepack files, but is
+also the format of choice for WavPack and other formats. Some MP3s
+also have APEv2 tags, but this can cause problems with many MP3
+decoders and taggers.
+
+APEv2 tags, like Vorbis comments, are freeform key=value pairs. APEv2
+keys can be any ASCII string with characters from 0x20 to 0x7E,
+between 2 and 255 characters long.  Keys are case-sensitive, but
+readers are recommended to be case insensitive, and it is forbidden to
+multiple keys which differ only in case.  Keys are usually stored
+title-cased (e.g. 'Artist' rather than 'artist').
+
+APEv2 values are slightly more structured than Vorbis comments; values
+are flagged as one of text, binary, or an external reference (usually
+a URI).
+
+Based off the format specification found at
+http://wiki.hydrogenaudio.org/index.php?title=APEv2_specification.
+"""
+
+__all__ = ["APEv2", "APEv2File", "Open", "delete"]
+
+import sys
+import struct
+from collections import MutableSequence
+
+from ._compat import (cBytesIO, PY3, text_type, PY2, reraise, swap_to_string,
+                      xrange)
+from mutagen import Metadata, FileType, StreamInfo
+from mutagen._util import (DictMixin, cdata, delete_bytes, total_ordering,
+                           MutagenError)
+
+
+def is_valid_apev2_key(key):
+    if not isinstance(key, text_type):
+        if PY3:
+            raise TypeError("APEv2 key must be str")
+
+        try:
+            key = key.decode('ascii')
+        except UnicodeDecodeError:
+            return False
+
+    # PY26 - Change to set literal syntax (since set is faster than list here)
+    return ((2 <= len(key) <= 255) and (min(key) >= u' ') and
+            (max(key) <= u'~') and
+            (key not in [u"OggS", u"TAG", u"ID3", u"MP+"]))
+
+# There are three different kinds of APE tag values.
+# "0: Item contains text information coded in UTF-8
+#  1: Item contains binary information
+#  2: Item is a locator of external stored information [e.g. URL]
+#  3: reserved"
+TEXT, BINARY, EXTERNAL = xrange(3)
+
+HAS_HEADER = 1 << 31
+HAS_NO_FOOTER = 1 << 30
+IS_HEADER = 1 << 29
+
+
+class error(IOError, MutagenError):
+    pass
+
+
+class APENoHeaderError(error, ValueError):
+    pass
+
+
+class APEUnsupportedVersionError(error, ValueError):
+    pass
+
+
+class APEBadItemError(error, ValueError):
+    pass
+
+
+class _APEv2Data(object):
+    # Store offsets of the important parts of the file.
+    start = header = data = footer = end = None
+    # Footer or header; seek here and read 32 to get version/size/items/flags
+    metadata = None
+    # Actual tag data
+    tag = None
+
+    version = None
+    size = None
+    items = None
+    flags = 0
+
+    # The tag is at the start rather than the end. A tag at both
+    # the start and end of the file (i.e. the tag is the whole file)
+    # is not considered to be at the start.
+    is_at_start = False
+
+    def __init__(self, fileobj):
+        self.__find_metadata(fileobj)
+
+        if self.header is None:
+            self.metadata = self.footer
+        elif self.footer is None:
+            self.metadata = self.header
+        else:
+            self.metadata = max(self.header, self.footer)
+
+        if self.metadata is None:
+            return
+
+        self.__fill_missing(fileobj)
+        self.__fix_brokenness(fileobj)
+        if self.data is not None:
+            fileobj.seek(self.data)
+            self.tag = fileobj.read(self.size)
+
+    def __find_metadata(self, fileobj):
+        # Try to find a header or footer.
+
+        # Check for a simple footer.
+        try:
+            fileobj.seek(-32, 2)
+        except IOError:
+            fileobj.seek(0, 2)
+            return
+        if fileobj.read(8) == b"APETAGEX":
+            fileobj.seek(-8, 1)
+            self.footer = self.metadata = fileobj.tell()
+            return
+
+        # Check for an APEv2 tag followed by an ID3v1 tag at the end.
+        try:
+            fileobj.seek(-128, 2)
+            if fileobj.read(3) == b"TAG":
+
+                fileobj.seek(-35, 1)  # "TAG" + header length
+                if fileobj.read(8) == b"APETAGEX":
+                    fileobj.seek(-8, 1)
+                    self.footer = fileobj.tell()
+                    return
+
+                # ID3v1 tag at the end, maybe preceded by Lyrics3v2.
+                # (http://www.id3.org/lyrics3200.html)
+                # (header length - "APETAGEX") - "LYRICS200"
+                fileobj.seek(15, 1)
+                if fileobj.read(9) == b'LYRICS200':
+                    fileobj.seek(-15, 1)  # "LYRICS200" + size tag
+                    try:
+                        offset = int(fileobj.read(6))
+                    except ValueError:
+                        raise IOError
+
+                    fileobj.seek(-32 - offset - 6, 1)
+                    if fileobj.read(8) == b"APETAGEX":
+                        fileobj.seek(-8, 1)
+                        self.footer = fileobj.tell()
+                        return
+
+        except IOError:
+            pass
+
+        # Check for a tag at the start.
+        fileobj.seek(0, 0)
+        if fileobj.read(8) == b"APETAGEX":
+            self.is_at_start = True
+            self.header = 0
+
+    def __fill_missing(self, fileobj):
+        fileobj.seek(self.metadata + 8)
+        self.version = fileobj.read(4)
+        self.size = cdata.uint_le(fileobj.read(4))
+        self.items = cdata.uint_le(fileobj.read(4))
+        self.flags = cdata.uint_le(fileobj.read(4))
+
+        if self.header is not None:
+            self.data = self.header + 32
+            # If we're reading the header, the size is the header
+            # offset + the size, which includes the footer.
+            self.end = self.data + self.size
+            fileobj.seek(self.end - 32, 0)
+            if fileobj.read(8) == b"APETAGEX":
+                self.footer = self.end - 32
+        elif self.footer is not None:
+            self.end = self.footer + 32
+            self.data = self.end - self.size
+            if self.flags & HAS_HEADER:
+                self.header = self.data - 32
+            else:
+                self.header = self.data
+        else:
+            raise APENoHeaderError("No APE tag found")
+
+        # exclude the footer from size
+        if self.footer is not None:
+            self.size -= 32
+
+    def __fix_brokenness(self, fileobj):
+        # Fix broken tags written with PyMusepack.
+        if self.header is not None:
+            start = self.header
+        else:
+            start = self.data
+        fileobj.seek(start)
+
+        while start > 0:
+            # Clean up broken writing from pre-Mutagen PyMusepack.
+            # It didn't remove the first 24 bytes of header.
+            try:
+                fileobj.seek(-24, 1)
+            except IOError:
+                break
+            else:
+                if fileobj.read(8) == b"APETAGEX":
+                    fileobj.seek(-8, 1)
+                    start = fileobj.tell()
+                else:
+                    break
+        self.start = start
+
+
+class _CIDictProxy(DictMixin):
+
+    def __init__(self, *args, **kwargs):
+        self.__casemap = {}
+        self.__dict = {}
+        super(_CIDictProxy, self).__init__(*args, **kwargs)
+        # Internally all names are stored as lowercase, but the case
+        # they were set with is remembered and used when saving.  This
+        # is roughly in line with the standard, which says that keys
+        # are case-sensitive but two keys differing only in case are
+        # not allowed, and recommends case-insensitive
+        # implementations.
+
+    def __getitem__(self, key):
+        return self.__dict[key.lower()]
+
+    def __setitem__(self, key, value):
+        lower = key.lower()
+        self.__casemap[lower] = key
+        self.__dict[lower] = value
+
+    def __delitem__(self, key):
+        lower = key.lower()
+        del(self.__casemap[lower])
+        del(self.__dict[lower])
+
+    def keys(self):
+        return [self.__casemap.get(key, key) for key in self.__dict.keys()]
+
+
+class APEv2(_CIDictProxy, Metadata):
+    """A file with an APEv2 tag.
+
+    ID3v1 tags are silently ignored and overwritten.
+    """
+
+    filename = None
+
+    def pprint(self):
+        """Return tag key=value pairs in a human-readable format."""
+
+        items = sorted(self.items())
+        return u"\n".join(u"%s=%s" % (k, v.pprint()) for k, v in items)
+
+    def load(self, filename):
+        """Load tags from a filename."""
+
+        self.filename = filename
+        with open(filename, "rb") as fileobj:
+            data = _APEv2Data(fileobj)
+
+        if data.tag:
+            self.clear()
+            self.__parse_tag(data.tag, data.items)
+        else:
+            raise APENoHeaderError("No APE tag found")
+
+    def __parse_tag(self, tag, count):
+        fileobj = cBytesIO(tag)
+
+        for i in xrange(count):
+            size_data = fileobj.read(4)
+            # someone writes wrong item counts
+            if not size_data:
+                break
+            size = cdata.uint_le(size_data)
+            flags = cdata.uint_le(fileobj.read(4))
+
+            # Bits 1 and 2 bits are flags, 0-3
+            # Bit 0 is read/write flag, ignored
+            kind = (flags & 6) >> 1
+            if kind == 3:
+                raise APEBadItemError("value type must be 0, 1, or 2")
+            key = value = fileobj.read(1)
+            while key[-1:] != b'\x00' and value:
+                value = fileobj.read(1)
+                key += value
+            if key[-1:] == b"\x00":
+                key = key[:-1]
+            if PY3:
+                try:
+                    key = key.decode("ascii")
+                except UnicodeError as err:
+                    reraise(APEBadItemError, err, sys.exc_info()[2])
+            value = fileobj.read(size)
+
+            value = _get_value_type(kind)._new(value)
+
+            self[key] = value
+
+    def __getitem__(self, key):
+        if not is_valid_apev2_key(key):
+            raise KeyError("%r is not a valid APEv2 key" % key)
+        if PY2:
+            key = key.encode('ascii')
+
+        return super(APEv2, self).__getitem__(key)
+
+    def __delitem__(self, key):
+        if not is_valid_apev2_key(key):
+            raise KeyError("%r is not a valid APEv2 key" % key)
+        if PY2:
+            key = key.encode('ascii')
+
+        super(APEv2, self).__delitem__(key)
+
+    def __setitem__(self, key, value):
+        """'Magic' value setter.
+
+        This function tries to guess at what kind of value you want to
+        store. If you pass in a valid UTF-8 or Unicode string, it
+        treats it as a text value. If you pass in a list, it treats it
+        as a list of string/Unicode values.  If you pass in a string
+        that is not valid UTF-8, it assumes it is a binary value.
+
+        Python 3: all bytes will be assumed to be a byte value, even
+        if they are valid utf-8.
+
+        If you need to force a specific type of value (e.g. binary
+        data that also happens to be valid UTF-8, or an external
+        reference), use the APEValue factory and set the value to the
+        result of that::
+
+            from mutagen.apev2 import APEValue, EXTERNAL
+            tag['Website'] = APEValue('http://example.org', EXTERNAL)
+        """
+
+        if not is_valid_apev2_key(key):
+            raise KeyError("%r is not a valid APEv2 key" % key)
+
+        if PY2:
+            key = key.encode('ascii')
+
+        if not isinstance(value, _APEValue):
+            # let's guess at the content if we're not already a value...
+            if isinstance(value, text_type):
+                # unicode? we've got to be text.
+                value = APEValue(value, TEXT)
+            elif isinstance(value, list):
+                items = []
+                for v in value:
+                    if not isinstance(v, text_type):
+                        if PY3:
+                            raise TypeError("item in list not str")
+                        v = v.decode("utf-8")
+                    items.append(v)
+
+                # list? text.
+                value = APEValue(u"\0".join(items), TEXT)
+            else:
+                if PY3:
+                    value = APEValue(value, BINARY)
+                else:
+                    try:
+                        value.decode("utf-8")
+                    except UnicodeError:
+                        # invalid UTF8 text, probably binary
+                        value = APEValue(value, BINARY)
+                    else:
+                        # valid UTF8, probably text
+                        value = APEValue(value, TEXT)
+
+        super(APEv2, self).__setitem__(key, value)
+
+    def save(self, filename=None):
+        """Save changes to a file.
+
+        If no filename is given, the one most recently loaded is used.
+
+        Tags are always written at the end of the file, and include
+        a header and a footer.
+        """
+
+        filename = filename or self.filename
+        try:
+            fileobj = open(filename, "r+b")
+        except IOError:
+            fileobj = open(filename, "w+b")
+        data = _APEv2Data(fileobj)
+
+        if data.is_at_start:
+            delete_bytes(fileobj, data.end - data.start, data.start)
+        elif data.start is not None:
+            fileobj.seek(data.start)
+            # Delete an ID3v1 tag if present, too.
+            fileobj.truncate()
+        fileobj.seek(0, 2)
+
+        tags = []
+        for key, value in self.items():
+            # Packed format for an item:
+            # 4B: Value length
+            # 4B: Value type
+            # Key name
+            # 1B: Null
+            # Key value
+            value_data = value._write()
+            if not isinstance(key, bytes):
+                key = key.encode("utf-8")
+            tag_data = bytearray()
+            tag_data += struct.pack("<2I", len(value_data), value.kind << 1)
+            tag_data += key + b"\0" + value_data
+            tags.append(bytes(tag_data))
+
+        # "APE tags items should be sorted ascending by size... This is
+        # not a MUST, but STRONGLY recommended. Actually the items should
+        # be sorted by importance/byte, but this is not feasible."
+        tags.sort(key=len)
+        num_tags = len(tags)
+        tags = b"".join(tags)
+
+        header = bytearray(b"APETAGEX")
+        # version, tag size, item count, flags
+        header += struct.pack("<4I", 2000, len(tags) + 32, num_tags,
+                              HAS_HEADER | IS_HEADER)
+        header += b"\0" * 8
+        fileobj.write(header)
+
+        fileobj.write(tags)
+
+        footer = bytearray(b"APETAGEX")
+        footer += struct.pack("<4I", 2000, len(tags) + 32, num_tags,
+                              HAS_HEADER)
+        footer += b"\0" * 8
+
+        fileobj.write(footer)
+        fileobj.close()
+
+    def delete(self, filename=None):
+        """Remove tags from a file."""
+
+        filename = filename or self.filename
+        with open(filename, "r+b") as fileobj:
+            data = _APEv2Data(fileobj)
+            if data.start is not None and data.size is not None:
+                delete_bytes(fileobj, data.end - data.start, data.start)
+
+        self.clear()
+
+
+Open = APEv2
+
+
+def delete(filename):
+    """Remove tags from a file."""
+
+    try:
+        APEv2(filename).delete()
+    except APENoHeaderError:
+        pass
+
+
+def _get_value_type(kind):
+    """Returns a _APEValue subclass or raises ValueError"""
+
+    if kind == TEXT:
+        return APETextValue
+    elif kind == BINARY:
+        return APEBinaryValue
+    elif kind == EXTERNAL:
+        return APEExtValue
+    raise ValueError("unknown kind %r" % kind)
+
+
+def APEValue(value, kind):
+    """APEv2 tag value factory.
+
+    Use this if you need to specify the value's type manually.  Binary
+    and text data are automatically detected by APEv2.__setitem__.
+    """
+
+    try:
+        type_ = _get_value_type(kind)
+    except ValueError:
+        raise ValueError("kind must be TEXT, BINARY, or EXTERNAL")
+    else:
+        return type_(value)
+
+
+class _APEValue(object):
+
+    kind = None
+    value = None
+
+    def __init__(self, value, kind=None):
+        # kind kwarg is for backwards compat
+        if kind is not None and kind != self.kind:
+            raise ValueError
+        self.value = self._validate(value)
+
+    @classmethod
+    def _new(cls, data):
+        instance = cls.__new__(cls)
+        instance._parse(data)
+        return instance
+
+    def _parse(self, data):
+        """Sets value or raises APEBadItemError"""
+
+        raise NotImplementedError
+
+    def _write(self):
+        """Returns bytes"""
+
+        raise NotImplementedError
+
+    def _validate(self, value):
+        """Returns validated value or raises TypeError/ValueErrr"""
+
+        raise NotImplementedError
+
+    def __repr__(self):
+        return "%s(%r, %d)" % (type(self).__name__, self.value, self.kind)
+
+
+@swap_to_string
+@total_ordering
+class _APEUtf8Value(_APEValue):
+
+    def _parse(self, data):
+        try:
+            self.value = data.decode("utf-8")
+        except UnicodeDecodeError as e:
+            reraise(APEBadItemError, e, sys.exc_info()[2])
+
+    def _validate(self, value):
+        if not isinstance(value, text_type):
+            if PY3:
+                raise TypeError("value not str")
+            else:
+                value = value.decode("utf-8")
+        return value
+
+    def _write(self):
+        return self.value.encode("utf-8")
+
+    def __len__(self):
+        return len(self.value)
+
+    def __bytes__(self):
+        return self._write()
+
+    def __eq__(self, other):
+        return self.value == other
+
+    def __lt__(self, other):
+        return self.value < other
+
+    def __str__(self):
+        return self.value
+
+
+class APETextValue(_APEUtf8Value, MutableSequence):
+    """An APEv2 text value.
+
+    Text values are Unicode/UTF-8 strings. They can be accessed like
+    strings (with a null separating the values), or arrays of strings.
+    """
+
+    kind = TEXT
+
+    def __iter__(self):
+        """Iterate over the strings of the value (not the characters)"""
+
+        return iter(self.value.split(u"\0"))
+
+    def __getitem__(self, index):
+        return self.value.split(u"\0")[index]
+
+    def __len__(self):
+        return self.value.count(u"\0") + 1
+
+    def __setitem__(self, index, value):
+        if not isinstance(value, text_type):
+            if PY3:
+                raise TypeError("value not str")
+            else:
+                value = value.decode("utf-8")
+
+        values = list(self)
+        values[index] = value
+        self.value = u"\0".join(values)
+
+    def insert(self, index, value):
+        if not isinstance(value, text_type):
+            if PY3:
+                raise TypeError("value not str")
+            else:
+                value = value.decode("utf-8")
+
+        values = list(self)
+        values.insert(index, value)
+        self.value = u"\0".join(values)
+
+    def __delitem__(self, index):
+        values = list(self)
+        del values[index]
+        self.value = u"\0".join(values)
+
+    def pprint(self):
+        return u" / ".join(self)
+
+
+@swap_to_string
+@total_ordering
+class APEBinaryValue(_APEValue):
+    """An APEv2 binary value."""
+
+    kind = BINARY
+
+    def _parse(self, data):
+        self.value = data
+
+    def _write(self):
+        return self.value
+
+    def _validate(self, value):
+        if not isinstance(value, bytes):
+            raise TypeError("value not bytes")
+        return bytes(value)
+
+    def __len__(self):
+        return len(self.value)
+
+    def __bytes__(self):
+        return self._write()
+
+    def __eq__(self, other):
+        return self.value == other
+
+    def __lt__(self, other):
+        return self.value < other
+
+    def pprint(self):
+        return u"[%d bytes]" % len(self)
+
+
+class APEExtValue(_APEUtf8Value):
+    """An APEv2 external value.
+
+    External values are usually URI or IRI strings.
+    """
+
+    kind = EXTERNAL
+
+    def pprint(self):
+        return u"[External] %s" % self.value
+
+
+class APEv2File(FileType):
+    class _Info(StreamInfo):
+        length = 0
+        bitrate = 0
+
+        def __init__(self, fileobj):
+            pass
+
+        @staticmethod
+        def pprint():
+            return u"Unknown format with APEv2 tag."
+
+    def load(self, filename):
+        self.filename = filename
+        self.info = self._Info(open(filename, "rb"))
+        try:
+            self.tags = APEv2(filename)
+        except APENoHeaderError:
+            self.tags = None
+
+    def add_tags(self):
+        if self.tags is None:
+            self.tags = APEv2()
+        else:
+            raise error("%r already has tags: %r" % (self, self.tags))
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        try:
+            fileobj.seek(-160, 2)
+        except IOError:
+            fileobj.seek(0)
+        footer = fileobj.read()
+        return ((b"APETAGEX" in footer) - header.startswith(b"ID3"))
diff --git a/resources/lib/mutagen/asf/__init__.py b/resources/lib/mutagen/asf/__init__.py
new file mode 100644
index 00000000..e667192d
--- /dev/null
+++ b/resources/lib/mutagen/asf/__init__.py
@@ -0,0 +1,319 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2005-2006  Joe Wreschnig
+# Copyright (C) 2006-2007  Lukas Lalinsky
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Read and write ASF (Window Media Audio) files."""
+
+__all__ = ["ASF", "Open"]
+
+from mutagen import FileType, Metadata, StreamInfo
+from mutagen._util import resize_bytes, DictMixin
+from mutagen._compat import string_types, long_, PY3, izip
+
+from ._util import error, ASFError, ASFHeaderError
+from ._objects import HeaderObject, MetadataLibraryObject, MetadataObject, \
+    ExtendedContentDescriptionObject, HeaderExtensionObject, \
+    ContentDescriptionObject
+from ._attrs import ASFGUIDAttribute, ASFWordAttribute, ASFQWordAttribute, \
+    ASFDWordAttribute, ASFBoolAttribute, ASFByteArrayAttribute, \
+    ASFUnicodeAttribute, ASFBaseAttribute, ASFValue
+
+
+# pyflakes
+error, ASFError, ASFHeaderError, ASFValue
+
+
+class ASFInfo(StreamInfo):
+    """ASF stream information."""
+
+    length = 0.0
+    """Length in seconds (`float`)"""
+
+    sample_rate = 0
+    """Sample rate in Hz (`int`)"""
+
+    bitrate = 0
+    """Bitrate in bps (`int`)"""
+
+    channels = 0
+    """Number of channels (`int`)"""
+
+    codec_type = u""
+    """Name of the codec type of the first audio stream or
+    an empty string if unknown. Example: ``Windows Media Audio 9 Standard``
+    (:class:`mutagen.text`)
+    """
+
+    codec_name = u""
+    """Name and maybe version of the codec used. Example:
+    ``Windows Media Audio 9.1`` (:class:`mutagen.text`)
+    """
+
+    codec_description = u""
+    """Further information on the codec used.
+    Example: ``64 kbps, 48 kHz, stereo 2-pass CBR`` (:class:`mutagen.text`)
+    """
+
+    def __init__(self):
+        self.length = 0.0
+        self.sample_rate = 0
+        self.bitrate = 0
+        self.channels = 0
+        self.codec_type = u""
+        self.codec_name = u""
+        self.codec_description = u""
+
+    def pprint(self):
+        """Returns a stream information text summary
+
+        :rtype: text
+        """
+
+        s = u"ASF (%s) %d bps, %s Hz, %d channels, %.2f seconds" % (
+            self.codec_type or self.codec_name or u"???", self.bitrate,
+            self.sample_rate, self.channels, self.length)
+        return s
+
+
+class ASFTags(list, DictMixin, Metadata):
+    """Dictionary containing ASF attributes."""
+
+    def __getitem__(self, key):
+        """A list of values for the key.
+
+        This is a copy, so comment['title'].append('a title') will not
+        work.
+
+        """
+
+        # PY3 only
+        if isinstance(key, slice):
+            return list.__getitem__(self, key)
+
+        values = [value for (k, value) in self if k == key]
+        if not values:
+            raise KeyError(key)
+        else:
+            return values
+
+    def __delitem__(self, key):
+        """Delete all values associated with the key."""
+
+        # PY3 only
+        if isinstance(key, slice):
+            return list.__delitem__(self, key)
+
+        to_delete = [x for x in self if x[0] == key]
+        if not to_delete:
+            raise KeyError(key)
+        else:
+            for k in to_delete:
+                self.remove(k)
+
+    def __contains__(self, key):
+        """Return true if the key has any values."""
+        for k, value in self:
+            if k == key:
+                return True
+        else:
+            return False
+
+    def __setitem__(self, key, values):
+        """Set a key's value or values.
+
+        Setting a value overwrites all old ones. The value may be a
+        list of Unicode or UTF-8 strings, or a single Unicode or UTF-8
+        string.
+        """
+
+        # PY3 only
+        if isinstance(key, slice):
+            return list.__setitem__(self, key, values)
+
+        if not isinstance(values, list):
+            values = [values]
+
+        to_append = []
+        for value in values:
+            if not isinstance(value, ASFBaseAttribute):
+                if isinstance(value, string_types):
+                    value = ASFUnicodeAttribute(value)
+                elif PY3 and isinstance(value, bytes):
+                    value = ASFByteArrayAttribute(value)
+                elif isinstance(value, bool):
+                    value = ASFBoolAttribute(value)
+                elif isinstance(value, int):
+                    value = ASFDWordAttribute(value)
+                elif isinstance(value, long_):
+                    value = ASFQWordAttribute(value)
+                else:
+                    raise TypeError("Invalid type %r" % type(value))
+            to_append.append((key, value))
+
+        try:
+            del(self[key])
+        except KeyError:
+            pass
+
+        self.extend(to_append)
+
+    def keys(self):
+        """Return a sequence of all keys in the comment."""
+
+        return self and set(next(izip(*self)))
+
+    def as_dict(self):
+        """Return a copy of the comment data in a real dict."""
+
+        d = {}
+        for key, value in self:
+            d.setdefault(key, []).append(value)
+        return d
+
+    def pprint(self):
+        """Returns a string containing all key, value pairs.
+
+        :rtype: text
+        """
+
+        return "\n".join("%s=%s" % (k, v) for k, v in self)
+
+
+UNICODE = ASFUnicodeAttribute.TYPE
+"""Unicode string type"""
+
+BYTEARRAY = ASFByteArrayAttribute.TYPE
+"""Byte array type"""
+
+BOOL = ASFBoolAttribute.TYPE
+"""Bool type"""
+
+DWORD = ASFDWordAttribute.TYPE
+""""DWord type (uint32)"""
+
+QWORD = ASFQWordAttribute.TYPE
+"""QWord type (uint64)"""
+
+WORD = ASFWordAttribute.TYPE
+"""Word type (uint16)"""
+
+GUID = ASFGUIDAttribute.TYPE
+"""GUID type"""
+
+
+class ASF(FileType):
+    """An ASF file, probably containing WMA or WMV.
+
+    :param filename: a filename to load
+    :raises mutagen.asf.error: In case loading fails
+    """
+
+    _mimes = ["audio/x-ms-wma", "audio/x-ms-wmv", "video/x-ms-asf",
+              "audio/x-wma", "video/x-wmv"]
+
+    info = None
+    """A `ASFInfo` instance"""
+
+    tags = None
+    """A `ASFTags` instance"""
+
+    def load(self, filename):
+        self.filename = filename
+        self.info = ASFInfo()
+        self.tags = ASFTags()
+
+        with open(filename, "rb") as fileobj:
+            self._tags = {}
+
+            self._header = HeaderObject.parse_full(self, fileobj)
+
+            for guid in [ContentDescriptionObject.GUID,
+                    ExtendedContentDescriptionObject.GUID, MetadataObject.GUID,
+                    MetadataLibraryObject.GUID]:
+                self.tags.extend(self._tags.pop(guid, []))
+
+            assert not self._tags
+
+    def save(self, filename=None, padding=None):
+        """Save tag changes back to the loaded file.
+
+        :param padding: A callback which returns the amount of padding to use.
+            See :class:`mutagen.PaddingInfo`
+
+        :raises mutagen.asf.error: In case saving fails
+        """
+
+        if filename is not None and filename != self.filename:
+            raise ValueError("saving to another file not supported atm")
+
+        # Move attributes to the right objects
+        self.to_content_description = {}
+        self.to_extended_content_description = {}
+        self.to_metadata = {}
+        self.to_metadata_library = []
+        for name, value in self.tags:
+            library_only = (value.data_size() > 0xFFFF or value.TYPE == GUID)
+            can_cont_desc = value.TYPE == UNICODE
+
+            if library_only or value.language is not None:
+                self.to_metadata_library.append((name, value))
+            elif value.stream is not None:
+                if name not in self.to_metadata:
+                    self.to_metadata[name] = value
+                else:
+                    self.to_metadata_library.append((name, value))
+            elif name in ContentDescriptionObject.NAMES:
+                if name not in self.to_content_description and can_cont_desc:
+                    self.to_content_description[name] = value
+                else:
+                    self.to_metadata_library.append((name, value))
+            else:
+                if name not in self.to_extended_content_description:
+                    self.to_extended_content_description[name] = value
+                else:
+                    self.to_metadata_library.append((name, value))
+
+        # Add missing objects
+        header = self._header
+        if header.get_child(ContentDescriptionObject.GUID) is None:
+            header.objects.append(ContentDescriptionObject())
+        if header.get_child(ExtendedContentDescriptionObject.GUID) is None:
+            header.objects.append(ExtendedContentDescriptionObject())
+        header_ext = header.get_child(HeaderExtensionObject.GUID)
+        if header_ext is None:
+            header_ext = HeaderExtensionObject()
+            header.objects.append(header_ext)
+        if header_ext.get_child(MetadataObject.GUID) is None:
+            header_ext.objects.append(MetadataObject())
+        if header_ext.get_child(MetadataLibraryObject.GUID) is None:
+            header_ext.objects.append(MetadataLibraryObject())
+
+        # Render to file
+        with open(self.filename, "rb+") as fileobj:
+            old_size = header.parse_size(fileobj)[0]
+            data = header.render_full(self, fileobj, old_size, padding)
+            size = len(data)
+            resize_bytes(fileobj, old_size, size, 0)
+            fileobj.seek(0)
+            fileobj.write(data)
+
+    def add_tags(self):
+        raise ASFError
+
+    def delete(self, filename=None):
+
+        if filename is not None and filename != self.filename:
+            raise ValueError("saving to another file not supported atm")
+
+        self.tags.clear()
+        self.save(padding=lambda x: 0)
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return header.startswith(HeaderObject.GUID) * 2
+
+Open = ASF
diff --git a/resources/lib/mutagen/asf/__pycache__/__init__.cpython-35.pyc b/resources/lib/mutagen/asf/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..277e2c5fe677f3fb5deae56dc081e4c55d092175
GIT binary patch
literal 8567
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
GqW=Or25P<l

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/asf/__pycache__/_attrs.cpython-35.pyc b/resources/lib/mutagen/asf/__pycache__/_attrs.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..aa916eddf8713bbf7a8cf3defef0062f0f3221c9
GIT binary patch
literal 15131
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/asf/__pycache__/_objects.cpython-35.pyc b/resources/lib/mutagen/asf/__pycache__/_objects.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cb0810d8909b8a57de791aca68842bd2ba3565d6
GIT binary patch
literal 15903
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
NVtv;%exDt){tt%Az9#?x

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/asf/__pycache__/_util.cpython-35.pyc b/resources/lib/mutagen/asf/__pycache__/_util.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..661bff507dd00b7435a4f08bb07b58b7deed1cf4
GIT binary patch
literal 10603
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
J|NUPB{{?fvP5S@<

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/asf/_attrs.py b/resources/lib/mutagen/asf/_attrs.py
new file mode 100644
index 00000000..4621c9fa
--- /dev/null
+++ b/resources/lib/mutagen/asf/_attrs.py
@@ -0,0 +1,438 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2005-2006  Joe Wreschnig
+# Copyright (C) 2006-2007  Lukas Lalinsky
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+import sys
+import struct
+
+from mutagen._compat import swap_to_string, text_type, PY2, reraise
+from mutagen._util import total_ordering
+
+from ._util import ASFError
+
+
+class ASFBaseAttribute(object):
+    """Generic attribute."""
+
+    TYPE = None
+
+    _TYPES = {}
+
+    value = None
+    """The Python value of this attribute (type depends on the class)"""
+
+    language = None
+    """Language"""
+
+    stream = None
+    """Stream"""
+
+    def __init__(self, value=None, data=None, language=None,
+                 stream=None, **kwargs):
+        self.language = language
+        self.stream = stream
+        if data:
+            self.value = self.parse(data, **kwargs)
+        else:
+            if value is None:
+                # we used to support not passing any args and instead assign
+                # them later, keep that working..
+                self.value = None
+            else:
+                self.value = self._validate(value)
+
+    @classmethod
+    def _register(cls, other):
+        cls._TYPES[other.TYPE] = other
+        return other
+
+    @classmethod
+    def _get_type(cls, type_):
+        """Raises KeyError"""
+
+        return cls._TYPES[type_]
+
+    def _validate(self, value):
+        """Raises TypeError or ValueError in case the user supplied value
+        isn't valid.
+        """
+
+        return value
+
+    def data_size(self):
+        raise NotImplementedError
+
+    def __repr__(self):
+        name = "%s(%r" % (type(self).__name__, self.value)
+        if self.language:
+            name += ", language=%d" % self.language
+        if self.stream:
+            name += ", stream=%d" % self.stream
+        name += ")"
+        return name
+
+    def render(self, name):
+        name = name.encode("utf-16-le") + b"\x00\x00"
+        data = self._render()
+        return (struct.pack("<H", len(name)) + name +
+                struct.pack("<HH", self.TYPE, len(data)) + data)
+
+    def render_m(self, name):
+        name = name.encode("utf-16-le") + b"\x00\x00"
+        if self.TYPE == 2:
+            data = self._render(dword=False)
+        else:
+            data = self._render()
+        return (struct.pack("<HHHHI", 0, self.stream or 0, len(name),
+                            self.TYPE, len(data)) + name + data)
+
+    def render_ml(self, name):
+        name = name.encode("utf-16-le") + b"\x00\x00"
+        if self.TYPE == 2:
+            data = self._render(dword=False)
+        else:
+            data = self._render()
+
+        return (struct.pack("<HHHHI", self.language or 0, self.stream or 0,
+                            len(name), self.TYPE, len(data)) + name + data)
+
+
+@ASFBaseAttribute._register
+@swap_to_string
+@total_ordering
+class ASFUnicodeAttribute(ASFBaseAttribute):
+    """Unicode string attribute.
+
+    ::
+
+        ASFUnicodeAttribute(u'some text')
+    """
+
+    TYPE = 0x0000
+
+    def parse(self, data):
+        try:
+            return data.decode("utf-16-le").strip("\x00")
+        except UnicodeDecodeError as e:
+            reraise(ASFError, e, sys.exc_info()[2])
+
+    def _validate(self, value):
+        if not isinstance(value, text_type):
+            if PY2:
+                return value.decode("utf-8")
+            else:
+                raise TypeError("%r not str" % value)
+        return value
+
+    def _render(self):
+        return self.value.encode("utf-16-le") + b"\x00\x00"
+
+    def data_size(self):
+        return len(self._render())
+
+    def __bytes__(self):
+        return self.value.encode("utf-16-le")
+
+    def __str__(self):
+        return self.value
+
+    def __eq__(self, other):
+        return text_type(self) == other
+
+    def __lt__(self, other):
+        return text_type(self) < other
+
+    __hash__ = ASFBaseAttribute.__hash__
+
+
+@ASFBaseAttribute._register
+@swap_to_string
+@total_ordering
+class ASFByteArrayAttribute(ASFBaseAttribute):
+    """Byte array attribute.
+
+    ::
+
+        ASFByteArrayAttribute(b'1234')
+    """
+    TYPE = 0x0001
+
+    def parse(self, data):
+        assert isinstance(data, bytes)
+        return data
+
+    def _render(self):
+        assert isinstance(self.value, bytes)
+        return self.value
+
+    def _validate(self, value):
+        if not isinstance(value, bytes):
+            raise TypeError("must be bytes/str: %r" % value)
+        return value
+
+    def data_size(self):
+        return len(self.value)
+
+    def __bytes__(self):
+        return self.value
+
+    def __str__(self):
+        return "[binary data (%d bytes)]" % len(self.value)
+
+    def __eq__(self, other):
+        return self.value == other
+
+    def __lt__(self, other):
+        return self.value < other
+
+    __hash__ = ASFBaseAttribute.__hash__
+
+
+@ASFBaseAttribute._register
+@swap_to_string
+@total_ordering
+class ASFBoolAttribute(ASFBaseAttribute):
+    """Bool attribute.
+
+    ::
+
+        ASFBoolAttribute(True)
+    """
+
+    TYPE = 0x0002
+
+    def parse(self, data, dword=True):
+        if dword:
+            return struct.unpack("<I", data)[0] == 1
+        else:
+            return struct.unpack("<H", data)[0] == 1
+
+    def _render(self, dword=True):
+        if dword:
+            return struct.pack("<I", bool(self.value))
+        else:
+            return struct.pack("<H", bool(self.value))
+
+    def _validate(self, value):
+        return bool(value)
+
+    def data_size(self):
+        return 4
+
+    def __bool__(self):
+        return bool(self.value)
+
+    def __bytes__(self):
+        return text_type(self.value).encode('utf-8')
+
+    def __str__(self):
+        return text_type(self.value)
+
+    def __eq__(self, other):
+        return bool(self.value) == other
+
+    def __lt__(self, other):
+        return bool(self.value) < other
+
+    __hash__ = ASFBaseAttribute.__hash__
+
+
+@ASFBaseAttribute._register
+@swap_to_string
+@total_ordering
+class ASFDWordAttribute(ASFBaseAttribute):
+    """DWORD attribute.
+
+    ::
+
+        ASFDWordAttribute(42)
+    """
+
+    TYPE = 0x0003
+
+    def parse(self, data):
+        return struct.unpack("<L", data)[0]
+
+    def _render(self):
+        return struct.pack("<L", self.value)
+
+    def _validate(self, value):
+        value = int(value)
+        if not 0 <= value <= 2 ** 32 - 1:
+            raise ValueError("Out of range")
+        return value
+
+    def data_size(self):
+        return 4
+
+    def __int__(self):
+        return self.value
+
+    def __bytes__(self):
+        return text_type(self.value).encode('utf-8')
+
+    def __str__(self):
+        return text_type(self.value)
+
+    def __eq__(self, other):
+        return int(self.value) == other
+
+    def __lt__(self, other):
+        return int(self.value) < other
+
+    __hash__ = ASFBaseAttribute.__hash__
+
+
+@ASFBaseAttribute._register
+@swap_to_string
+@total_ordering
+class ASFQWordAttribute(ASFBaseAttribute):
+    """QWORD attribute.
+
+    ::
+
+        ASFQWordAttribute(42)
+    """
+
+    TYPE = 0x0004
+
+    def parse(self, data):
+        return struct.unpack("<Q", data)[0]
+
+    def _render(self):
+        return struct.pack("<Q", self.value)
+
+    def _validate(self, value):
+        value = int(value)
+        if not 0 <= value <= 2 ** 64 - 1:
+            raise ValueError("Out of range")
+        return value
+
+    def data_size(self):
+        return 8
+
+    def __int__(self):
+        return self.value
+
+    def __bytes__(self):
+        return text_type(self.value).encode('utf-8')
+
+    def __str__(self):
+        return text_type(self.value)
+
+    def __eq__(self, other):
+        return int(self.value) == other
+
+    def __lt__(self, other):
+        return int(self.value) < other
+
+    __hash__ = ASFBaseAttribute.__hash__
+
+
+@ASFBaseAttribute._register
+@swap_to_string
+@total_ordering
+class ASFWordAttribute(ASFBaseAttribute):
+    """WORD attribute.
+
+    ::
+
+        ASFWordAttribute(42)
+    """
+
+    TYPE = 0x0005
+
+    def parse(self, data):
+        return struct.unpack("<H", data)[0]
+
+    def _render(self):
+        return struct.pack("<H", self.value)
+
+    def _validate(self, value):
+        value = int(value)
+        if not 0 <= value <= 2 ** 16 - 1:
+            raise ValueError("Out of range")
+        return value
+
+    def data_size(self):
+        return 2
+
+    def __int__(self):
+        return self.value
+
+    def __bytes__(self):
+        return text_type(self.value).encode('utf-8')
+
+    def __str__(self):
+        return text_type(self.value)
+
+    def __eq__(self, other):
+        return int(self.value) == other
+
+    def __lt__(self, other):
+        return int(self.value) < other
+
+    __hash__ = ASFBaseAttribute.__hash__
+
+
+@ASFBaseAttribute._register
+@swap_to_string
+@total_ordering
+class ASFGUIDAttribute(ASFBaseAttribute):
+    """GUID attribute."""
+
+    TYPE = 0x0006
+
+    def parse(self, data):
+        assert isinstance(data, bytes)
+        return data
+
+    def _render(self):
+        assert isinstance(self.value, bytes)
+        return self.value
+
+    def _validate(self, value):
+        if not isinstance(value, bytes):
+            raise TypeError("must be bytes/str: %r" % value)
+        return value
+
+    def data_size(self):
+        return len(self.value)
+
+    def __bytes__(self):
+        return self.value
+
+    def __str__(self):
+        return repr(self.value)
+
+    def __eq__(self, other):
+        return self.value == other
+
+    def __lt__(self, other):
+        return self.value < other
+
+    __hash__ = ASFBaseAttribute.__hash__
+
+
+def ASFValue(value, kind, **kwargs):
+    """Create a tag value of a specific kind.
+
+    ::
+
+        ASFValue(u"My Value", UNICODE)
+
+    :rtype: ASFBaseAttribute
+    :raises TypeError: in case a wrong type was passed
+    :raises ValueError: in case the value can't be be represented as ASFValue.
+    """
+
+    try:
+        attr_type = ASFBaseAttribute._get_type(kind)
+    except KeyError:
+        raise ValueError("Unknown value type %r" % kind)
+    else:
+        return attr_type(value=value, **kwargs)
diff --git a/resources/lib/mutagen/asf/_objects.py b/resources/lib/mutagen/asf/_objects.py
new file mode 100644
index 00000000..ed942679
--- /dev/null
+++ b/resources/lib/mutagen/asf/_objects.py
@@ -0,0 +1,437 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2005-2006  Joe Wreschnig
+# Copyright (C) 2006-2007  Lukas Lalinsky
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+import struct
+
+from mutagen._util import cdata, get_size
+from mutagen._compat import text_type, xrange, izip
+from mutagen._tags import PaddingInfo
+
+from ._util import guid2bytes, bytes2guid, CODECS, ASFError, ASFHeaderError
+from ._attrs import ASFBaseAttribute, ASFUnicodeAttribute
+
+
+class BaseObject(object):
+    """Base ASF object."""
+
+    GUID = None
+    _TYPES = {}
+
+    def __init__(self):
+        self.objects = []
+        self.data = b""
+
+    def parse(self, asf, data):
+        self.data = data
+
+    def render(self, asf):
+        data = self.GUID + struct.pack("<Q", len(self.data) + 24) + self.data
+        return data
+
+    def get_child(self, guid):
+        for obj in self.objects:
+            if obj.GUID == guid:
+                return obj
+        return None
+
+    @classmethod
+    def _register(cls, other):
+        cls._TYPES[other.GUID] = other
+        return other
+
+    @classmethod
+    def _get_object(cls, guid):
+        if guid in cls._TYPES:
+            return cls._TYPES[guid]()
+        else:
+            return UnknownObject(guid)
+
+    def __repr__(self):
+        return "<%s GUID=%s objects=%r>" % (
+            type(self).__name__, bytes2guid(self.GUID), self.objects)
+
+    def pprint(self):
+        l = []
+        l.append("%s(%s)" % (type(self).__name__, bytes2guid(self.GUID)))
+        for o in self.objects:
+            for e in o.pprint().splitlines():
+                l.append("  " + e)
+        return "\n".join(l)
+
+
+class UnknownObject(BaseObject):
+    """Unknown ASF object."""
+
+    def __init__(self, guid):
+        super(UnknownObject, self).__init__()
+        assert isinstance(guid, bytes)
+        self.GUID = guid
+
+
+@BaseObject._register
+class HeaderObject(BaseObject):
+    """ASF header."""
+
+    GUID = guid2bytes("75B22630-668E-11CF-A6D9-00AA0062CE6C")
+
+    @classmethod
+    def parse_full(cls, asf, fileobj):
+        """Raises ASFHeaderError"""
+
+        header = cls()
+
+        size, num_objects = cls.parse_size(fileobj)
+        for i in xrange(num_objects):
+            guid, size = struct.unpack("<16sQ", fileobj.read(24))
+            obj = BaseObject._get_object(guid)
+            data = fileobj.read(size - 24)
+            obj.parse(asf, data)
+            header.objects.append(obj)
+
+        return header
+
+    @classmethod
+    def parse_size(cls, fileobj):
+        """Returns (size, num_objects)
+
+        Raises ASFHeaderError
+        """
+
+        header = fileobj.read(30)
+        if len(header) != 30 or header[:16] != HeaderObject.GUID:
+            raise ASFHeaderError("Not an ASF file.")
+
+        return struct.unpack("<QL", header[16:28])
+
+    def render_full(self, asf, fileobj, available, padding_func):
+        # Render everything except padding
+        num_objects = 0
+        data = bytearray()
+        for obj in self.objects:
+            if obj.GUID == PaddingObject.GUID:
+                continue
+            data += obj.render(asf)
+            num_objects += 1
+
+        # calculate how much space we need at least
+        padding_obj = PaddingObject()
+        header_size = len(HeaderObject.GUID) + 14
+        padding_overhead = len(padding_obj.render(asf))
+        needed_size = len(data) + header_size + padding_overhead
+
+        # ask the user for padding adjustments
+        file_size = get_size(fileobj)
+        content_size = file_size - available
+        assert content_size >= 0
+        info = PaddingInfo(available - needed_size, content_size)
+
+        # add padding
+        padding = info._get_padding(padding_func)
+        padding_obj.parse(asf, b"\x00" * padding)
+        data += padding_obj.render(asf)
+        num_objects += 1
+
+        data = (HeaderObject.GUID +
+                struct.pack("<QL", len(data) + 30, num_objects) +
+                b"\x01\x02" + data)
+
+        return data
+
+    def parse(self, asf, data):
+        raise NotImplementedError
+
+    def render(self, asf):
+        raise NotImplementedError
+
+
+@BaseObject._register
+class ContentDescriptionObject(BaseObject):
+    """Content description."""
+
+    GUID = guid2bytes("75B22633-668E-11CF-A6D9-00AA0062CE6C")
+
+    NAMES = [
+        u"Title",
+        u"Author",
+        u"Copyright",
+        u"Description",
+        u"Rating",
+    ]
+
+    def parse(self, asf, data):
+        super(ContentDescriptionObject, self).parse(asf, data)
+        lengths = struct.unpack("<HHHHH", data[:10])
+        texts = []
+        pos = 10
+        for length in lengths:
+            end = pos + length
+            if length > 0:
+                texts.append(data[pos:end].decode("utf-16-le").strip(u"\x00"))
+            else:
+                texts.append(None)
+            pos = end
+
+        for key, value in izip(self.NAMES, texts):
+            if value is not None:
+                value = ASFUnicodeAttribute(value=value)
+                asf._tags.setdefault(self.GUID, []).append((key, value))
+
+    def render(self, asf):
+        def render_text(name):
+            value = asf.to_content_description.get(name)
+            if value is not None:
+                return text_type(value).encode("utf-16-le") + b"\x00\x00"
+            else:
+                return b""
+
+        texts = [render_text(x) for x in self.NAMES]
+        data = struct.pack("<HHHHH", *map(len, texts)) + b"".join(texts)
+        return self.GUID + struct.pack("<Q", 24 + len(data)) + data
+
+
+@BaseObject._register
+class ExtendedContentDescriptionObject(BaseObject):
+    """Extended content description."""
+
+    GUID = guid2bytes("D2D0A440-E307-11D2-97F0-00A0C95EA850")
+
+    def parse(self, asf, data):
+        super(ExtendedContentDescriptionObject, self).parse(asf, data)
+        num_attributes, = struct.unpack("<H", data[0:2])
+        pos = 2
+        for i in xrange(num_attributes):
+            name_length, = struct.unpack("<H", data[pos:pos + 2])
+            pos += 2
+            name = data[pos:pos + name_length]
+            name = name.decode("utf-16-le").strip("\x00")
+            pos += name_length
+            value_type, value_length = struct.unpack("<HH", data[pos:pos + 4])
+            pos += 4
+            value = data[pos:pos + value_length]
+            pos += value_length
+            attr = ASFBaseAttribute._get_type(value_type)(data=value)
+            asf._tags.setdefault(self.GUID, []).append((name, attr))
+
+    def render(self, asf):
+        attrs = asf.to_extended_content_description.items()
+        data = b"".join(attr.render(name) for (name, attr) in attrs)
+        data = struct.pack("<QH", 26 + len(data), len(attrs)) + data
+        return self.GUID + data
+
+
+@BaseObject._register
+class FilePropertiesObject(BaseObject):
+    """File properties."""
+
+    GUID = guid2bytes("8CABDCA1-A947-11CF-8EE4-00C00C205365")
+
+    def parse(self, asf, data):
+        super(FilePropertiesObject, self).parse(asf, data)
+        length, _, preroll = struct.unpack("<QQQ", data[40:64])
+        # there are files where preroll is larger than length, limit to >= 0
+        asf.info.length = max((length / 10000000.0) - (preroll / 1000.0), 0.0)
+
+
+@BaseObject._register
+class StreamPropertiesObject(BaseObject):
+    """Stream properties."""
+
+    GUID = guid2bytes("B7DC0791-A9B7-11CF-8EE6-00C00C205365")
+
+    def parse(self, asf, data):
+        super(StreamPropertiesObject, self).parse(asf, data)
+        channels, sample_rate, bitrate = struct.unpack("<HII", data[56:66])
+        asf.info.channels = channels
+        asf.info.sample_rate = sample_rate
+        asf.info.bitrate = bitrate * 8
+
+
+@BaseObject._register
+class CodecListObject(BaseObject):
+    """Codec List"""
+
+    GUID = guid2bytes("86D15240-311D-11D0-A3A4-00A0C90348F6")
+
+    def _parse_entry(self, data, offset):
+        """can raise cdata.error"""
+
+        type_, offset = cdata.uint16_le_from(data, offset)
+
+        units, offset = cdata.uint16_le_from(data, offset)
+        # utf-16 code units, not characters..
+        next_offset = offset + units * 2
+        try:
+            name = data[offset:next_offset].decode("utf-16-le").strip("\x00")
+        except UnicodeDecodeError:
+            name = u""
+        offset = next_offset
+
+        units, offset = cdata.uint16_le_from(data, offset)
+        next_offset = offset + units * 2
+        try:
+            desc = data[offset:next_offset].decode("utf-16-le").strip("\x00")
+        except UnicodeDecodeError:
+            desc = u""
+        offset = next_offset
+
+        bytes_, offset = cdata.uint16_le_from(data, offset)
+        next_offset = offset + bytes_
+        codec = u""
+        if bytes_ == 2:
+            codec_id = cdata.uint16_le_from(data, offset)[0]
+            if codec_id in CODECS:
+                codec = CODECS[codec_id]
+        offset = next_offset
+
+        return offset, type_, name, desc, codec
+
+    def parse(self, asf, data):
+        super(CodecListObject, self).parse(asf, data)
+
+        offset = 16
+        count, offset = cdata.uint32_le_from(data, offset)
+        for i in xrange(count):
+            try:
+                offset, type_, name, desc, codec = \
+                    self._parse_entry(data, offset)
+            except cdata.error:
+                raise ASFError("invalid codec entry")
+
+            # go with the first audio entry
+            if type_ == 2:
+                name = name.strip()
+                desc = desc.strip()
+                asf.info.codec_type = codec
+                asf.info.codec_name = name
+                asf.info.codec_description = desc
+                return
+
+
+@BaseObject._register
+class PaddingObject(BaseObject):
+    """Padding object"""
+
+    GUID = guid2bytes("1806D474-CADF-4509-A4BA-9AABCB96AAE8")
+
+
+@BaseObject._register
+class StreamBitratePropertiesObject(BaseObject):
+    """Stream bitrate properties"""
+
+    GUID = guid2bytes("7BF875CE-468D-11D1-8D82-006097C9A2B2")
+
+
+@BaseObject._register
+class ContentEncryptionObject(BaseObject):
+    """Content encryption"""
+
+    GUID = guid2bytes("2211B3FB-BD23-11D2-B4B7-00A0C955FC6E")
+
+
+@BaseObject._register
+class ExtendedContentEncryptionObject(BaseObject):
+    """Extended content encryption"""
+
+    GUID = guid2bytes("298AE614-2622-4C17-B935-DAE07EE9289C")
+
+
+@BaseObject._register
+class HeaderExtensionObject(BaseObject):
+    """Header extension."""
+
+    GUID = guid2bytes("5FBF03B5-A92E-11CF-8EE3-00C00C205365")
+
+    def parse(self, asf, data):
+        super(HeaderExtensionObject, self).parse(asf, data)
+        datasize, = struct.unpack("<I", data[18:22])
+        datapos = 0
+        while datapos < datasize:
+            guid, size = struct.unpack(
+                "<16sQ", data[22 + datapos:22 + datapos + 24])
+            obj = BaseObject._get_object(guid)
+            obj.parse(asf, data[22 + datapos + 24:22 + datapos + size])
+            self.objects.append(obj)
+            datapos += size
+
+    def render(self, asf):
+        data = bytearray()
+        for obj in self.objects:
+            # some files have the padding in the extension header, but we
+            # want to add it at the end of the top level header. Just
+            # skip padding at this level.
+            if obj.GUID == PaddingObject.GUID:
+                continue
+            data += obj.render(asf)
+        return (self.GUID + struct.pack("<Q", 24 + 16 + 6 + len(data)) +
+                b"\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11" +
+                b"\x8E\xE6\x00\xC0\x0C\x20\x53\x65" +
+                b"\x06\x00" + struct.pack("<I", len(data)) + data)
+
+
+@BaseObject._register
+class MetadataObject(BaseObject):
+    """Metadata description."""
+
+    GUID = guid2bytes("C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA")
+
+    def parse(self, asf, data):
+        super(MetadataObject, self).parse(asf, data)
+        num_attributes, = struct.unpack("<H", data[0:2])
+        pos = 2
+        for i in xrange(num_attributes):
+            (reserved, stream, name_length, value_type,
+             value_length) = struct.unpack("<HHHHI", data[pos:pos + 12])
+            pos += 12
+            name = data[pos:pos + name_length]
+            name = name.decode("utf-16-le").strip("\x00")
+            pos += name_length
+            value = data[pos:pos + value_length]
+            pos += value_length
+            args = {'data': value, 'stream': stream}
+            if value_type == 2:
+                args['dword'] = False
+            attr = ASFBaseAttribute._get_type(value_type)(**args)
+            asf._tags.setdefault(self.GUID, []).append((name, attr))
+
+    def render(self, asf):
+        attrs = asf.to_metadata.items()
+        data = b"".join([attr.render_m(name) for (name, attr) in attrs])
+        return (self.GUID + struct.pack("<QH", 26 + len(data), len(attrs)) +
+                data)
+
+
+@BaseObject._register
+class MetadataLibraryObject(BaseObject):
+    """Metadata library description."""
+
+    GUID = guid2bytes("44231C94-9498-49D1-A141-1D134E457054")
+
+    def parse(self, asf, data):
+        super(MetadataLibraryObject, self).parse(asf, data)
+        num_attributes, = struct.unpack("<H", data[0:2])
+        pos = 2
+        for i in xrange(num_attributes):
+            (language, stream, name_length, value_type,
+             value_length) = struct.unpack("<HHHHI", data[pos:pos + 12])
+            pos += 12
+            name = data[pos:pos + name_length]
+            name = name.decode("utf-16-le").strip("\x00")
+            pos += name_length
+            value = data[pos:pos + value_length]
+            pos += value_length
+            args = {'data': value, 'language': language, 'stream': stream}
+            if value_type == 2:
+                args['dword'] = False
+            attr = ASFBaseAttribute._get_type(value_type)(**args)
+            asf._tags.setdefault(self.GUID, []).append((name, attr))
+
+    def render(self, asf):
+        attrs = asf.to_metadata_library
+        data = b"".join([attr.render_ml(name) for (name, attr) in attrs])
+        return (self.GUID + struct.pack("<QH", 26 + len(data), len(attrs)) +
+                data)
diff --git a/resources/lib/mutagen/asf/_util.py b/resources/lib/mutagen/asf/_util.py
new file mode 100644
index 00000000..42154bff
--- /dev/null
+++ b/resources/lib/mutagen/asf/_util.py
@@ -0,0 +1,315 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2005-2006  Joe Wreschnig
+# Copyright (C) 2006-2007  Lukas Lalinsky
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+import struct
+
+from mutagen._util import MutagenError
+
+
+class error(IOError, MutagenError):
+    """Error raised by :mod:`mutagen.asf`"""
+
+
+class ASFError(error):
+    pass
+
+
+class ASFHeaderError(error):
+    pass
+
+
+def guid2bytes(s):
+    """Converts a GUID to the serialized bytes representation"""
+
+    assert isinstance(s, str)
+    assert len(s) == 36
+
+    p = struct.pack
+    return b"".join([
+        p("<IHH", int(s[:8], 16), int(s[9:13], 16), int(s[14:18], 16)),
+        p(">H", int(s[19:23], 16)),
+        p(">Q", int(s[24:], 16))[2:],
+        ])
+
+
+def bytes2guid(s):
+    """Converts a serialized GUID to a text GUID"""
+
+    assert isinstance(s, bytes)
+
+    u = struct.unpack
+    v = []
+    v.extend(u("<IHH", s[:8]))
+    v.extend(u(">HQ", s[8:10] + b"\x00\x00" + s[10:]))
+    return "%08X-%04X-%04X-%04X-%012X" % tuple(v)
+
+
+# Names from http://windows.microsoft.com/en-za/windows7/c00d10d1-[0-9A-F]{1,4}
+CODECS = {
+    0x0000: u"Unknown Wave Format",
+    0x0001: u"Microsoft PCM Format",
+    0x0002: u"Microsoft ADPCM Format",
+    0x0003: u"IEEE Float",
+    0x0004: u"Compaq Computer VSELP",
+    0x0005: u"IBM CVSD",
+    0x0006: u"Microsoft CCITT A-Law",
+    0x0007: u"Microsoft CCITT u-Law",
+    0x0008: u"Microsoft DTS",
+    0x0009: u"Microsoft DRM",
+    0x000A: u"Windows Media Audio 9 Voice",
+    0x000B: u"Windows Media Audio 10 Voice",
+    0x000C: u"OGG Vorbis",
+    0x000D: u"FLAC",
+    0x000E: u"MOT AMR",
+    0x000F: u"Nice Systems IMBE",
+    0x0010: u"OKI ADPCM",
+    0x0011: u"Intel IMA ADPCM",
+    0x0012: u"Videologic MediaSpace ADPCM",
+    0x0013: u"Sierra Semiconductor ADPCM",
+    0x0014: u"Antex Electronics G.723 ADPCM",
+    0x0015: u"DSP Solutions DIGISTD",
+    0x0016: u"DSP Solutions DIGIFIX",
+    0x0017: u"Dialogic OKI ADPCM",
+    0x0018: u"MediaVision ADPCM",
+    0x0019: u"Hewlett-Packard CU codec",
+    0x001A: u"Hewlett-Packard Dynamic Voice",
+    0x0020: u"Yamaha ADPCM",
+    0x0021: u"Speech Compression SONARC",
+    0x0022: u"DSP Group True Speech",
+    0x0023: u"Echo Speech EchoSC1",
+    0x0024: u"Ahead Inc. Audiofile AF36",
+    0x0025: u"Audio Processing Technology APTX",
+    0x0026: u"Ahead Inc. AudioFile AF10",
+    0x0027: u"Aculab Prosody 1612",
+    0x0028: u"Merging Technologies S.A. LRC",
+    0x0030: u"Dolby Labs AC2",
+    0x0031: u"Microsoft GSM 6.10",
+    0x0032: u"Microsoft MSNAudio",
+    0x0033: u"Antex Electronics ADPCME",
+    0x0034: u"Control Resources VQLPC",
+    0x0035: u"DSP Solutions Digireal",
+    0x0036: u"DSP Solutions DigiADPCM",
+    0x0037: u"Control Resources CR10",
+    0x0038: u"Natural MicroSystems VBXADPCM",
+    0x0039: u"Crystal Semiconductor IMA ADPCM",
+    0x003A: u"Echo Speech EchoSC3",
+    0x003B: u"Rockwell ADPCM",
+    0x003C: u"Rockwell DigiTalk",
+    0x003D: u"Xebec Multimedia Solutions",
+    0x0040: u"Antex Electronics G.721 ADPCM",
+    0x0041: u"Antex Electronics G.728 CELP",
+    0x0042: u"Intel G.723",
+    0x0043: u"Intel G.723.1",
+    0x0044: u"Intel G.729 Audio",
+    0x0045: u"Sharp G.726 Audio",
+    0x0050: u"Microsoft MPEG-1",
+    0x0052: u"InSoft RT24",
+    0x0053: u"InSoft PAC",
+    0x0055: u"MP3 - MPEG Layer III",
+    0x0059: u"Lucent G.723",
+    0x0060: u"Cirrus Logic",
+    0x0061: u"ESS Technology ESPCM",
+    0x0062: u"Voxware File-Mode",
+    0x0063: u"Canopus Atrac",
+    0x0064: u"APICOM G.726 ADPCM",
+    0x0065: u"APICOM G.722 ADPCM",
+    0x0066: u"Microsoft DSAT",
+    0x0067: u"Microsoft DSAT Display",
+    0x0069: u"Voxware Byte Aligned",
+    0x0070: u"Voxware AC8",
+    0x0071: u"Voxware AC10",
+    0x0072: u"Voxware AC16",
+    0x0073: u"Voxware AC20",
+    0x0074: u"Voxware RT24 MetaVoice",
+    0x0075: u"Voxware RT29 MetaSound",
+    0x0076: u"Voxware RT29HW",
+    0x0077: u"Voxware VR12",
+    0x0078: u"Voxware VR18",
+    0x0079: u"Voxware TQ40",
+    0x007A: u"Voxware SC3",
+    0x007B: u"Voxware SC3",
+    0x0080: u"Softsound",
+    0x0081: u"Voxware TQ60",
+    0x0082: u"Microsoft MSRT24",
+    0x0083: u"AT&T Labs G.729A",
+    0x0084: u"Motion Pixels MVI MV12",
+    0x0085: u"DataFusion Systems G.726",
+    0x0086: u"DataFusion Systems GSM610",
+    0x0088: u"Iterated Systems ISIAudio",
+    0x0089: u"Onlive",
+    0x008A: u"Multitude FT SX20",
+    0x008B: u"Infocom ITS ACM G.721",
+    0x008C: u"Convedia G.729",
+    0x008D: u"Congruency Audio",
+    0x0091: u"Siemens Business Communications SBC24",
+    0x0092: u"Sonic Foundry Dolby AC3 SPDIF",
+    0x0093: u"MediaSonic G.723",
+    0x0094: u"Aculab Prosody 8KBPS",
+    0x0097: u"ZyXEL ADPCM",
+    0x0098: u"Philips LPCBB",
+    0x0099: u"Studer Professional Audio AG Packed",
+    0x00A0: u"Malden Electronics PHONYTALK",
+    0x00A1: u"Racal Recorder GSM",
+    0x00A2: u"Racal Recorder G720.a",
+    0x00A3: u"Racal Recorder G723.1",
+    0x00A4: u"Racal Recorder Tetra ACELP",
+    0x00B0: u"NEC AAC",
+    0x00FF: u"CoreAAC Audio",
+    0x0100: u"Rhetorex ADPCM",
+    0x0101: u"BeCubed Software IRAT",
+    0x0111: u"Vivo G.723",
+    0x0112: u"Vivo Siren",
+    0x0120: u"Philips CELP",
+    0x0121: u"Philips Grundig",
+    0x0123: u"Digital G.723",
+    0x0125: u"Sanyo ADPCM",
+    0x0130: u"Sipro Lab Telecom ACELP.net",
+    0x0131: u"Sipro Lab Telecom ACELP.4800",
+    0x0132: u"Sipro Lab Telecom ACELP.8V3",
+    0x0133: u"Sipro Lab Telecom ACELP.G.729",
+    0x0134: u"Sipro Lab Telecom ACELP.G.729A",
+    0x0135: u"Sipro Lab Telecom ACELP.KELVIN",
+    0x0136: u"VoiceAge AMR",
+    0x0140: u"Dictaphone G.726 ADPCM",
+    0x0141: u"Dictaphone CELP68",
+    0x0142: u"Dictaphone CELP54",
+    0x0150: u"Qualcomm PUREVOICE",
+    0x0151: u"Qualcomm HALFRATE",
+    0x0155: u"Ring Zero Systems TUBGSM",
+    0x0160: u"Windows Media Audio Standard",
+    0x0161: u"Windows Media Audio 9 Standard",
+    0x0162: u"Windows Media Audio 9 Professional",
+    0x0163: u"Windows Media Audio 9 Lossless",
+    0x0164: u"Windows Media Audio Pro over SPDIF",
+    0x0170: u"Unisys NAP ADPCM",
+    0x0171: u"Unisys NAP ULAW",
+    0x0172: u"Unisys NAP ALAW",
+    0x0173: u"Unisys NAP 16K",
+    0x0174: u"Sycom ACM SYC008",
+    0x0175: u"Sycom ACM SYC701 G725",
+    0x0176: u"Sycom ACM SYC701 CELP54",
+    0x0177: u"Sycom ACM SYC701 CELP68",
+    0x0178: u"Knowledge Adventure ADPCM",
+    0x0180: u"Fraunhofer IIS MPEG-2 AAC",
+    0x0190: u"Digital Theater Systems DTS",
+    0x0200: u"Creative Labs ADPCM",
+    0x0202: u"Creative Labs FastSpeech8",
+    0x0203: u"Creative Labs FastSpeech10",
+    0x0210: u"UHER informatic GmbH ADPCM",
+    0x0215: u"Ulead DV Audio",
+    0x0216: u"Ulead DV Audio",
+    0x0220: u"Quarterdeck",
+    0x0230: u"I-link Worldwide ILINK VC",
+    0x0240: u"Aureal Semiconductor RAW SPORT",
+    0x0249: u"Generic Passthru",
+    0x0250: u"Interactive Products HSX",
+    0x0251: u"Interactive Products RPELP",
+    0x0260: u"Consistent Software CS2",
+    0x0270: u"Sony SCX",
+    0x0271: u"Sony SCY",
+    0x0272: u"Sony ATRAC3",
+    0x0273: u"Sony SPC",
+    0x0280: u"Telum Audio",
+    0x0281: u"Telum IA Audio",
+    0x0285: u"Norcom Voice Systems ADPCM",
+    0x0300: u"Fujitsu TOWNS SND",
+    0x0350: u"Micronas SC4 Speech",
+    0x0351: u"Micronas CELP833",
+    0x0400: u"Brooktree BTV Digital",
+    0x0401: u"Intel Music Coder",
+    0x0402: u"Intel Audio",
+    0x0450: u"QDesign Music",
+    0x0500: u"On2 AVC0 Audio",
+    0x0501: u"On2 AVC1 Audio",
+    0x0680: u"AT&T Labs VME VMPCM",
+    0x0681: u"AT&T Labs TPC",
+    0x08AE: u"ClearJump Lightwave Lossless",
+    0x1000: u"Olivetti GSM",
+    0x1001: u"Olivetti ADPCM",
+    0x1002: u"Olivetti CELP",
+    0x1003: u"Olivetti SBC",
+    0x1004: u"Olivetti OPR",
+    0x1100: u"Lernout & Hauspie",
+    0x1101: u"Lernout & Hauspie CELP",
+    0x1102: u"Lernout & Hauspie SBC8",
+    0x1103: u"Lernout & Hauspie SBC12",
+    0x1104: u"Lernout & Hauspie SBC16",
+    0x1400: u"Norris Communication",
+    0x1401: u"ISIAudio",
+    0x1500: u"AT&T Labs Soundspace Music Compression",
+    0x1600: u"Microsoft MPEG ADTS AAC",
+    0x1601: u"Microsoft MPEG RAW AAC",
+    0x1608: u"Nokia MPEG ADTS AAC",
+    0x1609: u"Nokia MPEG RAW AAC",
+    0x181C: u"VoxWare MetaVoice RT24",
+    0x1971: u"Sonic Foundry Lossless",
+    0x1979: u"Innings Telecom ADPCM",
+    0x1FC4: u"NTCSoft ALF2CD ACM",
+    0x2000: u"Dolby AC3",
+    0x2001: u"DTS",
+    0x4143: u"Divio AAC",
+    0x4201: u"Nokia Adaptive Multi-Rate",
+    0x4243: u"Divio G.726",
+    0x4261: u"ITU-T H.261",
+    0x4263: u"ITU-T H.263",
+    0x4264: u"ITU-T H.264",
+    0x674F: u"Ogg Vorbis Mode 1",
+    0x6750: u"Ogg Vorbis Mode 2",
+    0x6751: u"Ogg Vorbis Mode 3",
+    0x676F: u"Ogg Vorbis Mode 1+",
+    0x6770: u"Ogg Vorbis Mode 2+",
+    0x6771: u"Ogg Vorbis Mode 3+",
+    0x7000: u"3COM NBX Audio",
+    0x706D: u"FAAD AAC Audio",
+    0x77A1: u"True Audio Lossless Audio",
+    0x7A21: u"GSM-AMR CBR 3GPP Audio",
+    0x7A22: u"GSM-AMR VBR 3GPP Audio",
+    0xA100: u"Comverse Infosys G723.1",
+    0xA101: u"Comverse Infosys AVQSBC",
+    0xA102: u"Comverse Infosys SBC",
+    0xA103: u"Symbol Technologies G729a",
+    0xA104: u"VoiceAge AMR WB",
+    0xA105: u"Ingenient Technologies G.726",
+    0xA106: u"ISO/MPEG-4 Advanced Audio Coding (AAC)",
+    0xA107: u"Encore Software Ltd's G.726",
+    0xA108: u"ZOLL Medical Corporation ASAO",
+    0xA109: u"Speex Voice",
+    0xA10A: u"Vianix MASC Speech Compression",
+    0xA10B: u"Windows Media 9 Spectrum Analyzer Output",
+    0xA10C: u"Media Foundation Spectrum Analyzer Output",
+    0xA10D: u"GSM 6.10 (Full-Rate) Speech",
+    0xA10E: u"GSM 6.20 (Half-Rate) Speech",
+    0xA10F: u"GSM 6.60 (Enchanced Full-Rate) Speech",
+    0xA110: u"GSM 6.90 (Adaptive Multi-Rate) Speech",
+    0xA111: u"GSM Adaptive Multi-Rate WideBand Speech",
+    0xA112: u"Polycom G.722",
+    0xA113: u"Polycom G.728",
+    0xA114: u"Polycom G.729a",
+    0xA115: u"Polycom Siren",
+    0xA116: u"Global IP Sound ILBC",
+    0xA117: u"Radio Time Time Shifted Radio",
+    0xA118: u"Nice Systems ACA",
+    0xA119: u"Nice Systems ADPCM",
+    0xA11A: u"Vocord Group ITU-T G.721",
+    0xA11B: u"Vocord Group ITU-T G.726",
+    0xA11C: u"Vocord Group ITU-T G.722.1",
+    0xA11D: u"Vocord Group ITU-T G.728",
+    0xA11E: u"Vocord Group ITU-T G.729",
+    0xA11F: u"Vocord Group ITU-T G.729a",
+    0xA120: u"Vocord Group ITU-T G.723.1",
+    0xA121: u"Vocord Group LBC",
+    0xA122: u"Nice G.728",
+    0xA123: u"France Telecom G.729 ACM Audio",
+    0xA124: u"CODIAN Audio",
+    0xCC12: u"Intel YUV12 Codec",
+    0xCFCC: u"Digital Processing Systems Perception Motion JPEG",
+    0xD261: u"DEC H.261",
+    0xD263: u"DEC H.263",
+    0xFFFE: u"Extensible Wave Format",
+    0xFFFF: u"Unregistered",
+}
diff --git a/resources/lib/mutagen/easyid3.py b/resources/lib/mutagen/easyid3.py
new file mode 100644
index 00000000..f8dd2de0
--- /dev/null
+++ b/resources/lib/mutagen/easyid3.py
@@ -0,0 +1,534 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""Easier access to ID3 tags.
+
+EasyID3 is a wrapper around mutagen.id3.ID3 to make ID3 tags appear
+more like Vorbis or APEv2 tags.
+"""
+
+import mutagen.id3
+
+from ._compat import iteritems, text_type, PY2
+from mutagen import Metadata
+from mutagen._util import DictMixin, dict_match
+from mutagen.id3 import ID3, error, delete, ID3FileType
+
+
+__all__ = ['EasyID3', 'Open', 'delete']
+
+
+class EasyID3KeyError(KeyError, ValueError, error):
+    """Raised when trying to get/set an invalid key.
+
+    Subclasses both KeyError and ValueError for API compatibility,
+    catching KeyError is preferred.
+    """
+
+
+class EasyID3(DictMixin, Metadata):
+    """A file with an ID3 tag.
+
+    Like Vorbis comments, EasyID3 keys are case-insensitive ASCII
+    strings. Only a subset of ID3 frames are supported by default. Use
+    EasyID3.RegisterKey and its wrappers to support more.
+
+    You can also set the GetFallback, SetFallback, and DeleteFallback
+    to generic key getter/setter/deleter functions, which are called
+    if no specific handler is registered for a key. Additionally,
+    ListFallback can be used to supply an arbitrary list of extra
+    keys. These can be set on EasyID3 or on individual instances after
+    creation.
+
+    To use an EasyID3 class with mutagen.mp3.MP3::
+
+        from mutagen.mp3 import EasyMP3 as MP3
+        MP3(filename)
+
+    Because many of the attributes are constructed on the fly, things
+    like the following will not work::
+
+        ezid3["performer"].append("Joe")
+
+    Instead, you must do::
+
+        values = ezid3["performer"]
+        values.append("Joe")
+        ezid3["performer"] = values
+
+    """
+
+    Set = {}
+    Get = {}
+    Delete = {}
+    List = {}
+
+    # For compatibility.
+    valid_keys = Get
+
+    GetFallback = None
+    SetFallback = None
+    DeleteFallback = None
+    ListFallback = None
+
+    @classmethod
+    def RegisterKey(cls, key,
+                    getter=None, setter=None, deleter=None, lister=None):
+        """Register a new key mapping.
+
+        A key mapping is four functions, a getter, setter, deleter,
+        and lister. The key may be either a string or a glob pattern.
+
+        The getter, deleted, and lister receive an ID3 instance and
+        the requested key name. The setter also receives the desired
+        value, which will be a list of strings.
+
+        The getter, setter, and deleter are used to implement __getitem__,
+        __setitem__, and __delitem__.
+
+        The lister is used to implement keys(). It should return a
+        list of keys that are actually in the ID3 instance, provided
+        by its associated getter.
+        """
+        key = key.lower()
+        if getter is not None:
+            cls.Get[key] = getter
+        if setter is not None:
+            cls.Set[key] = setter
+        if deleter is not None:
+            cls.Delete[key] = deleter
+        if lister is not None:
+            cls.List[key] = lister
+
+    @classmethod
+    def RegisterTextKey(cls, key, frameid):
+        """Register a text key.
+
+        If the key you need to register is a simple one-to-one mapping
+        of ID3 frame name to EasyID3 key, then you can use this
+        function::
+
+            EasyID3.RegisterTextKey("title", "TIT2")
+        """
+        def getter(id3, key):
+            return list(id3[frameid])
+
+        def setter(id3, key, value):
+            try:
+                frame = id3[frameid]
+            except KeyError:
+                id3.add(mutagen.id3.Frames[frameid](encoding=3, text=value))
+            else:
+                frame.encoding = 3
+                frame.text = value
+
+        def deleter(id3, key):
+            del(id3[frameid])
+
+        cls.RegisterKey(key, getter, setter, deleter)
+
+    @classmethod
+    def RegisterTXXXKey(cls, key, desc):
+        """Register a user-defined text frame key.
+
+        Some ID3 tags are stored in TXXX frames, which allow a
+        freeform 'description' which acts as a subkey,
+        e.g. TXXX:BARCODE.::
+
+            EasyID3.RegisterTXXXKey('barcode', 'BARCODE').
+        """
+        frameid = "TXXX:" + desc
+
+        def getter(id3, key):
+            return list(id3[frameid])
+
+        def setter(id3, key, value):
+            try:
+                frame = id3[frameid]
+            except KeyError:
+                enc = 0
+                # Store 8859-1 if we can, per MusicBrainz spec.
+                for v in value:
+                    if v and max(v) > u'\x7f':
+                        enc = 3
+                        break
+
+                id3.add(mutagen.id3.TXXX(encoding=enc, text=value, desc=desc))
+            else:
+                frame.text = value
+
+        def deleter(id3, key):
+            del(id3[frameid])
+
+        cls.RegisterKey(key, getter, setter, deleter)
+
+    def __init__(self, filename=None):
+        self.__id3 = ID3()
+        if filename is not None:
+            self.load(filename)
+
+    load = property(lambda s: s.__id3.load,
+                    lambda s, v: setattr(s.__id3, 'load', v))
+
+    def save(self, *args, **kwargs):
+        # ignore v2_version until we support 2.3 here
+        kwargs.pop("v2_version", None)
+        self.__id3.save(*args, **kwargs)
+
+    delete = property(lambda s: s.__id3.delete,
+                      lambda s, v: setattr(s.__id3, 'delete', v))
+
+    filename = property(lambda s: s.__id3.filename,
+                        lambda s, fn: setattr(s.__id3, 'filename', fn))
+
+    size = property(lambda s: s.__id3.size,
+                    lambda s, fn: setattr(s.__id3, 'size', s))
+
+    def __getitem__(self, key):
+        key = key.lower()
+        func = dict_match(self.Get, key, self.GetFallback)
+        if func is not None:
+            return func(self.__id3, key)
+        else:
+            raise EasyID3KeyError("%r is not a valid key" % key)
+
+    def __setitem__(self, key, value):
+        key = key.lower()
+        if PY2:
+            if isinstance(value, basestring):
+                value = [value]
+        else:
+            if isinstance(value, text_type):
+                value = [value]
+        func = dict_match(self.Set, key, self.SetFallback)
+        if func is not None:
+            return func(self.__id3, key, value)
+        else:
+            raise EasyID3KeyError("%r is not a valid key" % key)
+
+    def __delitem__(self, key):
+        key = key.lower()
+        func = dict_match(self.Delete, key, self.DeleteFallback)
+        if func is not None:
+            return func(self.__id3, key)
+        else:
+            raise EasyID3KeyError("%r is not a valid key" % key)
+
+    def keys(self):
+        keys = []
+        for key in self.Get.keys():
+            if key in self.List:
+                keys.extend(self.List[key](self.__id3, key))
+            elif key in self:
+                keys.append(key)
+        if self.ListFallback is not None:
+            keys.extend(self.ListFallback(self.__id3, ""))
+        return keys
+
+    def pprint(self):
+        """Print tag key=value pairs."""
+        strings = []
+        for key in sorted(self.keys()):
+            values = self[key]
+            for value in values:
+                strings.append("%s=%s" % (key, value))
+        return "\n".join(strings)
+
+
+Open = EasyID3
+
+
+def genre_get(id3, key):
+    return id3["TCON"].genres
+
+
+def genre_set(id3, key, value):
+    try:
+        frame = id3["TCON"]
+    except KeyError:
+        id3.add(mutagen.id3.TCON(encoding=3, text=value))
+    else:
+        frame.encoding = 3
+        frame.genres = value
+
+
+def genre_delete(id3, key):
+    del(id3["TCON"])
+
+
+def date_get(id3, key):
+    return [stamp.text for stamp in id3["TDRC"].text]
+
+
+def date_set(id3, key, value):
+    id3.add(mutagen.id3.TDRC(encoding=3, text=value))
+
+
+def date_delete(id3, key):
+    del(id3["TDRC"])
+
+
+def original_date_get(id3, key):
+    return [stamp.text for stamp in id3["TDOR"].text]
+
+
+def original_date_set(id3, key, value):
+    id3.add(mutagen.id3.TDOR(encoding=3, text=value))
+
+
+def original_date_delete(id3, key):
+    del(id3["TDOR"])
+
+
+def performer_get(id3, key):
+    people = []
+    wanted_role = key.split(":", 1)[1]
+    try:
+        mcl = id3["TMCL"]
+    except KeyError:
+        raise KeyError(key)
+    for role, person in mcl.people:
+        if role == wanted_role:
+            people.append(person)
+    if people:
+        return people
+    else:
+        raise KeyError(key)
+
+
+def performer_set(id3, key, value):
+    wanted_role = key.split(":", 1)[1]
+    try:
+        mcl = id3["TMCL"]
+    except KeyError:
+        mcl = mutagen.id3.TMCL(encoding=3, people=[])
+        id3.add(mcl)
+    mcl.encoding = 3
+    people = [p for p in mcl.people if p[0] != wanted_role]
+    for v in value:
+        people.append((wanted_role, v))
+    mcl.people = people
+
+
+def performer_delete(id3, key):
+    wanted_role = key.split(":", 1)[1]
+    try:
+        mcl = id3["TMCL"]
+    except KeyError:
+        raise KeyError(key)
+    people = [p for p in mcl.people if p[0] != wanted_role]
+    if people == mcl.people:
+        raise KeyError(key)
+    elif people:
+        mcl.people = people
+    else:
+        del(id3["TMCL"])
+
+
+def performer_list(id3, key):
+    try:
+        mcl = id3["TMCL"]
+    except KeyError:
+        return []
+    else:
+        return list(set("performer:" + p[0] for p in mcl.people))
+
+
+def musicbrainz_trackid_get(id3, key):
+    return [id3["UFID:http://musicbrainz.org"].data.decode('ascii')]
+
+
+def musicbrainz_trackid_set(id3, key, value):
+    if len(value) != 1:
+        raise ValueError("only one track ID may be set per song")
+    value = value[0].encode('ascii')
+    try:
+        frame = id3["UFID:http://musicbrainz.org"]
+    except KeyError:
+        frame = mutagen.id3.UFID(owner="http://musicbrainz.org", data=value)
+        id3.add(frame)
+    else:
+        frame.data = value
+
+
+def musicbrainz_trackid_delete(id3, key):
+    del(id3["UFID:http://musicbrainz.org"])
+
+
+def website_get(id3, key):
+    urls = [frame.url for frame in id3.getall("WOAR")]
+    if urls:
+        return urls
+    else:
+        raise EasyID3KeyError(key)
+
+
+def website_set(id3, key, value):
+    id3.delall("WOAR")
+    for v in value:
+        id3.add(mutagen.id3.WOAR(url=v))
+
+
+def website_delete(id3, key):
+    id3.delall("WOAR")
+
+
+def gain_get(id3, key):
+    try:
+        frame = id3["RVA2:" + key[11:-5]]
+    except KeyError:
+        raise EasyID3KeyError(key)
+    else:
+        return [u"%+f dB" % frame.gain]
+
+
+def gain_set(id3, key, value):
+    if len(value) != 1:
+        raise ValueError(
+            "there must be exactly one gain value, not %r.", value)
+    gain = float(value[0].split()[0])
+    try:
+        frame = id3["RVA2:" + key[11:-5]]
+    except KeyError:
+        frame = mutagen.id3.RVA2(desc=key[11:-5], gain=0, peak=0, channel=1)
+        id3.add(frame)
+    frame.gain = gain
+
+
+def gain_delete(id3, key):
+    try:
+        frame = id3["RVA2:" + key[11:-5]]
+    except KeyError:
+        pass
+    else:
+        if frame.peak:
+            frame.gain = 0.0
+        else:
+            del(id3["RVA2:" + key[11:-5]])
+
+
+def peak_get(id3, key):
+    try:
+        frame = id3["RVA2:" + key[11:-5]]
+    except KeyError:
+        raise EasyID3KeyError(key)
+    else:
+        return [u"%f" % frame.peak]
+
+
+def peak_set(id3, key, value):
+    if len(value) != 1:
+        raise ValueError(
+            "there must be exactly one peak value, not %r.", value)
+    peak = float(value[0])
+    if peak >= 2 or peak < 0:
+        raise ValueError("peak must be => 0 and < 2.")
+    try:
+        frame = id3["RVA2:" + key[11:-5]]
+    except KeyError:
+        frame = mutagen.id3.RVA2(desc=key[11:-5], gain=0, peak=0, channel=1)
+        id3.add(frame)
+    frame.peak = peak
+
+
+def peak_delete(id3, key):
+    try:
+        frame = id3["RVA2:" + key[11:-5]]
+    except KeyError:
+        pass
+    else:
+        if frame.gain:
+            frame.peak = 0.0
+        else:
+            del(id3["RVA2:" + key[11:-5]])
+
+
+def peakgain_list(id3, key):
+    keys = []
+    for frame in id3.getall("RVA2"):
+        keys.append("replaygain_%s_gain" % frame.desc)
+        keys.append("replaygain_%s_peak" % frame.desc)
+    return keys
+
+for frameid, key in iteritems({
+    "TALB": "album",
+    "TBPM": "bpm",
+    "TCMP": "compilation",  # iTunes extension
+    "TCOM": "composer",
+    "TCOP": "copyright",
+    "TENC": "encodedby",
+    "TEXT": "lyricist",
+    "TLEN": "length",
+    "TMED": "media",
+    "TMOO": "mood",
+    "TIT2": "title",
+    "TIT3": "version",
+    "TPE1": "artist",
+    "TPE2": "performer",
+    "TPE3": "conductor",
+    "TPE4": "arranger",
+    "TPOS": "discnumber",
+    "TPUB": "organization",
+    "TRCK": "tracknumber",
+    "TOLY": "author",
+    "TSO2": "albumartistsort",  # iTunes extension
+    "TSOA": "albumsort",
+    "TSOC": "composersort",  # iTunes extension
+    "TSOP": "artistsort",
+    "TSOT": "titlesort",
+    "TSRC": "isrc",
+    "TSST": "discsubtitle",
+    "TLAN": "language",
+}):
+    EasyID3.RegisterTextKey(key, frameid)
+
+EasyID3.RegisterKey("genre", genre_get, genre_set, genre_delete)
+EasyID3.RegisterKey("date", date_get, date_set, date_delete)
+EasyID3.RegisterKey("originaldate", original_date_get, original_date_set,
+                    original_date_delete)
+EasyID3.RegisterKey(
+    "performer:*", performer_get, performer_set, performer_delete,
+    performer_list)
+EasyID3.RegisterKey("musicbrainz_trackid", musicbrainz_trackid_get,
+                    musicbrainz_trackid_set, musicbrainz_trackid_delete)
+EasyID3.RegisterKey("website", website_get, website_set, website_delete)
+EasyID3.RegisterKey(
+    "replaygain_*_gain", gain_get, gain_set, gain_delete, peakgain_list)
+EasyID3.RegisterKey("replaygain_*_peak", peak_get, peak_set, peak_delete)
+
+# At various times, information for this came from
+# http://musicbrainz.org/docs/specs/metadata_tags.html
+# http://bugs.musicbrainz.org/ticket/1383
+# http://musicbrainz.org/doc/MusicBrainzTag
+for desc, key in iteritems({
+    u"MusicBrainz Artist Id": "musicbrainz_artistid",
+    u"MusicBrainz Album Id": "musicbrainz_albumid",
+    u"MusicBrainz Album Artist Id": "musicbrainz_albumartistid",
+    u"MusicBrainz TRM Id": "musicbrainz_trmid",
+    u"MusicIP PUID": "musicip_puid",
+    u"MusicMagic Fingerprint": "musicip_fingerprint",
+    u"MusicBrainz Album Status": "musicbrainz_albumstatus",
+    u"MusicBrainz Album Type": "musicbrainz_albumtype",
+    u"MusicBrainz Album Release Country": "releasecountry",
+    u"MusicBrainz Disc Id": "musicbrainz_discid",
+    u"ASIN": "asin",
+    u"ALBUMARTISTSORT": "albumartistsort",
+    u"BARCODE": "barcode",
+    u"CATALOGNUMBER": "catalognumber",
+    u"MusicBrainz Release Track Id": "musicbrainz_releasetrackid",
+    u"MusicBrainz Release Group Id": "musicbrainz_releasegroupid",
+    u"MusicBrainz Work Id": "musicbrainz_workid",
+    u"Acoustid Fingerprint": "acoustid_fingerprint",
+    u"Acoustid Id": "acoustid_id",
+}):
+    EasyID3.RegisterTXXXKey(key, desc)
+
+
+class EasyID3FileType(ID3FileType):
+    """Like ID3FileType, but uses EasyID3 for tags."""
+    ID3 = EasyID3
diff --git a/resources/lib/mutagen/easymp4.py b/resources/lib/mutagen/easymp4.py
new file mode 100644
index 00000000..b965f37d
--- /dev/null
+++ b/resources/lib/mutagen/easymp4.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2009  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+from mutagen import Metadata
+from mutagen._util import DictMixin, dict_match
+from mutagen.mp4 import MP4, MP4Tags, error, delete
+from ._compat import PY2, text_type, PY3
+
+
+__all__ = ["EasyMP4Tags", "EasyMP4", "delete", "error"]
+
+
+class EasyMP4KeyError(error, KeyError, ValueError):
+    pass
+
+
+class EasyMP4Tags(DictMixin, Metadata):
+    """A file with MPEG-4 iTunes metadata.
+
+    Like Vorbis comments, EasyMP4Tags keys are case-insensitive ASCII
+    strings, and values are a list of Unicode strings (and these lists
+    are always of length 0 or 1).
+
+    If you need access to the full MP4 metadata feature set, you should use
+    MP4, not EasyMP4.
+    """
+
+    Set = {}
+    Get = {}
+    Delete = {}
+    List = {}
+
+    def __init__(self, *args, **kwargs):
+        self.__mp4 = MP4Tags(*args, **kwargs)
+        self.load = self.__mp4.load
+        self.save = self.__mp4.save
+        self.delete = self.__mp4.delete
+        self._padding = self.__mp4._padding
+
+    filename = property(lambda s: s.__mp4.filename,
+                        lambda s, fn: setattr(s.__mp4, 'filename', fn))
+
+    @classmethod
+    def RegisterKey(cls, key,
+                    getter=None, setter=None, deleter=None, lister=None):
+        """Register a new key mapping.
+
+        A key mapping is four functions, a getter, setter, deleter,
+        and lister. The key may be either a string or a glob pattern.
+
+        The getter, deleted, and lister receive an MP4Tags instance
+        and the requested key name. The setter also receives the
+        desired value, which will be a list of strings.
+
+        The getter, setter, and deleter are used to implement __getitem__,
+        __setitem__, and __delitem__.
+
+        The lister is used to implement keys(). It should return a
+        list of keys that are actually in the MP4 instance, provided
+        by its associated getter.
+        """
+        key = key.lower()
+        if getter is not None:
+            cls.Get[key] = getter
+        if setter is not None:
+            cls.Set[key] = setter
+        if deleter is not None:
+            cls.Delete[key] = deleter
+        if lister is not None:
+            cls.List[key] = lister
+
+    @classmethod
+    def RegisterTextKey(cls, key, atomid):
+        """Register a text key.
+
+        If the key you need to register is a simple one-to-one mapping
+        of MP4 atom name to EasyMP4Tags key, then you can use this
+        function::
+
+            EasyMP4Tags.RegisterTextKey("artist", "\xa9ART")
+        """
+        def getter(tags, key):
+            return tags[atomid]
+
+        def setter(tags, key, value):
+            tags[atomid] = value
+
+        def deleter(tags, key):
+            del(tags[atomid])
+
+        cls.RegisterKey(key, getter, setter, deleter)
+
+    @classmethod
+    def RegisterIntKey(cls, key, atomid, min_value=0, max_value=(2 ** 16) - 1):
+        """Register a scalar integer key.
+        """
+
+        def getter(tags, key):
+            return list(map(text_type, tags[atomid]))
+
+        def setter(tags, key, value):
+            clamp = lambda x: int(min(max(min_value, x), max_value))
+            tags[atomid] = [clamp(v) for v in map(int, value)]
+
+        def deleter(tags, key):
+            del(tags[atomid])
+
+        cls.RegisterKey(key, getter, setter, deleter)
+
+    @classmethod
+    def RegisterIntPairKey(cls, key, atomid, min_value=0,
+                           max_value=(2 ** 16) - 1):
+        def getter(tags, key):
+            ret = []
+            for (track, total) in tags[atomid]:
+                if total:
+                    ret.append(u"%d/%d" % (track, total))
+                else:
+                    ret.append(text_type(track))
+            return ret
+
+        def setter(tags, key, value):
+            clamp = lambda x: int(min(max(min_value, x), max_value))
+            data = []
+            for v in value:
+                try:
+                    tracks, total = v.split("/")
+                    tracks = clamp(int(tracks))
+                    total = clamp(int(total))
+                except (ValueError, TypeError):
+                    tracks = clamp(int(v))
+                    total = min_value
+                data.append((tracks, total))
+            tags[atomid] = data
+
+        def deleter(tags, key):
+            del(tags[atomid])
+
+        cls.RegisterKey(key, getter, setter, deleter)
+
+    @classmethod
+    def RegisterFreeformKey(cls, key, name, mean="com.apple.iTunes"):
+        """Register a text key.
+
+        If the key you need to register is a simple one-to-one mapping
+        of MP4 freeform atom (----) and name to EasyMP4Tags key, then
+        you can use this function::
+
+            EasyMP4Tags.RegisterFreeformKey(
+                "musicbrainz_artistid", "MusicBrainz Artist Id")
+        """
+        atomid = "----:" + mean + ":" + name
+
+        def getter(tags, key):
+            return [s.decode("utf-8", "replace") for s in tags[atomid]]
+
+        def setter(tags, key, value):
+            encoded = []
+            for v in value:
+                if not isinstance(v, text_type):
+                    if PY3:
+                        raise TypeError("%r not str" % v)
+                    v = v.decode("utf-8")
+                encoded.append(v.encode("utf-8"))
+            tags[atomid] = encoded
+
+        def deleter(tags, key):
+            del(tags[atomid])
+
+        cls.RegisterKey(key, getter, setter, deleter)
+
+    def __getitem__(self, key):
+        key = key.lower()
+        func = dict_match(self.Get, key)
+        if func is not None:
+            return func(self.__mp4, key)
+        else:
+            raise EasyMP4KeyError("%r is not a valid key" % key)
+
+    def __setitem__(self, key, value):
+        key = key.lower()
+
+        if PY2:
+            if isinstance(value, basestring):
+                value = [value]
+        else:
+            if isinstance(value, text_type):
+                value = [value]
+
+        func = dict_match(self.Set, key)
+        if func is not None:
+            return func(self.__mp4, key, value)
+        else:
+            raise EasyMP4KeyError("%r is not a valid key" % key)
+
+    def __delitem__(self, key):
+        key = key.lower()
+        func = dict_match(self.Delete, key)
+        if func is not None:
+            return func(self.__mp4, key)
+        else:
+            raise EasyMP4KeyError("%r is not a valid key" % key)
+
+    def keys(self):
+        keys = []
+        for key in self.Get.keys():
+            if key in self.List:
+                keys.extend(self.List[key](self.__mp4, key))
+            elif key in self:
+                keys.append(key)
+        return keys
+
+    def pprint(self):
+        """Print tag key=value pairs."""
+        strings = []
+        for key in sorted(self.keys()):
+            values = self[key]
+            for value in values:
+                strings.append("%s=%s" % (key, value))
+        return "\n".join(strings)
+
+for atomid, key in {
+    '\xa9nam': 'title',
+    '\xa9alb': 'album',
+    '\xa9ART': 'artist',
+    'aART': 'albumartist',
+    '\xa9day': 'date',
+    '\xa9cmt': 'comment',
+    'desc': 'description',
+    '\xa9grp': 'grouping',
+    '\xa9gen': 'genre',
+    'cprt': 'copyright',
+    'soal': 'albumsort',
+    'soaa': 'albumartistsort',
+    'soar': 'artistsort',
+    'sonm': 'titlesort',
+    'soco': 'composersort',
+}.items():
+    EasyMP4Tags.RegisterTextKey(key, atomid)
+
+for name, key in {
+    'MusicBrainz Artist Id': 'musicbrainz_artistid',
+    'MusicBrainz Track Id': 'musicbrainz_trackid',
+    'MusicBrainz Album Id': 'musicbrainz_albumid',
+    'MusicBrainz Album Artist Id': 'musicbrainz_albumartistid',
+    'MusicIP PUID': 'musicip_puid',
+    'MusicBrainz Album Status': 'musicbrainz_albumstatus',
+    'MusicBrainz Album Type': 'musicbrainz_albumtype',
+    'MusicBrainz Release Country': 'releasecountry',
+}.items():
+    EasyMP4Tags.RegisterFreeformKey(key, name)
+
+for name, key in {
+    "tmpo": "bpm",
+}.items():
+    EasyMP4Tags.RegisterIntKey(key, name)
+
+for name, key in {
+    "trkn": "tracknumber",
+    "disk": "discnumber",
+}.items():
+    EasyMP4Tags.RegisterIntPairKey(key, name)
+
+
+class EasyMP4(MP4):
+    """Like :class:`MP4 <mutagen.mp4.MP4>`,
+    but uses :class:`EasyMP4Tags` for tags.
+
+    :ivar info: :class:`MP4Info <mutagen.mp4.MP4Info>`
+    :ivar tags: :class:`EasyMP4Tags`
+    """
+
+    MP4Tags = EasyMP4Tags
+
+    Get = EasyMP4Tags.Get
+    Set = EasyMP4Tags.Set
+    Delete = EasyMP4Tags.Delete
+    List = EasyMP4Tags.List
+    RegisterTextKey = EasyMP4Tags.RegisterTextKey
+    RegisterKey = EasyMP4Tags.RegisterKey
diff --git a/resources/lib/mutagen/flac.py b/resources/lib/mutagen/flac.py
new file mode 100644
index 00000000..e6cd1cf7
--- /dev/null
+++ b/resources/lib/mutagen/flac.py
@@ -0,0 +1,876 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2005  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""Read and write FLAC Vorbis comments and stream information.
+
+Read more about FLAC at http://flac.sourceforge.net.
+
+FLAC supports arbitrary metadata blocks. The two most interesting ones
+are the FLAC stream information block, and the Vorbis comment block;
+these are also the only ones Mutagen can currently read.
+
+This module does not handle Ogg FLAC files.
+
+Based off documentation available at
+http://flac.sourceforge.net/format.html
+"""
+
+__all__ = ["FLAC", "Open", "delete"]
+
+import struct
+from ._vorbis import VCommentDict
+import mutagen
+
+from ._compat import cBytesIO, endswith, chr_, xrange
+from mutagen._util import resize_bytes, MutagenError, get_size
+from mutagen._tags import PaddingInfo
+from mutagen.id3 import BitPaddedInt
+from functools import reduce
+
+
+class error(IOError, MutagenError):
+    pass
+
+
+class FLACNoHeaderError(error):
+    pass
+
+
+class FLACVorbisError(ValueError, error):
+    pass
+
+
+def to_int_be(data):
+    """Convert an arbitrarily-long string to a long using big-endian
+    byte order."""
+    return reduce(lambda a, b: (a << 8) + b, bytearray(data), 0)
+
+
+class StrictFileObject(object):
+    """Wraps a file-like object and raises an exception if the requested
+    amount of data to read isn't returned."""
+
+    def __init__(self, fileobj):
+        self._fileobj = fileobj
+        for m in ["close", "tell", "seek", "write", "name"]:
+            if hasattr(fileobj, m):
+                setattr(self, m, getattr(fileobj, m))
+
+    def read(self, size=-1):
+        data = self._fileobj.read(size)
+        if size >= 0 and len(data) != size:
+            raise error("file said %d bytes, read %d bytes" % (
+                        size, len(data)))
+        return data
+
+    def tryread(self, *args):
+        return self._fileobj.read(*args)
+
+
+class MetadataBlock(object):
+    """A generic block of FLAC metadata.
+
+    This class is extended by specific used as an ancestor for more specific
+    blocks, and also as a container for data blobs of unknown blocks.
+
+    Attributes:
+
+    * data -- raw binary data for this block
+    """
+
+    _distrust_size = False
+    """For block types setting this, we don't trust the size field and
+    use the size of the content instead."""
+
+    _invalid_overflow_size = -1
+    """In case the real size was bigger than what is representable by the
+    24 bit size field, we save the wrong specified size here. This can
+    only be set if _distrust_size is True"""
+
+    _MAX_SIZE = 2 ** 24 - 1
+
+    def __init__(self, data):
+        """Parse the given data string or file-like as a metadata block.
+        The metadata header should not be included."""
+        if data is not None:
+            if not isinstance(data, StrictFileObject):
+                if isinstance(data, bytes):
+                    data = cBytesIO(data)
+                elif not hasattr(data, 'read'):
+                    raise TypeError(
+                        "StreamInfo requires string data or a file-like")
+                data = StrictFileObject(data)
+            self.load(data)
+
+    def load(self, data):
+        self.data = data.read()
+
+    def write(self):
+        return self.data
+
+    @classmethod
+    def _writeblock(cls, block, is_last=False):
+        """Returns the block content + header.
+
+        Raises error.
+        """
+
+        data = bytearray()
+        code = (block.code | 128) if is_last else block.code
+        datum = block.write()
+        size = len(datum)
+        if size > cls._MAX_SIZE:
+            if block._distrust_size and block._invalid_overflow_size != -1:
+                # The original size of this block was (1) wrong and (2)
+                # the real size doesn't allow us to save the file
+                # according to the spec (too big for 24 bit uint). Instead
+                # simply write back the original wrong size.. at least
+                # we don't make the file more "broken" as it is.
+                size = block._invalid_overflow_size
+            else:
+                raise error("block is too long to write")
+        assert not size > cls._MAX_SIZE
+        length = struct.pack(">I", size)[-3:]
+        data.append(code)
+        data += length
+        data += datum
+        return data
+
+    @classmethod
+    def _writeblocks(cls, blocks, available, cont_size, padding_func):
+        """Render metadata block as a byte string."""
+
+        # write everything except padding
+        data = bytearray()
+        for block in blocks:
+            if isinstance(block, Padding):
+                continue
+            data += cls._writeblock(block)
+        blockssize = len(data)
+
+        # take the padding overhead into account. we always add one
+        # to make things simple.
+        padding_block = Padding()
+        blockssize += len(cls._writeblock(padding_block))
+
+        # finally add a padding block
+        info = PaddingInfo(available - blockssize, cont_size)
+        padding_block.length = min(info._get_padding(padding_func),
+                                   cls._MAX_SIZE)
+        data += cls._writeblock(padding_block, is_last=True)
+
+        return data
+
+
+class StreamInfo(MetadataBlock, mutagen.StreamInfo):
+    """FLAC stream information.
+
+    This contains information about the audio data in the FLAC file.
+    Unlike most stream information objects in Mutagen, changes to this
+    one will rewritten to the file when it is saved. Unless you are
+    actually changing the audio stream itself, don't change any
+    attributes of this block.
+
+    Attributes:
+
+    * min_blocksize -- minimum audio block size
+    * max_blocksize -- maximum audio block size
+    * sample_rate -- audio sample rate in Hz
+    * channels -- audio channels (1 for mono, 2 for stereo)
+    * bits_per_sample -- bits per sample
+    * total_samples -- total samples in file
+    * length -- audio length in seconds
+    """
+
+    code = 0
+
+    def __eq__(self, other):
+        try:
+            return (self.min_blocksize == other.min_blocksize and
+                    self.max_blocksize == other.max_blocksize and
+                    self.sample_rate == other.sample_rate and
+                    self.channels == other.channels and
+                    self.bits_per_sample == other.bits_per_sample and
+                    self.total_samples == other.total_samples)
+        except:
+            return False
+
+    __hash__ = MetadataBlock.__hash__
+
+    def load(self, data):
+        self.min_blocksize = int(to_int_be(data.read(2)))
+        self.max_blocksize = int(to_int_be(data.read(2)))
+        self.min_framesize = int(to_int_be(data.read(3)))
+        self.max_framesize = int(to_int_be(data.read(3)))
+        # first 16 bits of sample rate
+        sample_first = to_int_be(data.read(2))
+        # last 4 bits of sample rate, 3 of channels, first 1 of bits/sample
+        sample_channels_bps = to_int_be(data.read(1))
+        # last 4 of bits/sample, 36 of total samples
+        bps_total = to_int_be(data.read(5))
+
+        sample_tail = sample_channels_bps >> 4
+        self.sample_rate = int((sample_first << 4) + sample_tail)
+        if not self.sample_rate:
+            raise error("A sample rate value of 0 is invalid")
+        self.channels = int(((sample_channels_bps >> 1) & 7) + 1)
+        bps_tail = bps_total >> 36
+        bps_head = (sample_channels_bps & 1) << 4
+        self.bits_per_sample = int(bps_head + bps_tail + 1)
+        self.total_samples = bps_total & 0xFFFFFFFFF
+        self.length = self.total_samples / float(self.sample_rate)
+
+        self.md5_signature = to_int_be(data.read(16))
+
+    def write(self):
+        f = cBytesIO()
+        f.write(struct.pack(">I", self.min_blocksize)[-2:])
+        f.write(struct.pack(">I", self.max_blocksize)[-2:])
+        f.write(struct.pack(">I", self.min_framesize)[-3:])
+        f.write(struct.pack(">I", self.max_framesize)[-3:])
+
+        # first 16 bits of sample rate
+        f.write(struct.pack(">I", self.sample_rate >> 4)[-2:])
+        # 4 bits sample, 3 channel, 1 bps
+        byte = (self.sample_rate & 0xF) << 4
+        byte += ((self.channels - 1) & 7) << 1
+        byte += ((self.bits_per_sample - 1) >> 4) & 1
+        f.write(chr_(byte))
+        # 4 bits of bps, 4 of sample count
+        byte = ((self.bits_per_sample - 1) & 0xF) << 4
+        byte += (self.total_samples >> 32) & 0xF
+        f.write(chr_(byte))
+        # last 32 of sample count
+        f.write(struct.pack(">I", self.total_samples & 0xFFFFFFFF))
+        # MD5 signature
+        sig = self.md5_signature
+        f.write(struct.pack(
+            ">4I", (sig >> 96) & 0xFFFFFFFF, (sig >> 64) & 0xFFFFFFFF,
+            (sig >> 32) & 0xFFFFFFFF, sig & 0xFFFFFFFF))
+        return f.getvalue()
+
+    def pprint(self):
+        return u"FLAC, %.2f seconds, %d Hz" % (self.length, self.sample_rate)
+
+
+class SeekPoint(tuple):
+    """A single seek point in a FLAC file.
+
+    Placeholder seek points have first_sample of 0xFFFFFFFFFFFFFFFFL,
+    and byte_offset and num_samples undefined. Seek points must be
+    sorted in ascending order by first_sample number. Seek points must
+    be unique by first_sample number, except for placeholder
+    points. Placeholder points must occur last in the table and there
+    may be any number of them.
+
+    Attributes:
+
+    * first_sample -- sample number of first sample in the target frame
+    * byte_offset -- offset from first frame to target frame
+    * num_samples -- number of samples in target frame
+    """
+
+    def __new__(cls, first_sample, byte_offset, num_samples):
+        return super(cls, SeekPoint).__new__(
+            cls, (first_sample, byte_offset, num_samples))
+
+    first_sample = property(lambda self: self[0])
+    byte_offset = property(lambda self: self[1])
+    num_samples = property(lambda self: self[2])
+
+
+class SeekTable(MetadataBlock):
+    """Read and write FLAC seek tables.
+
+    Attributes:
+
+    * seekpoints -- list of SeekPoint objects
+    """
+
+    __SEEKPOINT_FORMAT = '>QQH'
+    __SEEKPOINT_SIZE = struct.calcsize(__SEEKPOINT_FORMAT)
+
+    code = 3
+
+    def __init__(self, data):
+        self.seekpoints = []
+        super(SeekTable, self).__init__(data)
+
+    def __eq__(self, other):
+        try:
+            return (self.seekpoints == other.seekpoints)
+        except (AttributeError, TypeError):
+            return False
+
+    __hash__ = MetadataBlock.__hash__
+
+    def load(self, data):
+        self.seekpoints = []
+        sp = data.tryread(self.__SEEKPOINT_SIZE)
+        while len(sp) == self.__SEEKPOINT_SIZE:
+            self.seekpoints.append(SeekPoint(
+                *struct.unpack(self.__SEEKPOINT_FORMAT, sp)))
+            sp = data.tryread(self.__SEEKPOINT_SIZE)
+
+    def write(self):
+        f = cBytesIO()
+        for seekpoint in self.seekpoints:
+            packed = struct.pack(
+                self.__SEEKPOINT_FORMAT,
+                seekpoint.first_sample, seekpoint.byte_offset,
+                seekpoint.num_samples)
+            f.write(packed)
+        return f.getvalue()
+
+    def __repr__(self):
+        return "<%s seekpoints=%r>" % (type(self).__name__, self.seekpoints)
+
+
+class VCFLACDict(VCommentDict):
+    """Read and write FLAC Vorbis comments.
+
+    FLACs don't use the framing bit at the end of the comment block.
+    So this extends VCommentDict to not use the framing bit.
+    """
+
+    code = 4
+    _distrust_size = True
+
+    def load(self, data, errors='replace', framing=False):
+        super(VCFLACDict, self).load(data, errors=errors, framing=framing)
+
+    def write(self, framing=False):
+        return super(VCFLACDict, self).write(framing=framing)
+
+
+class CueSheetTrackIndex(tuple):
+    """Index for a track in a cuesheet.
+
+    For CD-DA, an index_number of 0 corresponds to the track
+    pre-gap. The first index in a track must have a number of 0 or 1,
+    and subsequently, index_numbers must increase by 1. Index_numbers
+    must be unique within a track. And index_offset must be evenly
+    divisible by 588 samples.
+
+    Attributes:
+
+    * index_number -- index point number
+    * index_offset -- offset in samples from track start
+    """
+
+    def __new__(cls, index_number, index_offset):
+        return super(cls, CueSheetTrackIndex).__new__(
+            cls, (index_number, index_offset))
+
+    index_number = property(lambda self: self[0])
+    index_offset = property(lambda self: self[1])
+
+
+class CueSheetTrack(object):
+    """A track in a cuesheet.
+
+    For CD-DA, track_numbers must be 1-99, or 170 for the
+    lead-out. Track_numbers must be unique within a cue sheet. There
+    must be atleast one index in every track except the lead-out track
+    which must have none.
+
+    Attributes:
+
+    * track_number -- track number
+    * start_offset -- track offset in samples from start of FLAC stream
+    * isrc -- ISRC code
+    * type -- 0 for audio, 1 for digital data
+    * pre_emphasis -- true if the track is recorded with pre-emphasis
+    * indexes -- list of CueSheetTrackIndex objects
+    """
+
+    def __init__(self, track_number, start_offset, isrc='', type_=0,
+                 pre_emphasis=False):
+        self.track_number = track_number
+        self.start_offset = start_offset
+        self.isrc = isrc
+        self.type = type_
+        self.pre_emphasis = pre_emphasis
+        self.indexes = []
+
+    def __eq__(self, other):
+        try:
+            return (self.track_number == other.track_number and
+                    self.start_offset == other.start_offset and
+                    self.isrc == other.isrc and
+                    self.type == other.type and
+                    self.pre_emphasis == other.pre_emphasis and
+                    self.indexes == other.indexes)
+        except (AttributeError, TypeError):
+            return False
+
+    __hash__ = object.__hash__
+
+    def __repr__(self):
+        return (("<%s number=%r, offset=%d, isrc=%r, type=%r, "
+                "pre_emphasis=%r, indexes=%r)>") %
+                (type(self).__name__, self.track_number, self.start_offset,
+                 self.isrc, self.type, self.pre_emphasis, self.indexes))
+
+
+class CueSheet(MetadataBlock):
+    """Read and write FLAC embedded cue sheets.
+
+    Number of tracks should be from 1 to 100. There should always be
+    exactly one lead-out track and that track must be the last track
+    in the cue sheet.
+
+    Attributes:
+
+    * media_catalog_number -- media catalog number in ASCII
+    * lead_in_samples -- number of lead-in samples
+    * compact_disc -- true if the cuesheet corresponds to a compact disc
+    * tracks -- list of CueSheetTrack objects
+    * lead_out -- lead-out as CueSheetTrack or None if lead-out was not found
+    """
+
+    __CUESHEET_FORMAT = '>128sQB258xB'
+    __CUESHEET_SIZE = struct.calcsize(__CUESHEET_FORMAT)
+    __CUESHEET_TRACK_FORMAT = '>QB12sB13xB'
+    __CUESHEET_TRACK_SIZE = struct.calcsize(__CUESHEET_TRACK_FORMAT)
+    __CUESHEET_TRACKINDEX_FORMAT = '>QB3x'
+    __CUESHEET_TRACKINDEX_SIZE = struct.calcsize(__CUESHEET_TRACKINDEX_FORMAT)
+
+    code = 5
+
+    media_catalog_number = b''
+    lead_in_samples = 88200
+    compact_disc = True
+
+    def __init__(self, data):
+        self.tracks = []
+        super(CueSheet, self).__init__(data)
+
+    def __eq__(self, other):
+        try:
+            return (self.media_catalog_number == other.media_catalog_number and
+                    self.lead_in_samples == other.lead_in_samples and
+                    self.compact_disc == other.compact_disc and
+                    self.tracks == other.tracks)
+        except (AttributeError, TypeError):
+            return False
+
+    __hash__ = MetadataBlock.__hash__
+
+    def load(self, data):
+        header = data.read(self.__CUESHEET_SIZE)
+        media_catalog_number, lead_in_samples, flags, num_tracks = \
+            struct.unpack(self.__CUESHEET_FORMAT, header)
+        self.media_catalog_number = media_catalog_number.rstrip(b'\0')
+        self.lead_in_samples = lead_in_samples
+        self.compact_disc = bool(flags & 0x80)
+        self.tracks = []
+        for i in xrange(num_tracks):
+            track = data.read(self.__CUESHEET_TRACK_SIZE)
+            start_offset, track_number, isrc_padded, flags, num_indexes = \
+                struct.unpack(self.__CUESHEET_TRACK_FORMAT, track)
+            isrc = isrc_padded.rstrip(b'\0')
+            type_ = (flags & 0x80) >> 7
+            pre_emphasis = bool(flags & 0x40)
+            val = CueSheetTrack(
+                track_number, start_offset, isrc, type_, pre_emphasis)
+            for j in xrange(num_indexes):
+                index = data.read(self.__CUESHEET_TRACKINDEX_SIZE)
+                index_offset, index_number = struct.unpack(
+                    self.__CUESHEET_TRACKINDEX_FORMAT, index)
+                val.indexes.append(
+                    CueSheetTrackIndex(index_number, index_offset))
+            self.tracks.append(val)
+
+    def write(self):
+        f = cBytesIO()
+        flags = 0
+        if self.compact_disc:
+            flags |= 0x80
+        packed = struct.pack(
+            self.__CUESHEET_FORMAT, self.media_catalog_number,
+            self.lead_in_samples, flags, len(self.tracks))
+        f.write(packed)
+        for track in self.tracks:
+            track_flags = 0
+            track_flags |= (track.type & 1) << 7
+            if track.pre_emphasis:
+                track_flags |= 0x40
+            track_packed = struct.pack(
+                self.__CUESHEET_TRACK_FORMAT, track.start_offset,
+                track.track_number, track.isrc, track_flags,
+                len(track.indexes))
+            f.write(track_packed)
+            for index in track.indexes:
+                index_packed = struct.pack(
+                    self.__CUESHEET_TRACKINDEX_FORMAT,
+                    index.index_offset, index.index_number)
+                f.write(index_packed)
+        return f.getvalue()
+
+    def __repr__(self):
+        return (("<%s media_catalog_number=%r, lead_in=%r, compact_disc=%r, "
+                 "tracks=%r>") %
+                (type(self).__name__, self.media_catalog_number,
+                 self.lead_in_samples, self.compact_disc, self.tracks))
+
+
+class Picture(MetadataBlock):
+    """Read and write FLAC embed pictures.
+
+    Attributes:
+
+    * type -- picture type (same as types for ID3 APIC frames)
+    * mime -- MIME type of the picture
+    * desc -- picture's description
+    * width -- width in pixels
+    * height -- height in pixels
+    * depth -- color depth in bits-per-pixel
+    * colors -- number of colors for indexed palettes (like GIF),
+      0 for non-indexed
+    * data -- picture data
+
+    To create a picture from file (in order to add to a FLAC file),
+    instantiate this object without passing anything to the constructor and
+    then set the properties manually::
+
+        p = Picture()
+
+        with open("Folder.jpg", "rb") as f:
+            pic.data = f.read()
+
+        pic.type = id3.PictureType.COVER_FRONT
+        pic.mime = u"image/jpeg"
+        pic.width = 500
+        pic.height = 500
+        pic.depth = 16 # color depth
+    """
+
+    code = 6
+    _distrust_size = True
+
+    def __init__(self, data=None):
+        self.type = 0
+        self.mime = u''
+        self.desc = u''
+        self.width = 0
+        self.height = 0
+        self.depth = 0
+        self.colors = 0
+        self.data = b''
+        super(Picture, self).__init__(data)
+
+    def __eq__(self, other):
+        try:
+            return (self.type == other.type and
+                    self.mime == other.mime and
+                    self.desc == other.desc and
+                    self.width == other.width and
+                    self.height == other.height and
+                    self.depth == other.depth and
+                    self.colors == other.colors and
+                    self.data == other.data)
+        except (AttributeError, TypeError):
+            return False
+
+    __hash__ = MetadataBlock.__hash__
+
+    def load(self, data):
+        self.type, length = struct.unpack('>2I', data.read(8))
+        self.mime = data.read(length).decode('UTF-8', 'replace')
+        length, = struct.unpack('>I', data.read(4))
+        self.desc = data.read(length).decode('UTF-8', 'replace')
+        (self.width, self.height, self.depth,
+         self.colors, length) = struct.unpack('>5I', data.read(20))
+        self.data = data.read(length)
+
+    def write(self):
+        f = cBytesIO()
+        mime = self.mime.encode('UTF-8')
+        f.write(struct.pack('>2I', self.type, len(mime)))
+        f.write(mime)
+        desc = self.desc.encode('UTF-8')
+        f.write(struct.pack('>I', len(desc)))
+        f.write(desc)
+        f.write(struct.pack('>5I', self.width, self.height, self.depth,
+                            self.colors, len(self.data)))
+        f.write(self.data)
+        return f.getvalue()
+
+    def __repr__(self):
+        return "<%s '%s' (%d bytes)>" % (type(self).__name__, self.mime,
+                                         len(self.data))
+
+
+class Padding(MetadataBlock):
+    """Empty padding space for metadata blocks.
+
+    To avoid rewriting the entire FLAC file when editing comments,
+    metadata is often padded. Padding should occur at the end, and no
+    more than one padding block should be in any FLAC file.
+    """
+
+    code = 1
+
+    def __init__(self, data=b""):
+        super(Padding, self).__init__(data)
+
+    def load(self, data):
+        self.length = len(data.read())
+
+    def write(self):
+        try:
+            return b"\x00" * self.length
+        # On some 64 bit platforms this won't generate a MemoryError
+        # or OverflowError since you might have enough RAM, but it
+        # still generates a ValueError. On other 64 bit platforms,
+        # this will still succeed for extremely large values.
+        # Those should never happen in the real world, and if they
+        # do, writeblocks will catch it.
+        except (OverflowError, ValueError, MemoryError):
+            raise error("cannot write %d bytes" % self.length)
+
+    def __eq__(self, other):
+        return isinstance(other, Padding) and self.length == other.length
+
+    __hash__ = MetadataBlock.__hash__
+
+    def __repr__(self):
+        return "<%s (%d bytes)>" % (type(self).__name__, self.length)
+
+
+class FLAC(mutagen.FileType):
+    """A FLAC audio file.
+
+    Attributes:
+
+    * cuesheet -- CueSheet object, if any
+    * seektable -- SeekTable object, if any
+    * pictures -- list of embedded pictures
+    """
+
+    _mimes = ["audio/x-flac", "application/x-flac"]
+
+    info = None
+    """A `StreamInfo`"""
+
+    tags = None
+    """A `VCommentDict`"""
+
+    METADATA_BLOCKS = [StreamInfo, Padding, None, SeekTable, VCFLACDict,
+                       CueSheet, Picture]
+    """Known metadata block types, indexed by ID."""
+
+    @staticmethod
+    def score(filename, fileobj, header_data):
+        return (header_data.startswith(b"fLaC") +
+                endswith(filename.lower(), ".flac") * 3)
+
+    def __read_metadata_block(self, fileobj):
+        byte = ord(fileobj.read(1))
+        size = to_int_be(fileobj.read(3))
+        code = byte & 0x7F
+        last_block = bool(byte & 0x80)
+
+        try:
+            block_type = self.METADATA_BLOCKS[code] or MetadataBlock
+        except IndexError:
+            block_type = MetadataBlock
+
+        if block_type._distrust_size:
+            # Some jackass is writing broken Metadata block length
+            # for Vorbis comment blocks, and the FLAC reference
+            # implementaton can parse them (mostly by accident),
+            # so we have to too.  Instead of parsing the size
+            # given, parse an actual Vorbis comment, leaving
+            # fileobj in the right position.
+            # http://code.google.com/p/mutagen/issues/detail?id=52
+            # ..same for the Picture block:
+            # http://code.google.com/p/mutagen/issues/detail?id=106
+            start = fileobj.tell()
+            block = block_type(fileobj)
+            real_size = fileobj.tell() - start
+            if real_size > MetadataBlock._MAX_SIZE:
+                block._invalid_overflow_size = size
+        else:
+            data = fileobj.read(size)
+            block = block_type(data)
+        block.code = code
+
+        if block.code == VCFLACDict.code:
+            if self.tags is None:
+                self.tags = block
+            else:
+                raise FLACVorbisError("> 1 Vorbis comment block found")
+        elif block.code == CueSheet.code:
+            if self.cuesheet is None:
+                self.cuesheet = block
+            else:
+                raise error("> 1 CueSheet block found")
+        elif block.code == SeekTable.code:
+            if self.seektable is None:
+                self.seektable = block
+            else:
+                raise error("> 1 SeekTable block found")
+        self.metadata_blocks.append(block)
+        return not last_block
+
+    def add_tags(self):
+        """Add a Vorbis comment block to the file."""
+        if self.tags is None:
+            self.tags = VCFLACDict()
+            self.metadata_blocks.append(self.tags)
+        else:
+            raise FLACVorbisError("a Vorbis comment already exists")
+
+    add_vorbiscomment = add_tags
+
+    def delete(self, filename=None):
+        """Remove Vorbis comments from a file.
+
+        If no filename is given, the one most recently loaded is used.
+        """
+        if filename is None:
+            filename = self.filename
+
+        if self.tags is not None:
+            self.metadata_blocks.remove(self.tags)
+            self.save(padding=lambda x: 0)
+            self.metadata_blocks.append(self.tags)
+            self.tags.clear()
+
+    vc = property(lambda s: s.tags, doc="Alias for tags; don't use this.")
+
+    def load(self, filename):
+        """Load file information from a filename."""
+
+        self.metadata_blocks = []
+        self.tags = None
+        self.cuesheet = None
+        self.seektable = None
+        self.filename = filename
+        fileobj = StrictFileObject(open(filename, "rb"))
+        try:
+            self.__check_header(fileobj)
+            while self.__read_metadata_block(fileobj):
+                pass
+        finally:
+            fileobj.close()
+
+        try:
+            self.metadata_blocks[0].length
+        except (AttributeError, IndexError):
+            raise FLACNoHeaderError("Stream info block not found")
+
+    @property
+    def info(self):
+        return self.metadata_blocks[0]
+
+    def add_picture(self, picture):
+        """Add a new picture to the file."""
+        self.metadata_blocks.append(picture)
+
+    def clear_pictures(self):
+        """Delete all pictures from the file."""
+
+        blocks = [b for b in self.metadata_blocks if b.code != Picture.code]
+        self.metadata_blocks = blocks
+
+    @property
+    def pictures(self):
+        """List of embedded pictures"""
+
+        return [b for b in self.metadata_blocks if b.code == Picture.code]
+
+    def save(self, filename=None, deleteid3=False, padding=None):
+        """Save metadata blocks to a file.
+
+        If no filename is given, the one most recently loaded is used.
+        """
+
+        if filename is None:
+            filename = self.filename
+
+        with open(filename, 'rb+') as f:
+            header = self.__check_header(f)
+            audio_offset = self.__find_audio_offset(f)
+            # "fLaC" and maybe ID3
+            available = audio_offset - header
+
+            # Delete ID3v2
+            if deleteid3 and header > 4:
+                available += header - 4
+                header = 4
+
+            content_size = get_size(f) - audio_offset
+            assert content_size >= 0
+            data = MetadataBlock._writeblocks(
+                self.metadata_blocks, available, content_size, padding)
+            data_size = len(data)
+
+            resize_bytes(f, available, data_size, header)
+            f.seek(header - 4)
+            f.write(b"fLaC")
+            f.write(data)
+
+            # Delete ID3v1
+            if deleteid3:
+                try:
+                    f.seek(-128, 2)
+                except IOError:
+                    pass
+                else:
+                    if f.read(3) == b"TAG":
+                        f.seek(-128, 2)
+                        f.truncate()
+
+    def __find_audio_offset(self, fileobj):
+        byte = 0x00
+        while not (byte & 0x80):
+            byte = ord(fileobj.read(1))
+            size = to_int_be(fileobj.read(3))
+            try:
+                block_type = self.METADATA_BLOCKS[byte & 0x7F]
+            except IndexError:
+                block_type = None
+
+            if block_type and block_type._distrust_size:
+                # See comments in read_metadata_block; the size can't
+                # be trusted for Vorbis comment blocks and Picture block
+                block_type(fileobj)
+            else:
+                fileobj.read(size)
+        return fileobj.tell()
+
+    def __check_header(self, fileobj):
+        """Returns the offset of the flac block start
+        (skipping id3 tags if found). The passed fileobj will be advanced to
+        that offset as well.
+        """
+
+        size = 4
+        header = fileobj.read(4)
+        if header != b"fLaC":
+            size = None
+            if header[:3] == b"ID3":
+                size = 14 + BitPaddedInt(fileobj.read(6)[2:])
+                fileobj.seek(size - 4)
+                if fileobj.read(4) != b"fLaC":
+                    size = None
+        if size is None:
+            raise FLACNoHeaderError(
+                "%r is not a valid FLAC file" % fileobj.name)
+        return size
+
+
+Open = FLAC
+
+
+def delete(filename):
+    """Remove tags from a file."""
+    FLAC(filename).delete()
diff --git a/resources/lib/mutagen/id3/__init__.py b/resources/lib/mutagen/id3/__init__.py
new file mode 100644
index 00000000..9aef865b
--- /dev/null
+++ b/resources/lib/mutagen/id3/__init__.py
@@ -0,0 +1,1093 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2005  Michael Urman
+#               2006  Lukas Lalinsky
+#               2013  Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""ID3v2 reading and writing.
+
+This is based off of the following references:
+
+* http://id3.org/id3v2.4.0-structure
+* http://id3.org/id3v2.4.0-frames
+* http://id3.org/id3v2.3.0
+* http://id3.org/id3v2-00
+* http://id3.org/ID3v1
+
+Its largest deviation from the above (versions 2.3 and 2.2) is that it
+will not interpret the / characters as a separator, and will almost
+always accept null separators to generate multi-valued text frames.
+
+Because ID3 frame structure differs between frame types, each frame is
+implemented as a different class (e.g. TIT2 as mutagen.id3.TIT2). Each
+frame's documentation contains a list of its attributes.
+
+Since this file's documentation is a little unwieldy, you are probably
+interested in the :class:`ID3` class to start with.
+"""
+
+__all__ = ['ID3', 'ID3FileType', 'Frames', 'Open', 'delete']
+
+import struct
+import errno
+
+from struct import unpack, pack, error as StructError
+
+import mutagen
+from mutagen._util import insert_bytes, delete_bytes, DictProxy, enum
+from mutagen._tags import PaddingInfo
+from .._compat import chr_, PY3
+
+from ._util import *
+from ._frames import *
+from ._specs import *
+
+
+@enum
+class ID3v1SaveOptions(object):
+
+    REMOVE = 0
+    """ID3v1 tags will be removed"""
+
+    UPDATE = 1
+    """ID3v1 tags will be updated but not added"""
+
+    CREATE = 2
+    """ID3v1 tags will be created and/or updated"""
+
+
+def _fullread(fileobj, size):
+    """Read a certain number of bytes from the source file.
+
+    Raises ValueError on invalid size input or EOFError/IOError.
+    """
+
+    if size < 0:
+        raise ValueError('Requested bytes (%s) less than zero' % size)
+    data = fileobj.read(size)
+    if len(data) != size:
+        raise EOFError("Not enough data to read")
+    return data
+
+
+class ID3Header(object):
+
+    _V24 = (2, 4, 0)
+    _V23 = (2, 3, 0)
+    _V22 = (2, 2, 0)
+    _V11 = (1, 1)
+
+    f_unsynch = property(lambda s: bool(s._flags & 0x80))
+    f_extended = property(lambda s: bool(s._flags & 0x40))
+    f_experimental = property(lambda s: bool(s._flags & 0x20))
+    f_footer = property(lambda s: bool(s._flags & 0x10))
+
+    def __init__(self, fileobj=None):
+        """Raises ID3NoHeaderError, ID3UnsupportedVersionError or error"""
+
+        if fileobj is None:
+            # for testing
+            self._flags = 0
+            return
+
+        fn = getattr(fileobj, "name", "<unknown>")
+        try:
+            data = _fullread(fileobj, 10)
+        except EOFError:
+            raise ID3NoHeaderError("%s: too small" % fn)
+
+        id3, vmaj, vrev, flags, size = unpack('>3sBBB4s', data)
+        self._flags = flags
+        self.size = BitPaddedInt(size) + 10
+        self.version = (2, vmaj, vrev)
+
+        if id3 != b'ID3':
+            raise ID3NoHeaderError("%r doesn't start with an ID3 tag" % fn)
+
+        if vmaj not in [2, 3, 4]:
+            raise ID3UnsupportedVersionError("%r ID3v2.%d not supported"
+                                             % (fn, vmaj))
+
+        if not BitPaddedInt.has_valid_padding(size):
+            raise error("Header size not synchsafe")
+
+        if (self.version >= self._V24) and (flags & 0x0f):
+            raise error(
+                "%r has invalid flags %#02x" % (fn, flags))
+        elif (self._V23 <= self.version < self._V24) and (flags & 0x1f):
+            raise error(
+                "%r has invalid flags %#02x" % (fn, flags))
+
+        if self.f_extended:
+            try:
+                extsize_data = _fullread(fileobj, 4)
+            except EOFError:
+                raise error("%s: too small" % fn)
+
+            if PY3:
+                frame_id = extsize_data.decode("ascii", "replace")
+            else:
+                frame_id = extsize_data
+
+            if frame_id in Frames:
+                # Some tagger sets the extended header flag but
+                # doesn't write an extended header; in this case, the
+                # ID3 data follows immediately. Since no extended
+                # header is going to be long enough to actually match
+                # a frame, and if it's *not* a frame we're going to be
+                # completely lost anyway, this seems to be the most
+                # correct check.
+                # http://code.google.com/p/quodlibet/issues/detail?id=126
+                self._flags ^= 0x40
+                extsize = 0
+                fileobj.seek(-4, 1)
+            elif self.version >= self._V24:
+                # "Where the 'Extended header size' is the size of the whole
+                # extended header, stored as a 32 bit synchsafe integer."
+                extsize = BitPaddedInt(extsize_data) - 4
+                if not BitPaddedInt.has_valid_padding(extsize_data):
+                    raise error(
+                        "Extended header size not synchsafe")
+            else:
+                # "Where the 'Extended header size', currently 6 or 10 bytes,
+                # excludes itself."
+                extsize = unpack('>L', extsize_data)[0]
+
+            try:
+                self._extdata = _fullread(fileobj, extsize)
+            except EOFError:
+                raise error("%s: too small" % fn)
+
+
+class ID3(DictProxy, mutagen.Metadata):
+    """A file with an ID3v2 tag.
+
+    Attributes:
+
+    * version -- ID3 tag version as a tuple
+    * unknown_frames -- raw frame data of any unknown frames found
+    * size -- the total size of the ID3 tag, including the header
+    """
+
+    __module__ = "mutagen.id3"
+
+    PEDANTIC = True
+    """Deprecated. Doesn't have any effect"""
+
+    filename = None
+
+    def __init__(self, *args, **kwargs):
+        self.unknown_frames = []
+        self.__unknown_version = None
+        self._header = None
+        self._version = (2, 4, 0)
+        super(ID3, self).__init__(*args, **kwargs)
+
+    @property
+    def version(self):
+        """ID3 tag version as a tuple (of the loaded file)"""
+
+        if self._header is not None:
+            return self._header.version
+        return self._version
+
+    @version.setter
+    def version(self, value):
+        self._version = value
+
+    @property
+    def f_unsynch(self):
+        if self._header is not None:
+            return self._header.f_unsynch
+        return False
+
+    @property
+    def f_extended(self):
+        if self._header is not None:
+            return self._header.f_extended
+        return False
+
+    @property
+    def size(self):
+        if self._header is not None:
+            return self._header.size
+        return 0
+
+    def _pre_load_header(self, fileobj):
+        # XXX: for aiff to adjust the offset..
+        pass
+
+    def load(self, filename, known_frames=None, translate=True, v2_version=4):
+        """Load tags from a filename.
+
+        Keyword arguments:
+
+        * filename -- filename to load tag data from
+        * known_frames -- dict mapping frame IDs to Frame objects
+        * translate -- Update all tags to ID3v2.3/4 internally. If you
+                       intend to save, this must be true or you have to
+                       call update_to_v23() / update_to_v24() manually.
+        * v2_version -- if update_to_v23 or update_to_v24 get called (3 or 4)
+
+        Example of loading a custom frame::
+
+            my_frames = dict(mutagen.id3.Frames)
+            class XMYF(Frame): ...
+            my_frames["XMYF"] = XMYF
+            mutagen.id3.ID3(filename, known_frames=my_frames)
+        """
+
+        if v2_version not in (3, 4):
+            raise ValueError("Only 3 and 4 possible for v2_version")
+
+        self.filename = filename
+        self.unknown_frames = []
+        self.__known_frames = known_frames
+        self._header = None
+        self._padding = 0  # for testing
+
+        with open(filename, 'rb') as fileobj:
+            self._pre_load_header(fileobj)
+
+            try:
+                self._header = ID3Header(fileobj)
+            except (ID3NoHeaderError, ID3UnsupportedVersionError):
+                frames, offset = _find_id3v1(fileobj)
+                if frames is None:
+                    raise
+
+                self.version = ID3Header._V11
+                for v in frames.values():
+                    self.add(v)
+            else:
+                frames = self.__known_frames
+                if frames is None:
+                    if self.version >= ID3Header._V23:
+                        frames = Frames
+                    elif self.version >= ID3Header._V22:
+                        frames = Frames_2_2
+
+                try:
+                    data = _fullread(fileobj, self.size - 10)
+                except (ValueError, EOFError, IOError) as e:
+                    raise error(e)
+
+                for frame in self.__read_frames(data, frames=frames):
+                    if isinstance(frame, Frame):
+                        self.add(frame)
+                    else:
+                        self.unknown_frames.append(frame)
+                self.__unknown_version = self.version[:2]
+
+        if translate:
+            if v2_version == 3:
+                self.update_to_v23()
+            else:
+                self.update_to_v24()
+
+    def getall(self, key):
+        """Return all frames with a given name (the list may be empty).
+
+        This is best explained by examples::
+
+            id3.getall('TIT2') == [id3['TIT2']]
+            id3.getall('TTTT') == []
+            id3.getall('TXXX') == [TXXX(desc='woo', text='bar'),
+                                   TXXX(desc='baz', text='quuuux'), ...]
+
+        Since this is based on the frame's HashKey, which is
+        colon-separated, you can use it to do things like
+        ``getall('COMM:MusicMatch')`` or ``getall('TXXX:QuodLibet:')``.
+        """
+        if key in self:
+            return [self[key]]
+        else:
+            key = key + ":"
+            return [v for s, v in self.items() if s.startswith(key)]
+
+    def delall(self, key):
+        """Delete all tags of a given kind; see getall."""
+        if key in self:
+            del(self[key])
+        else:
+            key = key + ":"
+            for k in list(self.keys()):
+                if k.startswith(key):
+                    del(self[k])
+
+    def setall(self, key, values):
+        """Delete frames of the given type and add frames in 'values'."""
+        self.delall(key)
+        for tag in values:
+            self[tag.HashKey] = tag
+
+    def pprint(self):
+        """Return tags in a human-readable format.
+
+        "Human-readable" is used loosely here. The format is intended
+        to mirror that used for Vorbis or APEv2 output, e.g.
+
+            ``TIT2=My Title``
+
+        However, ID3 frames can have multiple keys:
+
+            ``POPM=user@example.org=3 128/255``
+        """
+        frames = sorted(Frame.pprint(s) for s in self.values())
+        return "\n".join(frames)
+
+    def loaded_frame(self, tag):
+        """Deprecated; use the add method."""
+        # turn 2.2 into 2.3/2.4 tags
+        if len(type(tag).__name__) == 3:
+            tag = type(tag).__base__(tag)
+        self[tag.HashKey] = tag
+
+    # add = loaded_frame (and vice versa) break applications that
+    # expect to be able to override loaded_frame (e.g. Quod Libet),
+    # as does making loaded_frame call add.
+    def add(self, frame):
+        """Add a frame to the tag."""
+        return self.loaded_frame(frame)
+
+    def __read_frames(self, data, frames):
+        assert self.version >= ID3Header._V22
+
+        if self.version < ID3Header._V24 and self.f_unsynch:
+            try:
+                data = unsynch.decode(data)
+            except ValueError:
+                pass
+
+        if self.version >= ID3Header._V23:
+            if self.version < ID3Header._V24:
+                bpi = int
+            else:
+                bpi = _determine_bpi(data, frames)
+
+            while data:
+                header = data[:10]
+                try:
+                    name, size, flags = unpack('>4sLH', header)
+                except struct.error:
+                    return  # not enough header
+                if name.strip(b'\x00') == b'':
+                    return
+
+                size = bpi(size)
+                framedata = data[10:10 + size]
+                data = data[10 + size:]
+                self._padding = len(data)
+                if size == 0:
+                    continue  # drop empty frames
+
+                if PY3:
+                    try:
+                        name = name.decode('ascii')
+                    except UnicodeDecodeError:
+                        continue
+
+                try:
+                    # someone writes 2.3 frames with 2.2 names
+                    if name[-1] == "\x00":
+                        tag = Frames_2_2[name[:-1]]
+                        name = tag.__base__.__name__
+
+                    tag = frames[name]
+                except KeyError:
+                    if is_valid_frame_id(name):
+                        yield header + framedata
+                else:
+                    try:
+                        yield tag._fromData(self._header, flags, framedata)
+                    except NotImplementedError:
+                        yield header + framedata
+                    except ID3JunkFrameError:
+                        pass
+        elif self.version >= ID3Header._V22:
+            while data:
+                header = data[0:6]
+                try:
+                    name, size = unpack('>3s3s', header)
+                except struct.error:
+                    return  # not enough header
+                size, = struct.unpack('>L', b'\x00' + size)
+                if name.strip(b'\x00') == b'':
+                    return
+
+                framedata = data[6:6 + size]
+                data = data[6 + size:]
+                self._padding = len(data)
+                if size == 0:
+                    continue  # drop empty frames
+
+                if PY3:
+                    try:
+                        name = name.decode('ascii')
+                    except UnicodeDecodeError:
+                        continue
+
+                try:
+                    tag = frames[name]
+                except KeyError:
+                    if is_valid_frame_id(name):
+                        yield header + framedata
+                else:
+                    try:
+                        yield tag._fromData(self._header, 0, framedata)
+                    except (ID3EncryptionUnsupportedError,
+                            NotImplementedError):
+                        yield header + framedata
+                    except ID3JunkFrameError:
+                        pass
+
+    def _prepare_data(self, fileobj, start, available, v2_version, v23_sep,
+                      pad_func):
+        if v2_version == 3:
+            version = ID3Header._V23
+        elif v2_version == 4:
+            version = ID3Header._V24
+        else:
+            raise ValueError("Only 3 or 4 allowed for v2_version")
+
+        # Sort frames by 'importance'
+        order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"]
+        order = dict((b, a) for a, b in enumerate(order))
+        last = len(order)
+        frames = sorted(self.items(),
+                        key=lambda a: (order.get(a[0][:4], last), a[0]))
+
+        framedata = [self.__save_frame(frame, version=version, v23_sep=v23_sep)
+                     for (key, frame) in frames]
+
+        # only write unknown frames if they were loaded from the version
+        # we are saving with or upgraded to it
+        if self.__unknown_version == version[:2]:
+            framedata.extend(data for data in self.unknown_frames
+                             if len(data) > 10)
+
+        needed = sum(map(len, framedata)) + 10
+
+        fileobj.seek(0, 2)
+        trailing_size = fileobj.tell() - start
+
+        info = PaddingInfo(available - needed, trailing_size)
+        new_padding = info._get_padding(pad_func)
+        if new_padding < 0:
+            raise error("invalid padding")
+        new_size = needed + new_padding
+
+        new_framesize = BitPaddedInt.to_str(new_size - 10, width=4)
+        header = pack('>3sBBB4s', b'ID3', v2_version, 0, 0, new_framesize)
+
+        data = bytearray(header)
+        for frame in framedata:
+            data += frame
+        assert new_size >= len(data)
+        data += (new_size - len(data)) * b'\x00'
+        assert new_size == len(data)
+
+        return data
+
+    def save(self, filename=None, v1=1, v2_version=4, v23_sep='/',
+             padding=None):
+        """Save changes to a file.
+
+        Args:
+            filename:
+                Filename to save the tag to. If no filename is given,
+                the one most recently loaded is used.
+            v1 (ID3v1SaveOptions):
+                if 0, ID3v1 tags will be removed.
+                if 1, ID3v1 tags will be updated but not added.
+                if 2, ID3v1 tags will be created and/or updated
+            v2 (int):
+                version of ID3v2 tags (3 or 4).
+            v23_sep (str):
+                the separator used to join multiple text values
+                if v2_version == 3. Defaults to '/' but if it's None
+                will be the ID3v2v2.4 null separator.
+            padding (function):
+                A function taking a PaddingInfo which should
+                return the amount of padding to use. If None (default)
+                will default to something reasonable.
+
+        By default Mutagen saves ID3v2.4 tags. If you want to save ID3v2.3
+        tags, you must call method update_to_v23 before saving the file.
+
+        The lack of a way to update only an ID3v1 tag is intentional.
+
+        Can raise id3.error.
+        """
+
+        if filename is None:
+            filename = self.filename
+
+        try:
+            f = open(filename, 'rb+')
+        except IOError as err:
+            from errno import ENOENT
+            if err.errno != ENOENT:
+                raise
+            f = open(filename, 'ab')  # create, then reopen
+            f = open(filename, 'rb+')
+
+        try:
+            try:
+                header = ID3Header(f)
+            except ID3NoHeaderError:
+                old_size = 0
+            else:
+                old_size = header.size
+
+            data = self._prepare_data(
+                f, 0, old_size, v2_version, v23_sep, padding)
+            new_size = len(data)
+
+            if (old_size < new_size):
+                insert_bytes(f, new_size - old_size, old_size)
+            elif (old_size > new_size):
+                delete_bytes(f, old_size - new_size, new_size)
+            f.seek(0)
+            f.write(data)
+
+            self.__save_v1(f, v1)
+
+        finally:
+            f.close()
+
+    def __save_v1(self, f, v1):
+        tag, offset = _find_id3v1(f)
+        has_v1 = tag is not None
+
+        f.seek(offset, 2)
+        if v1 == ID3v1SaveOptions.UPDATE and has_v1 or \
+                v1 == ID3v1SaveOptions.CREATE:
+            f.write(MakeID3v1(self))
+        else:
+            f.truncate()
+
+    def delete(self, filename=None, delete_v1=True, delete_v2=True):
+        """Remove tags from a file.
+
+        If no filename is given, the one most recently loaded is used.
+
+        Keyword arguments:
+
+        * delete_v1 -- delete any ID3v1 tag
+        * delete_v2 -- delete any ID3v2 tag
+        """
+        if filename is None:
+            filename = self.filename
+        delete(filename, delete_v1, delete_v2)
+        self.clear()
+
+    def __save_frame(self, frame, name=None, version=ID3Header._V24,
+                     v23_sep=None):
+        flags = 0
+        if isinstance(frame, TextFrame):
+            if len(str(frame)) == 0:
+                return b''
+
+        if version == ID3Header._V23:
+            framev23 = frame._get_v23_frame(sep=v23_sep)
+            framedata = framev23._writeData()
+        else:
+            framedata = frame._writeData()
+
+        usize = len(framedata)
+        if usize > 2048:
+            # Disabled as this causes iTunes and other programs
+            # to fail to find these frames, which usually includes
+            # e.g. APIC.
+            # framedata = BitPaddedInt.to_str(usize) + framedata.encode('zlib')
+            # flags |= Frame.FLAG24_COMPRESS | Frame.FLAG24_DATALEN
+            pass
+
+        if version == ID3Header._V24:
+            bits = 7
+        elif version == ID3Header._V23:
+            bits = 8
+        else:
+            raise ValueError
+
+        datasize = BitPaddedInt.to_str(len(framedata), width=4, bits=bits)
+
+        if name is not None:
+            assert isinstance(name, bytes)
+            frame_name = name
+        else:
+            frame_name = type(frame).__name__
+            if PY3:
+                frame_name = frame_name.encode("ascii")
+
+        header = pack('>4s4sH', frame_name, datasize, flags)
+        return header + framedata
+
+    def __update_common(self):
+        """Updates done by both v23 and v24 update"""
+
+        if "TCON" in self:
+            # Get rid of "(xx)Foobr" format.
+            self["TCON"].genres = self["TCON"].genres
+
+        # ID3v2.2 LNK frames are just way too different to upgrade.
+        for frame in self.getall("LINK"):
+            if len(frame.frameid) != 4:
+                del self[frame.HashKey]
+
+        mimes = {"PNG": "image/png", "JPG": "image/jpeg"}
+        for pic in self.getall("APIC"):
+            if pic.mime in mimes:
+                newpic = APIC(
+                    encoding=pic.encoding, mime=mimes[pic.mime],
+                    type=pic.type, desc=pic.desc, data=pic.data)
+                self.add(newpic)
+
+    def update_to_v24(self):
+        """Convert older tags into an ID3v2.4 tag.
+
+        This updates old ID3v2 frames to ID3v2.4 ones (e.g. TYER to
+        TDRC). If you intend to save tags, you must call this function
+        at some point; it is called by default when loading the tag.
+        """
+
+        self.__update_common()
+
+        if self.__unknown_version == (2, 3):
+            # convert unknown 2.3 frames (flags/size) to 2.4
+            converted = []
+            for frame in self.unknown_frames:
+                try:
+                    name, size, flags = unpack('>4sLH', frame[:10])
+                except struct.error:
+                    continue
+
+                try:
+                    frame = BinaryFrame._fromData(
+                        self._header, flags, frame[10:])
+                except (error, NotImplementedError):
+                    continue
+
+                converted.append(self.__save_frame(frame, name=name))
+            self.unknown_frames[:] = converted
+            self.__unknown_version = (2, 4)
+
+        # TDAT, TYER, and TIME have been turned into TDRC.
+        try:
+            date = text_type(self.get("TYER", ""))
+            if date.strip(u"\x00"):
+                self.pop("TYER")
+                dat = text_type(self.get("TDAT", ""))
+                if dat.strip("\x00"):
+                    self.pop("TDAT")
+                    date = "%s-%s-%s" % (date, dat[2:], dat[:2])
+                    time = text_type(self.get("TIME", ""))
+                    if time.strip("\x00"):
+                        self.pop("TIME")
+                        date += "T%s:%s:00" % (time[:2], time[2:])
+                if "TDRC" not in self:
+                    self.add(TDRC(encoding=0, text=date))
+        except UnicodeDecodeError:
+            # Old ID3 tags have *lots* of Unicode problems, so if TYER
+            # is bad, just chuck the frames.
+            pass
+
+        # TORY can be the first part of a TDOR.
+        if "TORY" in self:
+            f = self.pop("TORY")
+            if "TDOR" not in self:
+                try:
+                    self.add(TDOR(encoding=0, text=str(f)))
+                except UnicodeDecodeError:
+                    pass
+
+        # IPLS is now TIPL.
+        if "IPLS" in self:
+            f = self.pop("IPLS")
+            if "TIPL" not in self:
+                self.add(TIPL(encoding=f.encoding, people=f.people))
+
+        # These can't be trivially translated to any ID3v2.4 tags, or
+        # should have been removed already.
+        for key in ["RVAD", "EQUA", "TRDA", "TSIZ", "TDAT", "TIME", "CRM"]:
+            if key in self:
+                del(self[key])
+
+    def update_to_v23(self):
+        """Convert older (and newer) tags into an ID3v2.3 tag.
+
+        This updates incompatible ID3v2 frames to ID3v2.3 ones. If you
+        intend to save tags as ID3v2.3, you must call this function
+        at some point.
+
+        If you want to to go off spec and include some v2.4 frames
+        in v2.3, remove them before calling this and add them back afterwards.
+        """
+
+        self.__update_common()
+
+        # we could downgrade unknown v2.4 frames here, but given that
+        # the main reason to save v2.3 is compatibility and this
+        # might increase the chance of some parser breaking.. better not
+
+        # TMCL, TIPL -> TIPL
+        if "TIPL" in self or "TMCL" in self:
+            people = []
+            if "TIPL" in self:
+                f = self.pop("TIPL")
+                people.extend(f.people)
+            if "TMCL" in self:
+                f = self.pop("TMCL")
+                people.extend(f.people)
+            if "IPLS" not in self:
+                self.add(IPLS(encoding=f.encoding, people=people))
+
+        # TDOR -> TORY
+        if "TDOR" in self:
+            f = self.pop("TDOR")
+            if f.text:
+                d = f.text[0]
+                if d.year and "TORY" not in self:
+                    self.add(TORY(encoding=f.encoding, text="%04d" % d.year))
+
+        # TDRC -> TYER, TDAT, TIME
+        if "TDRC" in self:
+            f = self.pop("TDRC")
+            if f.text:
+                d = f.text[0]
+                if d.year and "TYER" not in self:
+                    self.add(TYER(encoding=f.encoding, text="%04d" % d.year))
+                if d.month and d.day and "TDAT" not in self:
+                    self.add(TDAT(encoding=f.encoding,
+                                  text="%02d%02d" % (d.day, d.month)))
+                if d.hour and d.minute and "TIME" not in self:
+                    self.add(TIME(encoding=f.encoding,
+                                  text="%02d%02d" % (d.hour, d.minute)))
+
+        # New frames added in v2.4
+        v24_frames = [
+            'ASPI', 'EQU2', 'RVA2', 'SEEK', 'SIGN', 'TDEN', 'TDOR',
+            'TDRC', 'TDRL', 'TDTG', 'TIPL', 'TMCL', 'TMOO', 'TPRO',
+            'TSOA', 'TSOP', 'TSOT', 'TSST',
+        ]
+
+        for key in v24_frames:
+            if key in self:
+                del(self[key])
+
+
+def delete(filename, delete_v1=True, delete_v2=True):
+    """Remove tags from a file.
+
+    Keyword arguments:
+
+    * delete_v1 -- delete any ID3v1 tag
+    * delete_v2 -- delete any ID3v2 tag
+    """
+
+    with open(filename, 'rb+') as f:
+
+        if delete_v1:
+            tag, offset = _find_id3v1(f)
+            if tag is not None:
+                f.seek(offset, 2)
+                f.truncate()
+
+        # technically an insize=0 tag is invalid, but we delete it anyway
+        # (primarily because we used to write it)
+        if delete_v2:
+            f.seek(0, 0)
+            idata = f.read(10)
+            try:
+                id3, vmaj, vrev, flags, insize = unpack('>3sBBB4s', idata)
+            except struct.error:
+                id3, insize = b'', -1
+            insize = BitPaddedInt(insize)
+            if id3 == b'ID3' and insize >= 0:
+                delete_bytes(f, insize + 10, 0)
+
+
+# support open(filename) as interface
+Open = ID3
+
+
+def _determine_bpi(data, frames, EMPTY=b"\x00" * 10):
+    """Takes id3v2.4 frame data and determines if ints or bitpaddedints
+    should be used for parsing. Needed because iTunes used to write
+    normal ints for frame sizes.
+    """
+
+    # count number of tags found as BitPaddedInt and how far past
+    o = 0
+    asbpi = 0
+    while o < len(data) - 10:
+        part = data[o:o + 10]
+        if part == EMPTY:
+            bpioff = -((len(data) - o) % 10)
+            break
+        name, size, flags = unpack('>4sLH', part)
+        size = BitPaddedInt(size)
+        o += 10 + size
+        if PY3:
+            try:
+                name = name.decode("ascii")
+            except UnicodeDecodeError:
+                continue
+        if name in frames:
+            asbpi += 1
+    else:
+        bpioff = o - len(data)
+
+    # count number of tags found as int and how far past
+    o = 0
+    asint = 0
+    while o < len(data) - 10:
+        part = data[o:o + 10]
+        if part == EMPTY:
+            intoff = -((len(data) - o) % 10)
+            break
+        name, size, flags = unpack('>4sLH', part)
+        o += 10 + size
+        if PY3:
+            try:
+                name = name.decode("ascii")
+            except UnicodeDecodeError:
+                continue
+        if name in frames:
+            asint += 1
+    else:
+        intoff = o - len(data)
+
+    # if more tags as int, or equal and bpi is past and int is not
+    if asint > asbpi or (asint == asbpi and (bpioff >= 1 and intoff <= 1)):
+        return int
+    return BitPaddedInt
+
+
+def _find_id3v1(fileobj):
+    """Returns a tuple of (id3tag, offset_to_end) or (None, 0)
+
+    offset mainly because we used to write too short tags in some cases and
+    we need the offset to delete them.
+    """
+
+    # id3v1 is always at the end (after apev2)
+
+    extra_read = b"APETAGEX".index(b"TAG")
+
+    try:
+        fileobj.seek(-128 - extra_read, 2)
+    except IOError as e:
+        if e.errno == errno.EINVAL:
+            # If the file is too small, might be ok since we wrote too small
+            # tags at some point. let's see how the parsing goes..
+            fileobj.seek(0, 0)
+        else:
+            raise
+
+    data = fileobj.read(128 + extra_read)
+    try:
+        idx = data.index(b"TAG")
+    except ValueError:
+        return (None, 0)
+    else:
+        # FIXME: make use of the apev2 parser here
+        # if TAG is part of APETAGEX assume this is an APEv2 tag
+        try:
+            ape_idx = data.index(b"APETAGEX")
+        except ValueError:
+            pass
+        else:
+            if idx == ape_idx + extra_read:
+                return (None, 0)
+
+        tag = ParseID3v1(data[idx:])
+        if tag is None:
+            return (None, 0)
+
+        offset = idx - len(data)
+        return (tag, offset)
+
+
+# ID3v1.1 support.
+def ParseID3v1(data):
+    """Parse an ID3v1 tag, returning a list of ID3v2.4 frames.
+
+    Returns a {frame_name: frame} dict or None.
+    """
+
+    try:
+        data = data[data.index(b"TAG"):]
+    except ValueError:
+        return None
+    if 128 < len(data) or len(data) < 124:
+        return None
+
+    # Issue #69 - Previous versions of Mutagen, when encountering
+    # out-of-spec TDRC and TYER frames of less than four characters,
+    # wrote only the characters available - e.g. "1" or "" - into the
+    # year field. To parse those, reduce the size of the year field.
+    # Amazingly, "0s" works as a struct format string.
+    unpack_fmt = "3s30s30s30s%ds29sBB" % (len(data) - 124)
+
+    try:
+        tag, title, artist, album, year, comment, track, genre = unpack(
+            unpack_fmt, data)
+    except StructError:
+        return None
+
+    if tag != b"TAG":
+        return None
+
+    def fix(data):
+        return data.split(b"\x00")[0].strip().decode('latin1')
+
+    title, artist, album, year, comment = map(
+        fix, [title, artist, album, year, comment])
+
+    frames = {}
+    if title:
+        frames["TIT2"] = TIT2(encoding=0, text=title)
+    if artist:
+        frames["TPE1"] = TPE1(encoding=0, text=[artist])
+    if album:
+        frames["TALB"] = TALB(encoding=0, text=album)
+    if year:
+        frames["TDRC"] = TDRC(encoding=0, text=year)
+    if comment:
+        frames["COMM"] = COMM(
+            encoding=0, lang="eng", desc="ID3v1 Comment", text=comment)
+    # Don't read a track number if it looks like the comment was
+    # padded with spaces instead of nulls (thanks, WinAmp).
+    if track and ((track != 32) or (data[-3] == b'\x00'[0])):
+        frames["TRCK"] = TRCK(encoding=0, text=str(track))
+    if genre != 255:
+        frames["TCON"] = TCON(encoding=0, text=str(genre))
+    return frames
+
+
+def MakeID3v1(id3):
+    """Return an ID3v1.1 tag string from a dict of ID3v2.4 frames."""
+
+    v1 = {}
+
+    for v2id, name in {"TIT2": "title", "TPE1": "artist",
+                       "TALB": "album"}.items():
+        if v2id in id3:
+            text = id3[v2id].text[0].encode('latin1', 'replace')[:30]
+        else:
+            text = b""
+        v1[name] = text + (b"\x00" * (30 - len(text)))
+
+    if "COMM" in id3:
+        cmnt = id3["COMM"].text[0].encode('latin1', 'replace')[:28]
+    else:
+        cmnt = b""
+    v1["comment"] = cmnt + (b"\x00" * (29 - len(cmnt)))
+
+    if "TRCK" in id3:
+        try:
+            v1["track"] = chr_(+id3["TRCK"])
+        except ValueError:
+            v1["track"] = b"\x00"
+    else:
+        v1["track"] = b"\x00"
+
+    if "TCON" in id3:
+        try:
+            genre = id3["TCON"].genres[0]
+        except IndexError:
+            pass
+        else:
+            if genre in TCON.GENRES:
+                v1["genre"] = chr_(TCON.GENRES.index(genre))
+    if "genre" not in v1:
+        v1["genre"] = b"\xff"
+
+    if "TDRC" in id3:
+        year = text_type(id3["TDRC"]).encode('ascii')
+    elif "TYER" in id3:
+        year = text_type(id3["TYER"]).encode('ascii')
+    else:
+        year = b""
+    v1["year"] = (year + b"\x00\x00\x00\x00")[:4]
+
+    return (
+        b"TAG" +
+        v1["title"] +
+        v1["artist"] +
+        v1["album"] +
+        v1["year"] +
+        v1["comment"] +
+        v1["track"] +
+        v1["genre"]
+    )
+
+
+class ID3FileType(mutagen.FileType):
+    """An unknown type of file with ID3 tags."""
+
+    ID3 = ID3
+
+    class _Info(mutagen.StreamInfo):
+        length = 0
+
+        def __init__(self, fileobj, offset):
+            pass
+
+        @staticmethod
+        def pprint():
+            return "Unknown format with ID3 tag"
+
+    @staticmethod
+    def score(filename, fileobj, header_data):
+        return header_data.startswith(b"ID3")
+
+    def add_tags(self, ID3=None):
+        """Add an empty ID3 tag to the file.
+
+        A custom tag reader may be used in instead of the default
+        mutagen.id3.ID3 object, e.g. an EasyID3 reader.
+        """
+        if ID3 is None:
+            ID3 = self.ID3
+        if self.tags is None:
+            self.ID3 = ID3
+            self.tags = ID3()
+        else:
+            raise error("an ID3 tag already exists")
+
+    def load(self, filename, ID3=None, **kwargs):
+        """Load stream and tag information from a file.
+
+        A custom tag reader may be used in instead of the default
+        mutagen.id3.ID3 object, e.g. an EasyID3 reader.
+        """
+
+        if ID3 is None:
+            ID3 = self.ID3
+        else:
+            # If this was initialized with EasyID3, remember that for
+            # when tags are auto-instantiated in add_tags.
+            self.ID3 = ID3
+        self.filename = filename
+        try:
+            self.tags = ID3(filename, **kwargs)
+        except ID3NoHeaderError:
+            self.tags = None
+
+        if self.tags is not None:
+            try:
+                offset = self.tags.size
+            except AttributeError:
+                offset = None
+        else:
+            offset = None
+
+        with open(filename, "rb") as fileobj:
+            self.info = self._Info(fileobj, offset)
diff --git a/resources/lib/mutagen/id3/__pycache__/__init__.cpython-35.pyc b/resources/lib/mutagen/id3/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f423db1af7e6371db8a6eea5d505a35cb1aee338
GIT binary patch
literal 27785
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/id3/__pycache__/_frames.cpython-35.pyc b/resources/lib/mutagen/id3/__pycache__/_frames.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0331df593173cd69e7761b5b023fe931ec86c0b1
GIT binary patch
literal 64560
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/id3/__pycache__/_specs.cpython-35.pyc b/resources/lib/mutagen/id3/__pycache__/_specs.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9af16aa1ca17196b9bf38ccd837732df328d8b9f
GIT binary patch
literal 22816
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
bC^ysnQE2@B+4Mj<mL5&Nls=g5PpAJcimw4r

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/id3/__pycache__/_util.cpython-35.pyc b/resources/lib/mutagen/id3/__pycache__/_util.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c2c75dadb9a532645a03141da6396906431288b9
GIT binary patch
literal 4933
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/id3/_frames.py b/resources/lib/mutagen/id3/_frames.py
new file mode 100644
index 00000000..c185cef3
--- /dev/null
+++ b/resources/lib/mutagen/id3/_frames.py
@@ -0,0 +1,1925 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2005  Michael Urman
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+import zlib
+from struct import unpack
+
+from ._util import ID3JunkFrameError, ID3EncryptionUnsupportedError, unsynch
+from ._specs import (
+    BinaryDataSpec, StringSpec, Latin1TextSpec, EncodedTextSpec, ByteSpec,
+    EncodingSpec, ASPIIndexSpec, SizedIntegerSpec, IntegerSpec,
+    VolumeAdjustmentsSpec, VolumePeakSpec, VolumeAdjustmentSpec,
+    ChannelSpec, MultiSpec, SynchronizedTextSpec, KeyEventSpec, TimeStampSpec,
+    EncodedNumericPartTextSpec, EncodedNumericTextSpec, SpecError)
+from .._compat import text_type, string_types, swap_to_string, iteritems, izip
+
+
+def is_valid_frame_id(frame_id):
+    return frame_id.isalnum() and frame_id.isupper()
+
+
+def _bytes2key(b):
+    assert isinstance(b, bytes)
+
+    return b.decode("latin1")
+
+
+class Frame(object):
+    """Fundamental unit of ID3 data.
+
+    ID3 tags are split into frames. Each frame has a potentially
+    different structure, and so this base class is not very featureful.
+    """
+
+    FLAG23_ALTERTAG = 0x8000
+    FLAG23_ALTERFILE = 0x4000
+    FLAG23_READONLY = 0x2000
+    FLAG23_COMPRESS = 0x0080
+    FLAG23_ENCRYPT = 0x0040
+    FLAG23_GROUP = 0x0020
+
+    FLAG24_ALTERTAG = 0x4000
+    FLAG24_ALTERFILE = 0x2000
+    FLAG24_READONLY = 0x1000
+    FLAG24_GROUPID = 0x0040
+    FLAG24_COMPRESS = 0x0008
+    FLAG24_ENCRYPT = 0x0004
+    FLAG24_UNSYNCH = 0x0002
+    FLAG24_DATALEN = 0x0001
+
+    _framespec = []
+
+    def __init__(self, *args, **kwargs):
+        if len(args) == 1 and len(kwargs) == 0 and \
+                isinstance(args[0], type(self)):
+            other = args[0]
+            # ask the sub class to fill in our data
+            other._to_other(self)
+        else:
+            for checker, val in izip(self._framespec, args):
+                setattr(self, checker.name, checker.validate(self, val))
+            for checker in self._framespec[len(args):]:
+                try:
+                    validated = checker.validate(
+                        self, kwargs.get(checker.name, None))
+                except ValueError as e:
+                    raise ValueError("%s: %s" % (checker.name, e))
+                setattr(self, checker.name, validated)
+
+    def _to_other(self, other):
+        # this impl covers subclasses with the same framespec
+        if other._framespec is not self._framespec:
+            raise ValueError
+
+        for checker in other._framespec:
+            setattr(other, checker.name, getattr(self, checker.name))
+
+    def _get_v23_frame(self, **kwargs):
+        """Returns a frame copy which is suitable for writing into a v2.3 tag.
+
+        kwargs get passed to the specs.
+        """
+
+        new_kwargs = {}
+        for checker in self._framespec:
+            name = checker.name
+            value = getattr(self, name)
+            new_kwargs[name] = checker._validate23(self, value, **kwargs)
+        return type(self)(**new_kwargs)
+
+    @property
+    def HashKey(self):
+        """An internal key used to ensure frame uniqueness in a tag"""
+
+        return self.FrameID
+
+    @property
+    def FrameID(self):
+        """ID3v2 three or four character frame ID"""
+
+        return type(self).__name__
+
+    def __repr__(self):
+        """Python representation of a frame.
+
+        The string returned is a valid Python expression to construct
+        a copy of this frame.
+        """
+        kw = []
+        for attr in self._framespec:
+            # so repr works during __init__
+            if hasattr(self, attr.name):
+                kw.append('%s=%r' % (attr.name, getattr(self, attr.name)))
+        return '%s(%s)' % (type(self).__name__, ', '.join(kw))
+
+    def _readData(self, data):
+        """Raises ID3JunkFrameError; Returns leftover data"""
+
+        for reader in self._framespec:
+            if len(data):
+                try:
+                    value, data = reader.read(self, data)
+                except SpecError as e:
+                    raise ID3JunkFrameError(e)
+            else:
+                raise ID3JunkFrameError("no data left")
+            setattr(self, reader.name, value)
+
+        return data
+
+    def _writeData(self):
+        data = []
+        for writer in self._framespec:
+            data.append(writer.write(self, getattr(self, writer.name)))
+        return b''.join(data)
+
+    def pprint(self):
+        """Return a human-readable representation of the frame."""
+        return "%s=%s" % (type(self).__name__, self._pprint())
+
+    def _pprint(self):
+        return "[unrepresentable data]"
+
+    @classmethod
+    def _fromData(cls, id3, tflags, data):
+        """Construct this ID3 frame from raw string data.
+
+        Raises:
+
+        ID3JunkFrameError in case parsing failed
+        NotImplementedError in case parsing isn't implemented
+        ID3EncryptionUnsupportedError in case the frame is encrypted.
+        """
+
+        if id3.version >= id3._V24:
+            if tflags & (Frame.FLAG24_COMPRESS | Frame.FLAG24_DATALEN):
+                # The data length int is syncsafe in 2.4 (but not 2.3).
+                # However, we don't actually need the data length int,
+                # except to work around a QL 0.12 bug, and in that case
+                # all we need are the raw bytes.
+                datalen_bytes = data[:4]
+                data = data[4:]
+            if tflags & Frame.FLAG24_UNSYNCH or id3.f_unsynch:
+                try:
+                    data = unsynch.decode(data)
+                except ValueError:
+                    # Some things write synch-unsafe data with either the frame
+                    # or global unsynch flag set. Try to load them as is.
+                    # https://bitbucket.org/lazka/mutagen/issue/210
+                    # https://bitbucket.org/lazka/mutagen/issue/223
+                    pass
+            if tflags & Frame.FLAG24_ENCRYPT:
+                raise ID3EncryptionUnsupportedError
+            if tflags & Frame.FLAG24_COMPRESS:
+                try:
+                    data = zlib.decompress(data)
+                except zlib.error as err:
+                    # the initial mutagen that went out with QL 0.12 did not
+                    # write the 4 bytes of uncompressed size. Compensate.
+                    data = datalen_bytes + data
+                    try:
+                        data = zlib.decompress(data)
+                    except zlib.error as err:
+                        raise ID3JunkFrameError(
+                            'zlib: %s: %r' % (err, data))
+
+        elif id3.version >= id3._V23:
+            if tflags & Frame.FLAG23_COMPRESS:
+                usize, = unpack('>L', data[:4])
+                data = data[4:]
+            if tflags & Frame.FLAG23_ENCRYPT:
+                raise ID3EncryptionUnsupportedError
+            if tflags & Frame.FLAG23_COMPRESS:
+                try:
+                    data = zlib.decompress(data)
+                except zlib.error as err:
+                    raise ID3JunkFrameError('zlib: %s: %r' % (err, data))
+
+        frame = cls()
+        frame._readData(data)
+        return frame
+
+    def __hash__(self):
+        raise TypeError("Frame objects are unhashable")
+
+
+class FrameOpt(Frame):
+    """A frame with optional parts.
+
+    Some ID3 frames have optional data; this class extends Frame to
+    provide support for those parts.
+    """
+
+    _optionalspec = []
+
+    def __init__(self, *args, **kwargs):
+        super(FrameOpt, self).__init__(*args, **kwargs)
+        for spec in self._optionalspec:
+            if spec.name in kwargs:
+                validated = spec.validate(self, kwargs[spec.name])
+                setattr(self, spec.name, validated)
+            else:
+                break
+
+    def _to_other(self, other):
+        super(FrameOpt, self)._to_other(other)
+
+        # this impl covers subclasses with the same optionalspec
+        if other._optionalspec is not self._optionalspec:
+            raise ValueError
+
+        for checker in other._optionalspec:
+            if hasattr(self, checker.name):
+                setattr(other, checker.name, getattr(self, checker.name))
+
+    def _readData(self, data):
+        """Raises ID3JunkFrameError; Returns leftover data"""
+
+        for reader in self._framespec:
+            if len(data):
+                try:
+                    value, data = reader.read(self, data)
+                except SpecError as e:
+                    raise ID3JunkFrameError(e)
+            else:
+                raise ID3JunkFrameError("no data left")
+            setattr(self, reader.name, value)
+
+        if data:
+            for reader in self._optionalspec:
+                if len(data):
+                    try:
+                        value, data = reader.read(self, data)
+                    except SpecError as e:
+                        raise ID3JunkFrameError(e)
+                else:
+                    break
+                setattr(self, reader.name, value)
+
+        return data
+
+    def _writeData(self):
+        data = []
+        for writer in self._framespec:
+            data.append(writer.write(self, getattr(self, writer.name)))
+        for writer in self._optionalspec:
+            try:
+                data.append(writer.write(self, getattr(self, writer.name)))
+            except AttributeError:
+                break
+        return b''.join(data)
+
+    def __repr__(self):
+        kw = []
+        for attr in self._framespec:
+            kw.append('%s=%r' % (attr.name, getattr(self, attr.name)))
+        for attr in self._optionalspec:
+            if hasattr(self, attr.name):
+                kw.append('%s=%r' % (attr.name, getattr(self, attr.name)))
+        return '%s(%s)' % (type(self).__name__, ', '.join(kw))
+
+
+@swap_to_string
+class TextFrame(Frame):
+    """Text strings.
+
+    Text frames support casts to unicode or str objects, as well as
+    list-like indexing, extend, and append.
+
+    Iterating over a TextFrame iterates over its strings, not its
+    characters.
+
+    Text frames have a 'text' attribute which is the list of strings,
+    and an 'encoding' attribute; 0 for ISO-8859 1, 1 UTF-16, 2 for
+    UTF-16BE, and 3 for UTF-8. If you don't want to worry about
+    encodings, just set it to 3.
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        MultiSpec('text', EncodedTextSpec('text'), sep=u'\u0000'),
+    ]
+
+    def __bytes__(self):
+        return text_type(self).encode('utf-8')
+
+    def __str__(self):
+        return u'\u0000'.join(self.text)
+
+    def __eq__(self, other):
+        if isinstance(other, bytes):
+            return bytes(self) == other
+        elif isinstance(other, text_type):
+            return text_type(self) == other
+        return self.text == other
+
+    __hash__ = Frame.__hash__
+
+    def __getitem__(self, item):
+        return self.text[item]
+
+    def __iter__(self):
+        return iter(self.text)
+
+    def append(self, value):
+        """Append a string."""
+
+        return self.text.append(value)
+
+    def extend(self, value):
+        """Extend the list by appending all strings from the given list."""
+
+        return self.text.extend(value)
+
+    def _pprint(self):
+        return " / ".join(self.text)
+
+
+class NumericTextFrame(TextFrame):
+    """Numerical text strings.
+
+    The numeric value of these frames can be gotten with unary plus, e.g.::
+
+        frame = TLEN('12345')
+        length = +frame
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        MultiSpec('text', EncodedNumericTextSpec('text'), sep=u'\u0000'),
+    ]
+
+    def __pos__(self):
+        """Return the numerical value of the string."""
+        return int(self.text[0])
+
+
+class NumericPartTextFrame(TextFrame):
+    """Multivalue numerical text strings.
+
+    These strings indicate 'part (e.g. track) X of Y', and unary plus
+    returns the first value::
+
+        frame = TRCK('4/15')
+        track = +frame # track == 4
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        MultiSpec('text', EncodedNumericPartTextSpec('text'), sep=u'\u0000'),
+    ]
+
+    def __pos__(self):
+        return int(self.text[0].split("/")[0])
+
+
+@swap_to_string
+class TimeStampTextFrame(TextFrame):
+    """A list of time stamps.
+
+    The 'text' attribute in this frame is a list of ID3TimeStamp
+    objects, not a list of strings.
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        MultiSpec('text', TimeStampSpec('stamp'), sep=u','),
+    ]
+
+    def __bytes__(self):
+        return text_type(self).encode('utf-8')
+
+    def __str__(self):
+        return u','.join([stamp.text for stamp in self.text])
+
+    def _pprint(self):
+        return u" / ".join([stamp.text for stamp in self.text])
+
+
+@swap_to_string
+class UrlFrame(Frame):
+    """A frame containing a URL string.
+
+    The ID3 specification is silent about IRIs and normalized URL
+    forms. Mutagen assumes all URLs in files are encoded as Latin 1,
+    but string conversion of this frame returns a UTF-8 representation
+    for compatibility with other string conversions.
+
+    The only sane way to handle URLs in MP3s is to restrict them to
+    ASCII.
+    """
+
+    _framespec = [Latin1TextSpec('url')]
+
+    def __bytes__(self):
+        return self.url.encode('utf-8')
+
+    def __str__(self):
+        return self.url
+
+    def __eq__(self, other):
+        return self.url == other
+
+    __hash__ = Frame.__hash__
+
+    def _pprint(self):
+        return self.url
+
+
+class UrlFrameU(UrlFrame):
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.url)
+
+
+class TALB(TextFrame):
+    "Album"
+
+
+class TBPM(NumericTextFrame):
+    "Beats per minute"
+
+
+class TCOM(TextFrame):
+    "Composer"
+
+
+class TCON(TextFrame):
+    """Content type (Genre)
+
+    ID3 has several ways genres can be represented; for convenience,
+    use the 'genres' property rather than the 'text' attribute.
+    """
+
+    from mutagen._constants import GENRES
+    GENRES = GENRES
+
+    def __get_genres(self):
+        genres = []
+        import re
+        genre_re = re.compile(r"((?:\((?P<id>[0-9]+|RX|CR)\))*)(?P<str>.+)?")
+        for value in self.text:
+            # 255 possible entries in id3v1
+            if value.isdigit() and int(value) < 256:
+                try:
+                    genres.append(self.GENRES[int(value)])
+                except IndexError:
+                    genres.append(u"Unknown")
+            elif value == "CR":
+                genres.append(u"Cover")
+            elif value == "RX":
+                genres.append(u"Remix")
+            elif value:
+                newgenres = []
+                genreid, dummy, genrename = genre_re.match(value).groups()
+
+                if genreid:
+                    for gid in genreid[1:-1].split(")("):
+                        if gid.isdigit() and int(gid) < len(self.GENRES):
+                            gid = text_type(self.GENRES[int(gid)])
+                            newgenres.append(gid)
+                        elif gid == "CR":
+                            newgenres.append(u"Cover")
+                        elif gid == "RX":
+                            newgenres.append(u"Remix")
+                        else:
+                            newgenres.append(u"Unknown")
+
+                if genrename:
+                    # "Unescaping" the first parenthesis
+                    if genrename.startswith("(("):
+                        genrename = genrename[1:]
+                    if genrename not in newgenres:
+                        newgenres.append(genrename)
+
+                genres.extend(newgenres)
+
+        return genres
+
+    def __set_genres(self, genres):
+        if isinstance(genres, string_types):
+            genres = [genres]
+        self.text = [self.__decode(g) for g in genres]
+
+    def __decode(self, value):
+        if isinstance(value, bytes):
+            enc = EncodedTextSpec._encodings[self.encoding][0]
+            return value.decode(enc)
+        else:
+            return value
+
+    genres = property(__get_genres, __set_genres, None,
+                      "A list of genres parsed from the raw text data.")
+
+    def _pprint(self):
+        return " / ".join(self.genres)
+
+
+class TCOP(TextFrame):
+    "Copyright (c)"
+
+
+class TCMP(NumericTextFrame):
+    "iTunes Compilation Flag"
+
+
+class TDAT(TextFrame):
+    "Date of recording (DDMM)"
+
+
+class TDEN(TimeStampTextFrame):
+    "Encoding Time"
+
+
+class TDES(TextFrame):
+    "iTunes Podcast Description"
+
+
+class TDOR(TimeStampTextFrame):
+    "Original Release Time"
+
+
+class TDLY(NumericTextFrame):
+    "Audio Delay (ms)"
+
+
+class TDRC(TimeStampTextFrame):
+    "Recording Time"
+
+
+class TDRL(TimeStampTextFrame):
+    "Release Time"
+
+
+class TDTG(TimeStampTextFrame):
+    "Tagging Time"
+
+
+class TENC(TextFrame):
+    "Encoder"
+
+
+class TEXT(TextFrame):
+    "Lyricist"
+
+
+class TFLT(TextFrame):
+    "File type"
+
+
+class TGID(TextFrame):
+    "iTunes Podcast Identifier"
+
+
+class TIME(TextFrame):
+    "Time of recording (HHMM)"
+
+
+class TIT1(TextFrame):
+    "Content group description"
+
+
+class TIT2(TextFrame):
+    "Title"
+
+
+class TIT3(TextFrame):
+    "Subtitle/Description refinement"
+
+
+class TKEY(TextFrame):
+    "Starting Key"
+
+
+class TLAN(TextFrame):
+    "Audio Languages"
+
+
+class TLEN(NumericTextFrame):
+    "Audio Length (ms)"
+
+
+class TMED(TextFrame):
+    "Source Media Type"
+
+
+class TMOO(TextFrame):
+    "Mood"
+
+
+class TOAL(TextFrame):
+    "Original Album"
+
+
+class TOFN(TextFrame):
+    "Original Filename"
+
+
+class TOLY(TextFrame):
+    "Original Lyricist"
+
+
+class TOPE(TextFrame):
+    "Original Artist/Performer"
+
+
+class TORY(NumericTextFrame):
+    "Original Release Year"
+
+
+class TOWN(TextFrame):
+    "Owner/Licensee"
+
+
+class TPE1(TextFrame):
+    "Lead Artist/Performer/Soloist/Group"
+
+
+class TPE2(TextFrame):
+    "Band/Orchestra/Accompaniment"
+
+
+class TPE3(TextFrame):
+    "Conductor"
+
+
+class TPE4(TextFrame):
+    "Interpreter/Remixer/Modifier"
+
+
+class TPOS(NumericPartTextFrame):
+    "Part of set"
+
+
+class TPRO(TextFrame):
+    "Produced (P)"
+
+
+class TPUB(TextFrame):
+    "Publisher"
+
+
+class TRCK(NumericPartTextFrame):
+    "Track Number"
+
+
+class TRDA(TextFrame):
+    "Recording Dates"
+
+
+class TRSN(TextFrame):
+    "Internet Radio Station Name"
+
+
+class TRSO(TextFrame):
+    "Internet Radio Station Owner"
+
+
+class TSIZ(NumericTextFrame):
+    "Size of audio data (bytes)"
+
+
+class TSO2(TextFrame):
+    "iTunes Album Artist Sort"
+
+
+class TSOA(TextFrame):
+    "Album Sort Order key"
+
+
+class TSOC(TextFrame):
+    "iTunes Composer Sort"
+
+
+class TSOP(TextFrame):
+    "Perfomer Sort Order key"
+
+
+class TSOT(TextFrame):
+    "Title Sort Order key"
+
+
+class TSRC(TextFrame):
+    "International Standard Recording Code (ISRC)"
+
+
+class TSSE(TextFrame):
+    "Encoder settings"
+
+
+class TSST(TextFrame):
+    "Set Subtitle"
+
+
+class TYER(NumericTextFrame):
+    "Year of recording"
+
+
+class TXXX(TextFrame):
+    """User-defined text data.
+
+    TXXX frames have a 'desc' attribute which is set to any Unicode
+    value (though the encoding of the text and the description must be
+    the same). Many taggers use this frame to store freeform keys.
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        EncodedTextSpec('desc'),
+        MultiSpec('text', EncodedTextSpec('text'), sep=u'\u0000'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.desc)
+
+    def _pprint(self):
+        return "%s=%s" % (self.desc, " / ".join(self.text))
+
+
+class WCOM(UrlFrameU):
+    "Commercial Information"
+
+
+class WCOP(UrlFrame):
+    "Copyright Information"
+
+
+class WFED(UrlFrame):
+    "iTunes Podcast Feed"
+
+
+class WOAF(UrlFrame):
+    "Official File Information"
+
+
+class WOAR(UrlFrameU):
+    "Official Artist/Performer Information"
+
+
+class WOAS(UrlFrame):
+    "Official Source Information"
+
+
+class WORS(UrlFrame):
+    "Official Internet Radio Information"
+
+
+class WPAY(UrlFrame):
+    "Payment Information"
+
+
+class WPUB(UrlFrame):
+    "Official Publisher Information"
+
+
+class WXXX(UrlFrame):
+    """User-defined URL data.
+
+    Like TXXX, this has a freeform description associated with it.
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        EncodedTextSpec('desc'),
+        Latin1TextSpec('url'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.desc)
+
+
+class PairedTextFrame(Frame):
+    """Paired text strings.
+
+    Some ID3 frames pair text strings, to associate names with a more
+    specific involvement in the song. The 'people' attribute of these
+    frames contains a list of pairs::
+
+        [['trumpet', 'Miles Davis'], ['bass', 'Paul Chambers']]
+
+    Like text frames, these frames also have an encoding attribute.
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        MultiSpec('people',
+                  EncodedTextSpec('involvement'),
+                  EncodedTextSpec('person'))
+    ]
+
+    def __eq__(self, other):
+        return self.people == other
+
+    __hash__ = Frame.__hash__
+
+
+class TIPL(PairedTextFrame):
+    "Involved People List"
+
+
+class TMCL(PairedTextFrame):
+    "Musicians Credits List"
+
+
+class IPLS(TIPL):
+    "Involved People List"
+
+
+class BinaryFrame(Frame):
+    """Binary data
+
+    The 'data' attribute contains the raw byte string.
+    """
+
+    _framespec = [BinaryDataSpec('data')]
+
+    def __eq__(self, other):
+        return self.data == other
+
+    __hash__ = Frame.__hash__
+
+
+class MCDI(BinaryFrame):
+    "Binary dump of CD's TOC"
+
+
+class ETCO(Frame):
+    """Event timing codes."""
+
+    _framespec = [
+        ByteSpec("format"),
+        KeyEventSpec("events"),
+    ]
+
+    def __eq__(self, other):
+        return self.events == other
+
+    __hash__ = Frame.__hash__
+
+
+class MLLT(Frame):
+    """MPEG location lookup table.
+
+    This frame's attributes may be changed in the future based on
+    feedback from real-world use.
+    """
+
+    _framespec = [
+        SizedIntegerSpec('frames', 2),
+        SizedIntegerSpec('bytes', 3),
+        SizedIntegerSpec('milliseconds', 3),
+        ByteSpec('bits_for_bytes'),
+        ByteSpec('bits_for_milliseconds'),
+        BinaryDataSpec('data'),
+    ]
+
+    def __eq__(self, other):
+        return self.data == other
+
+    __hash__ = Frame.__hash__
+
+
+class SYTC(Frame):
+    """Synchronised tempo codes.
+
+    This frame's attributes may be changed in the future based on
+    feedback from real-world use.
+    """
+
+    _framespec = [
+        ByteSpec("format"),
+        BinaryDataSpec("data"),
+    ]
+
+    def __eq__(self, other):
+        return self.data == other
+
+    __hash__ = Frame.__hash__
+
+
+@swap_to_string
+class USLT(Frame):
+    """Unsynchronised lyrics/text transcription.
+
+    Lyrics have a three letter ISO language code ('lang'), a
+    description ('desc'), and a block of plain text ('text').
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        StringSpec('lang', 3),
+        EncodedTextSpec('desc'),
+        EncodedTextSpec('text'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s:%s' % (self.FrameID, self.desc, self.lang)
+
+    def __bytes__(self):
+        return self.text.encode('utf-8')
+
+    def __str__(self):
+        return self.text
+
+    def __eq__(self, other):
+        return self.text == other
+
+    __hash__ = Frame.__hash__
+
+
+@swap_to_string
+class SYLT(Frame):
+    """Synchronised lyrics/text."""
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        StringSpec('lang', 3),
+        ByteSpec('format'),
+        ByteSpec('type'),
+        EncodedTextSpec('desc'),
+        SynchronizedTextSpec('text'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s:%s' % (self.FrameID, self.desc, self.lang)
+
+    def __eq__(self, other):
+        return str(self) == other
+
+    __hash__ = Frame.__hash__
+
+    def __str__(self):
+        return u"".join(text for (text, time) in self.text)
+
+    def __bytes__(self):
+        return text_type(self).encode("utf-8")
+
+
+class COMM(TextFrame):
+    """User comment.
+
+    User comment frames have a descrption, like TXXX, and also a three
+    letter ISO language code in the 'lang' attribute.
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        StringSpec('lang', 3),
+        EncodedTextSpec('desc'),
+        MultiSpec('text', EncodedTextSpec('text'), sep=u'\u0000'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s:%s' % (self.FrameID, self.desc, self.lang)
+
+    def _pprint(self):
+        return "%s=%s=%s" % (self.desc, self.lang, " / ".join(self.text))
+
+
+class RVA2(Frame):
+    """Relative volume adjustment (2).
+
+    This frame is used to implemented volume scaling, and in
+    particular, normalization using ReplayGain.
+
+    Attributes:
+
+    * desc -- description or context of this adjustment
+    * channel -- audio channel to adjust (master is 1)
+    * gain -- a + or - dB gain relative to some reference level
+    * peak -- peak of the audio as a floating point number, [0, 1]
+
+    When storing ReplayGain tags, use descriptions of 'album' and
+    'track' on channel 1.
+    """
+
+    _framespec = [
+        Latin1TextSpec('desc'),
+        ChannelSpec('channel'),
+        VolumeAdjustmentSpec('gain'),
+        VolumePeakSpec('peak'),
+    ]
+
+    _channels = ["Other", "Master volume", "Front right", "Front left",
+                 "Back right", "Back left", "Front centre", "Back centre",
+                 "Subwoofer"]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.desc)
+
+    def __eq__(self, other):
+        try:
+            return ((str(self) == other) or
+                    (self.desc == other.desc and
+                     self.channel == other.channel and
+                     self.gain == other.gain and
+                     self.peak == other.peak))
+        except AttributeError:
+            return False
+
+    __hash__ = Frame.__hash__
+
+    def __str__(self):
+        return "%s: %+0.4f dB/%0.4f" % (
+            self._channels[self.channel], self.gain, self.peak)
+
+
+class EQU2(Frame):
+    """Equalisation (2).
+
+    Attributes:
+    method -- interpolation method (0 = band, 1 = linear)
+    desc -- identifying description
+    adjustments -- list of (frequency, vol_adjustment) pairs
+    """
+
+    _framespec = [
+        ByteSpec("method"),
+        Latin1TextSpec("desc"),
+        VolumeAdjustmentsSpec("adjustments"),
+    ]
+
+    def __eq__(self, other):
+        return self.adjustments == other
+
+    __hash__ = Frame.__hash__
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.desc)
+
+
+# class RVAD: unsupported
+# class EQUA: unsupported
+
+
+class RVRB(Frame):
+    """Reverb."""
+
+    _framespec = [
+        SizedIntegerSpec('left', 2),
+        SizedIntegerSpec('right', 2),
+        ByteSpec('bounce_left'),
+        ByteSpec('bounce_right'),
+        ByteSpec('feedback_ltl'),
+        ByteSpec('feedback_ltr'),
+        ByteSpec('feedback_rtr'),
+        ByteSpec('feedback_rtl'),
+        ByteSpec('premix_ltr'),
+        ByteSpec('premix_rtl'),
+    ]
+
+    def __eq__(self, other):
+        return (self.left, self.right) == other
+
+    __hash__ = Frame.__hash__
+
+
+class APIC(Frame):
+    """Attached (or linked) Picture.
+
+    Attributes:
+
+    * encoding -- text encoding for the description
+    * mime -- a MIME type (e.g. image/jpeg) or '-->' if the data is a URI
+    * type -- the source of the image (3 is the album front cover)
+    * desc -- a text description of the image
+    * data -- raw image data, as a byte string
+
+    Mutagen will automatically compress large images when saving tags.
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        Latin1TextSpec('mime'),
+        ByteSpec('type'),
+        EncodedTextSpec('desc'),
+        BinaryDataSpec('data'),
+    ]
+
+    def __eq__(self, other):
+        return self.data == other
+
+    __hash__ = Frame.__hash__
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.desc)
+
+    def _validate_from_22(self, other, checker):
+        if checker.name == "mime":
+            self.mime = other.mime.decode("ascii", "ignore")
+        else:
+            super(APIC, self)._validate_from_22(other, checker)
+
+    def _pprint(self):
+        return "%s (%s, %d bytes)" % (
+            self.desc, self.mime, len(self.data))
+
+
+class PCNT(Frame):
+    """Play counter.
+
+    The 'count' attribute contains the (recorded) number of times this
+    file has been played.
+
+    This frame is basically obsoleted by POPM.
+    """
+
+    _framespec = [IntegerSpec('count')]
+
+    def __eq__(self, other):
+        return self.count == other
+
+    __hash__ = Frame.__hash__
+
+    def __pos__(self):
+        return self.count
+
+    def _pprint(self):
+        return text_type(self.count)
+
+
+class POPM(FrameOpt):
+    """Popularimeter.
+
+    This frame keys a rating (out of 255) and a play count to an email
+    address.
+
+    Attributes:
+
+    * email -- email this POPM frame is for
+    * rating -- rating from 0 to 255
+    * count -- number of times the files has been played (optional)
+    """
+
+    _framespec = [
+        Latin1TextSpec('email'),
+        ByteSpec('rating'),
+    ]
+
+    _optionalspec = [IntegerSpec('count')]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.email)
+
+    def __eq__(self, other):
+        return self.rating == other
+
+    __hash__ = FrameOpt.__hash__
+
+    def __pos__(self):
+        return self.rating
+
+    def _pprint(self):
+        return "%s=%r %r/255" % (
+            self.email, getattr(self, 'count', None), self.rating)
+
+
+class GEOB(Frame):
+    """General Encapsulated Object.
+
+    A blob of binary data, that is not a picture (those go in APIC).
+
+    Attributes:
+
+    * encoding -- encoding of the description
+    * mime -- MIME type of the data or '-->' if the data is a URI
+    * filename -- suggested filename if extracted
+    * desc -- text description of the data
+    * data -- raw data, as a byte string
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        Latin1TextSpec('mime'),
+        EncodedTextSpec('filename'),
+        EncodedTextSpec('desc'),
+        BinaryDataSpec('data'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.desc)
+
+    def __eq__(self, other):
+        return self.data == other
+
+    __hash__ = Frame.__hash__
+
+
+class RBUF(FrameOpt):
+    """Recommended buffer size.
+
+    Attributes:
+
+    * size -- recommended buffer size in bytes
+    * info -- if ID3 tags may be elsewhere in the file (optional)
+    * offset -- the location of the next ID3 tag, if any
+
+    Mutagen will not find the next tag itself.
+    """
+
+    _framespec = [SizedIntegerSpec('size', 3)]
+
+    _optionalspec = [
+        ByteSpec('info'),
+        SizedIntegerSpec('offset', 4),
+    ]
+
+    def __eq__(self, other):
+        return self.size == other
+
+    __hash__ = FrameOpt.__hash__
+
+    def __pos__(self):
+        return self.size
+
+
+@swap_to_string
+class AENC(FrameOpt):
+    """Audio encryption.
+
+    Attributes:
+
+    * owner -- key identifying this encryption type
+    * preview_start -- unencrypted data block offset
+    * preview_length -- number of unencrypted blocks
+    * data -- data required for decryption (optional)
+
+    Mutagen cannot decrypt files.
+    """
+
+    _framespec = [
+        Latin1TextSpec('owner'),
+        SizedIntegerSpec('preview_start', 2),
+        SizedIntegerSpec('preview_length', 2),
+    ]
+
+    _optionalspec = [BinaryDataSpec('data')]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.owner)
+
+    def __bytes__(self):
+        return self.owner.encode('utf-8')
+
+    def __str__(self):
+        return self.owner
+
+    def __eq__(self, other):
+        return self.owner == other
+
+    __hash__ = FrameOpt.__hash__
+
+
+class LINK(FrameOpt):
+    """Linked information.
+
+    Attributes:
+
+    * frameid -- the ID of the linked frame
+    * url -- the location of the linked frame
+    * data -- further ID information for the frame
+    """
+
+    _framespec = [
+        StringSpec('frameid', 4),
+        Latin1TextSpec('url'),
+    ]
+
+    _optionalspec = [BinaryDataSpec('data')]
+
+    @property
+    def HashKey(self):
+        try:
+            return "%s:%s:%s:%s" % (
+                self.FrameID, self.frameid, self.url, _bytes2key(self.data))
+        except AttributeError:
+            return "%s:%s:%s" % (self.FrameID, self.frameid, self.url)
+
+    def __eq__(self, other):
+        try:
+            return (self.frameid, self.url, self.data) == other
+        except AttributeError:
+            return (self.frameid, self.url) == other
+
+    __hash__ = FrameOpt.__hash__
+
+
+class POSS(Frame):
+    """Position synchronisation frame
+
+    Attribute:
+
+    * format -- format of the position attribute (frames or milliseconds)
+    * position -- current position of the file
+    """
+
+    _framespec = [
+        ByteSpec('format'),
+        IntegerSpec('position'),
+    ]
+
+    def __pos__(self):
+        return self.position
+
+    def __eq__(self, other):
+        return self.position == other
+
+    __hash__ = Frame.__hash__
+
+
+class UFID(Frame):
+    """Unique file identifier.
+
+    Attributes:
+
+    * owner -- format/type of identifier
+    * data -- identifier
+    """
+
+    _framespec = [
+        Latin1TextSpec('owner'),
+        BinaryDataSpec('data'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.owner)
+
+    def __eq__(s, o):
+        if isinstance(o, UFI):
+            return s.owner == o.owner and s.data == o.data
+        else:
+            return s.data == o
+
+    __hash__ = Frame.__hash__
+
+    def _pprint(self):
+        return "%s=%r" % (self.owner, self.data)
+
+
+@swap_to_string
+class USER(Frame):
+    """Terms of use.
+
+    Attributes:
+
+    * encoding -- text encoding
+    * lang -- ISO three letter language code
+    * text -- licensing terms for the audio
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        StringSpec('lang', 3),
+        EncodedTextSpec('text'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.lang)
+
+    def __bytes__(self):
+        return self.text.encode('utf-8')
+
+    def __str__(self):
+        return self.text
+
+    def __eq__(self, other):
+        return self.text == other
+
+    __hash__ = Frame.__hash__
+
+    def _pprint(self):
+        return "%r=%s" % (self.lang, self.text)
+
+
+@swap_to_string
+class OWNE(Frame):
+    """Ownership frame."""
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        Latin1TextSpec('price'),
+        StringSpec('date', 8),
+        EncodedTextSpec('seller'),
+    ]
+
+    def __bytes__(self):
+        return self.seller.encode('utf-8')
+
+    def __str__(self):
+        return self.seller
+
+    def __eq__(self, other):
+        return self.seller == other
+
+    __hash__ = Frame.__hash__
+
+
+class COMR(FrameOpt):
+    """Commercial frame."""
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        Latin1TextSpec('price'),
+        StringSpec('valid_until', 8),
+        Latin1TextSpec('contact'),
+        ByteSpec('format'),
+        EncodedTextSpec('seller'),
+        EncodedTextSpec('desc'),
+    ]
+
+    _optionalspec = [
+        Latin1TextSpec('mime'),
+        BinaryDataSpec('logo'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, _bytes2key(self._writeData()))
+
+    def __eq__(self, other):
+        return self._writeData() == other._writeData()
+
+    __hash__ = FrameOpt.__hash__
+
+
+@swap_to_string
+class ENCR(Frame):
+    """Encryption method registration.
+
+    The standard does not allow multiple ENCR frames with the same owner
+    or the same method. Mutagen only verifies that the owner is unique.
+    """
+
+    _framespec = [
+        Latin1TextSpec('owner'),
+        ByteSpec('method'),
+        BinaryDataSpec('data'),
+    ]
+
+    @property
+    def HashKey(self):
+        return "%s:%s" % (self.FrameID, self.owner)
+
+    def __bytes__(self):
+        return self.data
+
+    def __eq__(self, other):
+        return self.data == other
+
+    __hash__ = Frame.__hash__
+
+
+@swap_to_string
+class GRID(FrameOpt):
+    """Group identification registration."""
+
+    _framespec = [
+        Latin1TextSpec('owner'),
+        ByteSpec('group'),
+    ]
+
+    _optionalspec = [BinaryDataSpec('data')]
+
+    @property
+    def HashKey(self):
+        return '%s:%s' % (self.FrameID, self.group)
+
+    def __pos__(self):
+        return self.group
+
+    def __bytes__(self):
+        return self.owner.encode('utf-8')
+
+    def __str__(self):
+        return self.owner
+
+    def __eq__(self, other):
+        return self.owner == other or self.group == other
+
+    __hash__ = FrameOpt.__hash__
+
+
+@swap_to_string
+class PRIV(Frame):
+    """Private frame."""
+
+    _framespec = [
+        Latin1TextSpec('owner'),
+        BinaryDataSpec('data'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s:%s' % (
+            self.FrameID, self.owner, _bytes2key(self.data))
+
+    def __bytes__(self):
+        return self.data
+
+    def __eq__(self, other):
+        return self.data == other
+
+    def _pprint(self):
+        return "%s=%r" % (self.owner, self.data)
+
+    __hash__ = Frame.__hash__
+
+
+@swap_to_string
+class SIGN(Frame):
+    """Signature frame."""
+
+    _framespec = [
+        ByteSpec('group'),
+        BinaryDataSpec('sig'),
+    ]
+
+    @property
+    def HashKey(self):
+        return '%s:%s:%s' % (self.FrameID, self.group, _bytes2key(self.sig))
+
+    def __bytes__(self):
+        return self.sig
+
+    def __eq__(self, other):
+        return self.sig == other
+
+    __hash__ = Frame.__hash__
+
+
+class SEEK(Frame):
+    """Seek frame.
+
+    Mutagen does not find tags at seek offsets.
+    """
+
+    _framespec = [IntegerSpec('offset')]
+
+    def __pos__(self):
+        return self.offset
+
+    def __eq__(self, other):
+        return self.offset == other
+
+    __hash__ = Frame.__hash__
+
+
+class ASPI(Frame):
+    """Audio seek point index.
+
+    Attributes: S, L, N, b, and Fi. For the meaning of these, see
+    the ID3v2.4 specification. Fi is a list of integers.
+    """
+    _framespec = [
+        SizedIntegerSpec("S", 4),
+        SizedIntegerSpec("L", 4),
+        SizedIntegerSpec("N", 2),
+        ByteSpec("b"),
+        ASPIIndexSpec("Fi"),
+    ]
+
+    def __eq__(self, other):
+        return self.Fi == other
+
+    __hash__ = Frame.__hash__
+
+
+# ID3v2.2 frames
+class UFI(UFID):
+    "Unique File Identifier"
+
+
+class TT1(TIT1):
+    "Content group description"
+
+
+class TT2(TIT2):
+    "Title"
+
+
+class TT3(TIT3):
+    "Subtitle/Description refinement"
+
+
+class TP1(TPE1):
+    "Lead Artist/Performer/Soloist/Group"
+
+
+class TP2(TPE2):
+    "Band/Orchestra/Accompaniment"
+
+
+class TP3(TPE3):
+    "Conductor"
+
+
+class TP4(TPE4):
+    "Interpreter/Remixer/Modifier"
+
+
+class TCM(TCOM):
+    "Composer"
+
+
+class TXT(TEXT):
+    "Lyricist"
+
+
+class TLA(TLAN):
+    "Audio Language(s)"
+
+
+class TCO(TCON):
+    "Content Type (Genre)"
+
+
+class TAL(TALB):
+    "Album"
+
+
+class TPA(TPOS):
+    "Part of set"
+
+
+class TRK(TRCK):
+    "Track Number"
+
+
+class TRC(TSRC):
+    "International Standard Recording Code (ISRC)"
+
+
+class TYE(TYER):
+    "Year of recording"
+
+
+class TDA(TDAT):
+    "Date of recording (DDMM)"
+
+
+class TIM(TIME):
+    "Time of recording (HHMM)"
+
+
+class TRD(TRDA):
+    "Recording Dates"
+
+
+class TMT(TMED):
+    "Source Media Type"
+
+
+class TFT(TFLT):
+    "File Type"
+
+
+class TBP(TBPM):
+    "Beats per minute"
+
+
+class TCP(TCMP):
+    "iTunes Compilation Flag"
+
+
+class TCR(TCOP):
+    "Copyright (C)"
+
+
+class TPB(TPUB):
+    "Publisher"
+
+
+class TEN(TENC):
+    "Encoder"
+
+
+class TSS(TSSE):
+    "Encoder settings"
+
+
+class TOF(TOFN):
+    "Original Filename"
+
+
+class TLE(TLEN):
+    "Audio Length (ms)"
+
+
+class TSI(TSIZ):
+    "Audio Data size (bytes)"
+
+
+class TDY(TDLY):
+    "Audio Delay (ms)"
+
+
+class TKE(TKEY):
+    "Starting Key"
+
+
+class TOT(TOAL):
+    "Original Album"
+
+
+class TOA(TOPE):
+    "Original Artist/Perfomer"
+
+
+class TOL(TOLY):
+    "Original Lyricist"
+
+
+class TOR(TORY):
+    "Original Release Year"
+
+
+class TXX(TXXX):
+    "User-defined Text"
+
+
+class WAF(WOAF):
+    "Official File Information"
+
+
+class WAR(WOAR):
+    "Official Artist/Performer Information"
+
+
+class WAS(WOAS):
+    "Official Source Information"
+
+
+class WCM(WCOM):
+    "Commercial Information"
+
+
+class WCP(WCOP):
+    "Copyright Information"
+
+
+class WPB(WPUB):
+    "Official Publisher Information"
+
+
+class WXX(WXXX):
+    "User-defined URL"
+
+
+class IPL(IPLS):
+    "Involved people list"
+
+
+class MCI(MCDI):
+    "Binary dump of CD's TOC"
+
+
+class ETC(ETCO):
+    "Event timing codes"
+
+
+class MLL(MLLT):
+    "MPEG location lookup table"
+
+
+class STC(SYTC):
+    "Synced tempo codes"
+
+
+class ULT(USLT):
+    "Unsychronised lyrics/text transcription"
+
+
+class SLT(SYLT):
+    "Synchronised lyrics/text"
+
+
+class COM(COMM):
+    "Comment"
+
+
+# class RVA(RVAD)
+# class EQU(EQUA)
+
+
+class REV(RVRB):
+    "Reverb"
+
+
+class PIC(APIC):
+    """Attached Picture.
+
+    The 'mime' attribute of an ID3v2.2 attached picture must be either
+    'PNG' or 'JPG'.
+    """
+
+    _framespec = [
+        EncodingSpec('encoding'),
+        StringSpec('mime', 3),
+        ByteSpec('type'),
+        EncodedTextSpec('desc'),
+        BinaryDataSpec('data')
+    ]
+
+    def _to_other(self, other):
+        if not isinstance(other, APIC):
+            raise TypeError
+
+        other.encoding = self.encoding
+        other.mime = self.mime
+        other.type = self.type
+        other.desc = self.desc
+        other.data = self.data
+
+
+class GEO(GEOB):
+    "General Encapsulated Object"
+
+
+class CNT(PCNT):
+    "Play counter"
+
+
+class POP(POPM):
+    "Popularimeter"
+
+
+class BUF(RBUF):
+    "Recommended buffer size"
+
+
+class CRM(Frame):
+    """Encrypted meta frame"""
+    _framespec = [Latin1TextSpec('owner'), Latin1TextSpec('desc'),
+                  BinaryDataSpec('data')]
+
+    def __eq__(self, other):
+        return self.data == other
+    __hash__ = Frame.__hash__
+
+
+class CRA(AENC):
+    "Audio encryption"
+
+
+class LNK(LINK):
+    """Linked information"""
+
+    _framespec = [
+        StringSpec('frameid', 3),
+        Latin1TextSpec('url')
+    ]
+
+    _optionalspec = [BinaryDataSpec('data')]
+
+    def _to_other(self, other):
+        if not isinstance(other, LINK):
+            raise TypeError
+
+        other.frameid = self.frameid
+        other.url = self.url
+        if hasattr(self, "data"):
+            other.data = self.data
+
+
+Frames = {}
+"""All supported ID3v2.3/4 frames, keyed by frame name."""
+
+
+Frames_2_2 = {}
+"""All supported ID3v2.2 frames, keyed by frame name."""
+
+
+k, v = None, None
+for k, v in iteritems(globals()):
+    if isinstance(v, type) and issubclass(v, Frame):
+        v.__module__ = "mutagen.id3"
+
+        if len(k) == 3:
+            Frames_2_2[k] = v
+        elif len(k) == 4:
+            Frames[k] = v
+
+try:
+    del k
+    del v
+except NameError:
+    pass
diff --git a/resources/lib/mutagen/id3/_specs.py b/resources/lib/mutagen/id3/_specs.py
new file mode 100644
index 00000000..4358a65d
--- /dev/null
+++ b/resources/lib/mutagen/id3/_specs.py
@@ -0,0 +1,635 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2005  Michael Urman
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+import struct
+from struct import unpack, pack
+
+from .._compat import text_type, chr_, PY3, swap_to_string, string_types, \
+    xrange
+from .._util import total_ordering, decode_terminated, enum, izip
+from ._util import BitPaddedInt
+
+
+@enum
+class PictureType(object):
+    """Enumeration of image types defined by the ID3 standard for the APIC
+    frame, but also reused in WMA/FLAC/VorbisComment.
+    """
+
+    OTHER = 0
+    """Other"""
+
+    FILE_ICON = 1
+    """32x32 pixels 'file icon' (PNG only)"""
+
+    OTHER_FILE_ICON = 2
+    """Other file icon"""
+
+    COVER_FRONT = 3
+    """Cover (front)"""
+
+    COVER_BACK = 4
+    """Cover (back)"""
+
+    LEAFLET_PAGE = 5
+    """Leaflet page"""
+
+    MEDIA = 6
+    """Media (e.g. label side of CD)"""
+
+    LEAD_ARTIST = 7
+    """Lead artist/lead performer/soloist"""
+
+    ARTIST = 8
+    """Artist/performer"""
+
+    CONDUCTOR = 9
+    """Conductor"""
+
+    BAND = 10
+    """Band/Orchestra"""
+
+    COMPOSER = 11
+    """Composer"""
+
+    LYRICIST = 12
+    """Lyricist/text writer"""
+
+    RECORDING_LOCATION = 13
+    """Recording Location"""
+
+    DURING_RECORDING = 14
+    """During recording"""
+
+    DURING_PERFORMANCE = 15
+    """During performance"""
+
+    SCREEN_CAPTURE = 16
+    """Movie/video screen capture"""
+
+    FISH = 17
+    """A bright coloured fish"""
+
+    ILLUSTRATION = 18
+    """Illustration"""
+
+    BAND_LOGOTYPE = 19
+    """Band/artist logotype"""
+
+    PUBLISHER_LOGOTYPE = 20
+    """Publisher/Studio logotype"""
+
+
+class SpecError(Exception):
+    pass
+
+
+class Spec(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def __hash__(self):
+        raise TypeError("Spec objects are unhashable")
+
+    def _validate23(self, frame, value, **kwargs):
+        """Return a possibly modified value which, if written,
+        results in valid id3v2.3 data.
+        """
+
+        return value
+
+    def read(self, frame, data):
+        """Returns the (value, left_data) or raises SpecError"""
+
+        raise NotImplementedError
+
+    def write(self, frame, value):
+        raise NotImplementedError
+
+    def validate(self, frame, value):
+        """Returns the validated data or raises ValueError/TypeError"""
+
+        raise NotImplementedError
+
+
+class ByteSpec(Spec):
+    def read(self, frame, data):
+        return bytearray(data)[0], data[1:]
+
+    def write(self, frame, value):
+        return chr_(value)
+
+    def validate(self, frame, value):
+        if value is not None:
+            chr_(value)
+        return value
+
+
+class IntegerSpec(Spec):
+    def read(self, frame, data):
+        return int(BitPaddedInt(data, bits=8)), b''
+
+    def write(self, frame, value):
+        return BitPaddedInt.to_str(value, bits=8, width=-1)
+
+    def validate(self, frame, value):
+        return value
+
+
+class SizedIntegerSpec(Spec):
+    def __init__(self, name, size):
+        self.name, self.__sz = name, size
+
+    def read(self, frame, data):
+        return int(BitPaddedInt(data[:self.__sz], bits=8)), data[self.__sz:]
+
+    def write(self, frame, value):
+        return BitPaddedInt.to_str(value, bits=8, width=self.__sz)
+
+    def validate(self, frame, value):
+        return value
+
+
+@enum
+class Encoding(object):
+    """Text Encoding"""
+
+    LATIN1 = 0
+    """ISO-8859-1"""
+
+    UTF16 = 1
+    """UTF-16 with BOM"""
+
+    UTF16BE = 2
+    """UTF-16BE without BOM"""
+
+    UTF8 = 3
+    """UTF-8"""
+
+
+class EncodingSpec(ByteSpec):
+
+    def read(self, frame, data):
+        enc, data = super(EncodingSpec, self).read(frame, data)
+        if enc not in (Encoding.LATIN1, Encoding.UTF16, Encoding.UTF16BE,
+                       Encoding.UTF8):
+            raise SpecError('Invalid Encoding: %r' % enc)
+        return enc, data
+
+    def validate(self, frame, value):
+        if value is None:
+            return None
+        if value not in (Encoding.LATIN1, Encoding.UTF16, Encoding.UTF16BE,
+                         Encoding.UTF8):
+            raise ValueError('Invalid Encoding: %r' % value)
+        return value
+
+    def _validate23(self, frame, value, **kwargs):
+        # only 0, 1 are valid in v2.3, default to utf-16
+        if value not in (Encoding.LATIN1, Encoding.UTF16):
+            value = Encoding.UTF16
+        return value
+
+
+class StringSpec(Spec):
+    """A fixed size ASCII only payload."""
+
+    def __init__(self, name, length):
+        super(StringSpec, self).__init__(name)
+        self.len = length
+
+    def read(s, frame, data):
+        chunk = data[:s.len]
+        try:
+            ascii = chunk.decode("ascii")
+        except UnicodeDecodeError:
+            raise SpecError("not ascii")
+        else:
+            if PY3:
+                chunk = ascii
+
+        return chunk, data[s.len:]
+
+    def write(s, frame, value):
+        if value is None:
+            return b'\x00' * s.len
+        else:
+            if PY3:
+                value = value.encode("ascii")
+            return (bytes(value) + b'\x00' * s.len)[:s.len]
+
+    def validate(s, frame, value):
+        if value is None:
+            return None
+
+        if PY3:
+            if not isinstance(value, str):
+                raise TypeError("%s has to be str" % s.name)
+            value.encode("ascii")
+        else:
+            if not isinstance(value, bytes):
+                value = value.encode("ascii")
+
+        if len(value) == s.len:
+            return value
+
+        raise ValueError('Invalid StringSpec[%d] data: %r' % (s.len, value))
+
+
+class BinaryDataSpec(Spec):
+    def read(self, frame, data):
+        return data, b''
+
+    def write(self, frame, value):
+        if value is None:
+            return b""
+        if isinstance(value, bytes):
+            return value
+        value = text_type(value).encode("ascii")
+        return value
+
+    def validate(self, frame, value):
+        if value is None:
+            return None
+
+        if isinstance(value, bytes):
+            return value
+        elif PY3:
+            raise TypeError("%s has to be bytes" % self.name)
+
+        value = text_type(value).encode("ascii")
+        return value
+
+
+class EncodedTextSpec(Spec):
+
+    _encodings = {
+        Encoding.LATIN1: ('latin1', b'\x00'),
+        Encoding.UTF16: ('utf16', b'\x00\x00'),
+        Encoding.UTF16BE: ('utf_16_be', b'\x00\x00'),
+        Encoding.UTF8: ('utf8', b'\x00'),
+    }
+
+    def read(self, frame, data):
+        enc, term = self._encodings[frame.encoding]
+        try:
+            # allow missing termination
+            return decode_terminated(data, enc, strict=False)
+        except ValueError:
+            # utf-16 termination with missing BOM, or single NULL
+            if not data[:len(term)].strip(b"\x00"):
+                return u"", data[len(term):]
+
+            # utf-16 data with single NULL, see issue 169
+            try:
+                return decode_terminated(data + b"\x00", enc)
+            except ValueError:
+                raise SpecError("Decoding error")
+
+    def write(self, frame, value):
+        enc, term = self._encodings[frame.encoding]
+        return value.encode(enc) + term
+
+    def validate(self, frame, value):
+        return text_type(value)
+
+
+class MultiSpec(Spec):
+    def __init__(self, name, *specs, **kw):
+        super(MultiSpec, self).__init__(name)
+        self.specs = specs
+        self.sep = kw.get('sep')
+
+    def read(self, frame, data):
+        values = []
+        while data:
+            record = []
+            for spec in self.specs:
+                value, data = spec.read(frame, data)
+                record.append(value)
+            if len(self.specs) != 1:
+                values.append(record)
+            else:
+                values.append(record[0])
+        return values, data
+
+    def write(self, frame, value):
+        data = []
+        if len(self.specs) == 1:
+            for v in value:
+                data.append(self.specs[0].write(frame, v))
+        else:
+            for record in value:
+                for v, s in izip(record, self.specs):
+                    data.append(s.write(frame, v))
+        return b''.join(data)
+
+    def validate(self, frame, value):
+        if value is None:
+            return []
+        if self.sep and isinstance(value, string_types):
+            value = value.split(self.sep)
+        if isinstance(value, list):
+            if len(self.specs) == 1:
+                return [self.specs[0].validate(frame, v) for v in value]
+            else:
+                return [
+                    [s.validate(frame, v) for (v, s) in izip(val, self.specs)]
+                    for val in value]
+        raise ValueError('Invalid MultiSpec data: %r' % value)
+
+    def _validate23(self, frame, value, **kwargs):
+        if len(self.specs) != 1:
+            return [[s._validate23(frame, v, **kwargs)
+                     for (v, s) in izip(val, self.specs)]
+                    for val in value]
+
+        spec = self.specs[0]
+
+        # Merge single text spec multispecs only.
+        # (TimeStampSpec beeing the exception, but it's not a valid v2.3 frame)
+        if not isinstance(spec, EncodedTextSpec) or \
+                isinstance(spec, TimeStampSpec):
+            return value
+
+        value = [spec._validate23(frame, v, **kwargs) for v in value]
+        if kwargs.get("sep") is not None:
+            return [spec.validate(frame, kwargs["sep"].join(value))]
+        return value
+
+
+class EncodedNumericTextSpec(EncodedTextSpec):
+    pass
+
+
+class EncodedNumericPartTextSpec(EncodedTextSpec):
+    pass
+
+
+class Latin1TextSpec(EncodedTextSpec):
+    def read(self, frame, data):
+        if b'\x00' in data:
+            data, ret = data.split(b'\x00', 1)
+        else:
+            ret = b''
+        return data.decode('latin1'), ret
+
+    def write(self, data, value):
+        return value.encode('latin1') + b'\x00'
+
+    def validate(self, frame, value):
+        return text_type(value)
+
+
+@swap_to_string
+@total_ordering
+class ID3TimeStamp(object):
+    """A time stamp in ID3v2 format.
+
+    This is a restricted form of the ISO 8601 standard; time stamps
+    take the form of:
+        YYYY-MM-DD HH:MM:SS
+    Or some partial form (YYYY-MM-DD HH, YYYY, etc.).
+
+    The 'text' attribute contains the raw text data of the time stamp.
+    """
+
+    import re
+
+    def __init__(self, text):
+        if isinstance(text, ID3TimeStamp):
+            text = text.text
+        elif not isinstance(text, text_type):
+            if PY3:
+                raise TypeError("not a str")
+            text = text.decode("utf-8")
+
+        self.text = text
+
+    __formats = ['%04d'] + ['%02d'] * 5
+    __seps = ['-', '-', ' ', ':', ':', 'x']
+
+    def get_text(self):
+        parts = [self.year, self.month, self.day,
+                 self.hour, self.minute, self.second]
+        pieces = []
+        for i, part in enumerate(parts):
+            if part is None:
+                break
+            pieces.append(self.__formats[i] % part + self.__seps[i])
+        return u''.join(pieces)[:-1]
+
+    def set_text(self, text, splitre=re.compile('[-T:/.]|\s+')):
+        year, month, day, hour, minute, second = \
+            splitre.split(text + ':::::')[:6]
+        for a in 'year month day hour minute second'.split():
+            try:
+                v = int(locals()[a])
+            except ValueError:
+                v = None
+            setattr(self, a, v)
+
+    text = property(get_text, set_text, doc="ID3v2.4 date and time.")
+
+    def __str__(self):
+        return self.text
+
+    def __bytes__(self):
+        return self.text.encode("utf-8")
+
+    def __repr__(self):
+        return repr(self.text)
+
+    def __eq__(self, other):
+        return self.text == other.text
+
+    def __lt__(self, other):
+        return self.text < other.text
+
+    __hash__ = object.__hash__
+
+    def encode(self, *args):
+        return self.text.encode(*args)
+
+
+class TimeStampSpec(EncodedTextSpec):
+    def read(self, frame, data):
+        value, data = super(TimeStampSpec, self).read(frame, data)
+        return self.validate(frame, value), data
+
+    def write(self, frame, data):
+        return super(TimeStampSpec, self).write(frame,
+                                                data.text.replace(' ', 'T'))
+
+    def validate(self, frame, value):
+        try:
+            return ID3TimeStamp(value)
+        except TypeError:
+            raise ValueError("Invalid ID3TimeStamp: %r" % value)
+
+
+class ChannelSpec(ByteSpec):
+    (OTHER, MASTER, FRONTRIGHT, FRONTLEFT, BACKRIGHT, BACKLEFT, FRONTCENTRE,
+     BACKCENTRE, SUBWOOFER) = xrange(9)
+
+
+class VolumeAdjustmentSpec(Spec):
+    def read(self, frame, data):
+        value, = unpack('>h', data[0:2])
+        return value / 512.0, data[2:]
+
+    def write(self, frame, value):
+        number = int(round(value * 512))
+        # pack only fails in 2.7, do it manually in 2.6
+        if not -32768 <= number <= 32767:
+            raise SpecError("not in range")
+        return pack('>h', number)
+
+    def validate(self, frame, value):
+        if value is not None:
+            try:
+                self.write(frame, value)
+            except SpecError:
+                raise ValueError("out of range")
+        return value
+
+
+class VolumePeakSpec(Spec):
+    def read(self, frame, data):
+        # http://bugs.xmms.org/attachment.cgi?id=113&action=view
+        peak = 0
+        data_array = bytearray(data)
+        bits = data_array[0]
+        vol_bytes = min(4, (bits + 7) >> 3)
+        # not enough frame data
+        if vol_bytes + 1 > len(data):
+            raise SpecError("not enough frame data")
+        shift = ((8 - (bits & 7)) & 7) + (4 - vol_bytes) * 8
+        for i in xrange(1, vol_bytes + 1):
+            peak *= 256
+            peak += data_array[i]
+        peak *= 2 ** shift
+        return (float(peak) / (2 ** 31 - 1)), data[1 + vol_bytes:]
+
+    def write(self, frame, value):
+        number = int(round(value * 32768))
+        # pack only fails in 2.7, do it manually in 2.6
+        if not 0 <= number <= 65535:
+            raise SpecError("not in range")
+        # always write as 16 bits for sanity.
+        return b"\x10" + pack('>H', number)
+
+    def validate(self, frame, value):
+        if value is not None:
+            try:
+                self.write(frame, value)
+            except SpecError:
+                raise ValueError("out of range")
+        return value
+
+
+class SynchronizedTextSpec(EncodedTextSpec):
+    def read(self, frame, data):
+        texts = []
+        encoding, term = self._encodings[frame.encoding]
+        while data:
+            try:
+                value, data = decode_terminated(data, encoding)
+            except ValueError:
+                raise SpecError("decoding error")
+
+            if len(data) < 4:
+                raise SpecError("not enough data")
+            time, = struct.unpack(">I", data[:4])
+
+            texts.append((value, time))
+            data = data[4:]
+        return texts, b""
+
+    def write(self, frame, value):
+        data = []
+        encoding, term = self._encodings[frame.encoding]
+        for text, time in value:
+            text = text.encode(encoding) + term
+            data.append(text + struct.pack(">I", time))
+        return b"".join(data)
+
+    def validate(self, frame, value):
+        return value
+
+
+class KeyEventSpec(Spec):
+    def read(self, frame, data):
+        events = []
+        while len(data) >= 5:
+            events.append(struct.unpack(">bI", data[:5]))
+            data = data[5:]
+        return events, data
+
+    def write(self, frame, value):
+        return b"".join(struct.pack(">bI", *event) for event in value)
+
+    def validate(self, frame, value):
+        return value
+
+
+class VolumeAdjustmentsSpec(Spec):
+    # Not to be confused with VolumeAdjustmentSpec.
+    def read(self, frame, data):
+        adjustments = {}
+        while len(data) >= 4:
+            freq, adj = struct.unpack(">Hh", data[:4])
+            data = data[4:]
+            freq /= 2.0
+            adj /= 512.0
+            adjustments[freq] = adj
+        adjustments = sorted(adjustments.items())
+        return adjustments, data
+
+    def write(self, frame, value):
+        value.sort()
+        return b"".join(struct.pack(">Hh", int(freq * 2), int(adj * 512))
+                        for (freq, adj) in value)
+
+    def validate(self, frame, value):
+        return value
+
+
+class ASPIIndexSpec(Spec):
+    def read(self, frame, data):
+        if frame.b == 16:
+            format = "H"
+            size = 2
+        elif frame.b == 8:
+            format = "B"
+            size = 1
+        else:
+            raise SpecError("invalid bit count in ASPI (%d)" % frame.b)
+
+        indexes = data[:frame.N * size]
+        data = data[frame.N * size:]
+        try:
+            return list(struct.unpack(">" + format * frame.N, indexes)), data
+        except struct.error as e:
+            raise SpecError(e)
+
+    def write(self, frame, values):
+        if frame.b == 16:
+            format = "H"
+        elif frame.b == 8:
+            format = "B"
+        else:
+            raise SpecError("frame.b must be 8 or 16")
+        try:
+            return struct.pack(">" + format * frame.N, *values)
+        except struct.error as e:
+            raise SpecError(e)
+
+    def validate(self, frame, values):
+        return values
diff --git a/resources/lib/mutagen/id3/_util.py b/resources/lib/mutagen/id3/_util.py
new file mode 100644
index 00000000..29f7241d
--- /dev/null
+++ b/resources/lib/mutagen/id3/_util.py
@@ -0,0 +1,167 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2005  Michael Urman
+#               2013  Christoph Reiter
+#               2014  Ben Ockmore
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+from .._compat import long_, integer_types, PY3
+from .._util import MutagenError
+
+
+class error(MutagenError):
+    pass
+
+
+class ID3NoHeaderError(error, ValueError):
+    pass
+
+
+class ID3UnsupportedVersionError(error, NotImplementedError):
+    pass
+
+
+class ID3EncryptionUnsupportedError(error, NotImplementedError):
+    pass
+
+
+class ID3JunkFrameError(error, ValueError):
+    pass
+
+
+class unsynch(object):
+    @staticmethod
+    def decode(value):
+        fragments = bytearray(value).split(b'\xff')
+        if len(fragments) > 1 and not fragments[-1]:
+            raise ValueError('string ended unsafe')
+
+        for f in fragments[1:]:
+            if (not f) or (f[0] >= 0xE0):
+                raise ValueError('invalid sync-safe string')
+
+            if f[0] == 0x00:
+                del f[0]
+
+        return bytes(bytearray(b'\xff').join(fragments))
+
+    @staticmethod
+    def encode(value):
+        fragments = bytearray(value).split(b'\xff')
+        for f in fragments[1:]:
+            if (not f) or (f[0] >= 0xE0) or (f[0] == 0x00):
+                f.insert(0, 0x00)
+        return bytes(bytearray(b'\xff').join(fragments))
+
+
+class _BitPaddedMixin(object):
+
+    def as_str(self, width=4, minwidth=4):
+        return self.to_str(self, self.bits, self.bigendian, width, minwidth)
+
+    @staticmethod
+    def to_str(value, bits=7, bigendian=True, width=4, minwidth=4):
+        mask = (1 << bits) - 1
+
+        if width != -1:
+            index = 0
+            bytes_ = bytearray(width)
+            try:
+                while value:
+                    bytes_[index] = value & mask
+                    value >>= bits
+                    index += 1
+            except IndexError:
+                raise ValueError('Value too wide (>%d bytes)' % width)
+        else:
+            # PCNT and POPM use growing integers
+            # of at least 4 bytes (=minwidth) as counters.
+            bytes_ = bytearray()
+            append = bytes_.append
+            while value:
+                append(value & mask)
+                value >>= bits
+            bytes_ = bytes_.ljust(minwidth, b"\x00")
+
+        if bigendian:
+            bytes_.reverse()
+        return bytes(bytes_)
+
+    @staticmethod
+    def has_valid_padding(value, bits=7):
+        """Whether the padding bits are all zero"""
+
+        assert bits <= 8
+
+        mask = (((1 << (8 - bits)) - 1) << bits)
+
+        if isinstance(value, integer_types):
+            while value:
+                if value & mask:
+                    return False
+                value >>= 8
+        elif isinstance(value, bytes):
+            for byte in bytearray(value):
+                if byte & mask:
+                    return False
+        else:
+            raise TypeError
+
+        return True
+
+
+class BitPaddedInt(int, _BitPaddedMixin):
+
+    def __new__(cls, value, bits=7, bigendian=True):
+
+        mask = (1 << (bits)) - 1
+        numeric_value = 0
+        shift = 0
+
+        if isinstance(value, integer_types):
+            while value:
+                numeric_value += (value & mask) << shift
+                value >>= 8
+                shift += bits
+        elif isinstance(value, bytes):
+            if bigendian:
+                value = reversed(value)
+            for byte in bytearray(value):
+                numeric_value += (byte & mask) << shift
+                shift += bits
+        else:
+            raise TypeError
+
+        if isinstance(numeric_value, int):
+            self = int.__new__(BitPaddedInt, numeric_value)
+        else:
+            self = long_.__new__(BitPaddedLong, numeric_value)
+
+        self.bits = bits
+        self.bigendian = bigendian
+        return self
+
+if PY3:
+    BitPaddedLong = BitPaddedInt
+else:
+    class BitPaddedLong(long_, _BitPaddedMixin):
+        pass
+
+
+class ID3BadUnsynchData(error, ValueError):
+    """Deprecated"""
+
+
+class ID3BadCompressedData(error, ValueError):
+    """Deprecated"""
+
+
+class ID3TagError(error, ValueError):
+    """Deprecated"""
+
+
+class ID3Warning(error, UserWarning):
+    """Deprecated"""
diff --git a/resources/lib/mutagen/m4a.py b/resources/lib/mutagen/m4a.py
new file mode 100644
index 00000000..5730ace3
--- /dev/null
+++ b/resources/lib/mutagen/m4a.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+# Copyright 2006 Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""
+since 1.9: mutagen.m4a is deprecated; use mutagen.mp4 instead.
+since 1.31: mutagen.m4a will no longer work; any operation that could fail
+            will fail now.
+"""
+
+import warnings
+
+from mutagen import FileType, Metadata, StreamInfo
+from ._util import DictProxy, MutagenError
+
+warnings.warn(
+    "mutagen.m4a is deprecated; use mutagen.mp4 instead.",
+    DeprecationWarning)
+
+
+class error(IOError, MutagenError):
+    pass
+
+
+class M4AMetadataError(error):
+    pass
+
+
+class M4AStreamInfoError(error):
+    pass
+
+
+class M4AMetadataValueError(ValueError, M4AMetadataError):
+    pass
+
+
+__all__ = ['M4A', 'Open', 'delete', 'M4ACover']
+
+
+class M4ACover(bytes):
+
+    FORMAT_JPEG = 0x0D
+    FORMAT_PNG = 0x0E
+
+    def __new__(cls, data, imageformat=None):
+        self = bytes.__new__(cls, data)
+        if imageformat is None:
+            imageformat = M4ACover.FORMAT_JPEG
+        self.imageformat = imageformat
+        return self
+
+
+class M4ATags(DictProxy, Metadata):
+
+    def load(self, atoms, fileobj):
+        raise error("deprecated")
+
+    def save(self, filename):
+        raise error("deprecated")
+
+    def delete(self, filename):
+        raise error("deprecated")
+
+    def pprint(self):
+        return u""
+
+
+class M4AInfo(StreamInfo):
+
+    bitrate = 0
+
+    def __init__(self, atoms, fileobj):
+        raise error("deprecated")
+
+    def pprint(self):
+        return u""
+
+
+class M4A(FileType):
+
+    _mimes = ["audio/mp4", "audio/x-m4a", "audio/mpeg4", "audio/aac"]
+
+    def load(self, filename):
+        raise error("deprecated")
+
+    def add_tags(self):
+        self.tags = M4ATags()
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return 0
+
+
+Open = M4A
+
+
+def delete(filename):
+    raise error("deprecated")
diff --git a/resources/lib/mutagen/monkeysaudio.py b/resources/lib/mutagen/monkeysaudio.py
new file mode 100644
index 00000000..0e29273f
--- /dev/null
+++ b/resources/lib/mutagen/monkeysaudio.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Lukas Lalinsky
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Monkey's Audio streams with APEv2 tags.
+
+Monkey's Audio is a very efficient lossless audio compressor developed
+by Matt Ashland.
+
+For more information, see http://www.monkeysaudio.com/.
+"""
+
+__all__ = ["MonkeysAudio", "Open", "delete"]
+
+import struct
+
+from ._compat import endswith
+from mutagen import StreamInfo
+from mutagen.apev2 import APEv2File, error, delete
+from mutagen._util import cdata
+
+
+class MonkeysAudioHeaderError(error):
+    pass
+
+
+class MonkeysAudioInfo(StreamInfo):
+    """Monkey's Audio stream information.
+
+    Attributes:
+
+    * channels -- number of audio channels
+    * length -- file length in seconds, as a float
+    * sample_rate -- audio sampling rate in Hz
+    * bits_per_sample -- bits per sample
+    * version -- Monkey's Audio stream version, as a float (eg: 3.99)
+    """
+
+    def __init__(self, fileobj):
+        header = fileobj.read(76)
+        if len(header) != 76 or not header.startswith(b"MAC "):
+            raise MonkeysAudioHeaderError("not a Monkey's Audio file")
+        self.version = cdata.ushort_le(header[4:6])
+        if self.version >= 3980:
+            (blocks_per_frame, final_frame_blocks, total_frames,
+             self.bits_per_sample, self.channels,
+             self.sample_rate) = struct.unpack("<IIIHHI", header[56:76])
+        else:
+            compression_level = cdata.ushort_le(header[6:8])
+            self.channels, self.sample_rate = struct.unpack(
+                "<HI", header[10:16])
+            total_frames, final_frame_blocks = struct.unpack(
+                "<II", header[24:32])
+            if self.version >= 3950:
+                blocks_per_frame = 73728 * 4
+            elif self.version >= 3900 or (self.version >= 3800 and
+                                          compression_level == 4):
+                blocks_per_frame = 73728
+            else:
+                blocks_per_frame = 9216
+        self.version /= 1000.0
+        self.length = 0.0
+        if (self.sample_rate != 0) and (total_frames > 0):
+            total_blocks = ((total_frames - 1) * blocks_per_frame +
+                            final_frame_blocks)
+            self.length = float(total_blocks) / self.sample_rate
+
+    def pprint(self):
+        return u"Monkey's Audio %.2f, %.2f seconds, %d Hz" % (
+            self.version, self.length, self.sample_rate)
+
+
+class MonkeysAudio(APEv2File):
+    _Info = MonkeysAudioInfo
+    _mimes = ["audio/ape", "audio/x-ape"]
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return header.startswith(b"MAC ") + endswith(filename.lower(), ".ape")
+
+
+Open = MonkeysAudio
diff --git a/resources/lib/mutagen/mp3.py b/resources/lib/mutagen/mp3.py
new file mode 100644
index 00000000..afb600cf
--- /dev/null
+++ b/resources/lib/mutagen/mp3.py
@@ -0,0 +1,362 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""MPEG audio stream information and tags."""
+
+import os
+import struct
+
+from ._compat import endswith, xrange
+from ._mp3util import XingHeader, XingHeaderError, VBRIHeader, VBRIHeaderError
+from mutagen import StreamInfo
+from mutagen._util import MutagenError, enum
+from mutagen.id3 import ID3FileType, BitPaddedInt, delete
+
+__all__ = ["MP3", "Open", "delete", "MP3"]
+
+
+class error(RuntimeError, MutagenError):
+    pass
+
+
+class HeaderNotFoundError(error, IOError):
+    pass
+
+
+class InvalidMPEGHeader(error, IOError):
+    pass
+
+
+@enum
+class BitrateMode(object):
+
+    UNKNOWN = 0
+    """Probably a CBR file, but not sure"""
+
+    CBR = 1
+    """Constant Bitrate"""
+
+    VBR = 2
+    """Variable Bitrate"""
+
+    ABR = 3
+    """Average Bitrate (a variant of VBR)"""
+
+
+def _guess_xing_bitrate_mode(xing):
+
+    if xing.lame_header:
+        lame = xing.lame_header
+        if lame.vbr_method in (1, 8):
+            return BitrateMode.CBR
+        elif lame.vbr_method in (2, 9):
+            return BitrateMode.ABR
+        elif lame.vbr_method in (3, 4, 5, 6):
+            return BitrateMode.VBR
+        # everything else undefined, continue guessing
+
+    # info tags get only written by lame for cbr files
+    if xing.is_info:
+        return BitrateMode.CBR
+
+    # older lame and non-lame with some variant of vbr
+    if xing.vbr_scale != -1 or xing.lame_version:
+        return BitrateMode.VBR
+
+    return BitrateMode.UNKNOWN
+
+
+# Mode values.
+STEREO, JOINTSTEREO, DUALCHANNEL, MONO = xrange(4)
+
+
+class MPEGInfo(StreamInfo):
+    """MPEG audio stream information
+
+    Parse information about an MPEG audio file. This also reads the
+    Xing VBR header format.
+
+    This code was implemented based on the format documentation at
+    http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm.
+
+    Useful attributes:
+
+    * length -- audio length, in seconds
+    * channels -- number of audio channels
+    * bitrate -- audio bitrate, in bits per second
+    * sketchy -- if true, the file may not be valid MPEG audio
+    * encoder_info -- a string containing encoder name and possibly version.
+                      In case a lame tag is present this will start with
+                      ``"LAME "``, if unknown it is empty, otherwise the
+                      text format is undefined.
+    * bitrate_mode -- a :class:`BitrateMode`
+
+    * track_gain -- replaygain track gain (89db) or None
+    * track_peak -- replaygain track peak or None
+    * album_gain -- replaygain album gain (89db) or None
+
+    Useless attributes:
+
+    * version -- MPEG version (1, 2, 2.5)
+    * layer -- 1, 2, or 3
+    * mode -- One of STEREO, JOINTSTEREO, DUALCHANNEL, or MONO (0-3)
+    * protected -- whether or not the file is "protected"
+    * padding -- whether or not audio frames are padded
+    * sample_rate -- audio sample rate, in Hz
+    """
+
+    # Map (version, layer) tuples to bitrates.
+    __BITRATE = {
+        (1, 1): [0, 32, 64, 96, 128, 160, 192, 224,
+                 256, 288, 320, 352, 384, 416, 448],
+        (1, 2): [0, 32, 48, 56, 64, 80, 96, 112, 128,
+                 160, 192, 224, 256, 320, 384],
+        (1, 3): [0, 32, 40, 48, 56, 64, 80, 96, 112,
+                 128, 160, 192, 224, 256, 320],
+        (2, 1): [0, 32, 48, 56, 64, 80, 96, 112, 128,
+                 144, 160, 176, 192, 224, 256],
+        (2, 2): [0, 8, 16, 24, 32, 40, 48, 56, 64,
+                 80, 96, 112, 128, 144, 160],
+    }
+
+    __BITRATE[(2, 3)] = __BITRATE[(2, 2)]
+    for i in xrange(1, 4):
+        __BITRATE[(2.5, i)] = __BITRATE[(2, i)]
+
+    # Map version to sample rates.
+    __RATES = {
+        1: [44100, 48000, 32000],
+        2: [22050, 24000, 16000],
+        2.5: [11025, 12000, 8000]
+    }
+
+    sketchy = False
+    encoder_info = u""
+    bitrate_mode = BitrateMode.UNKNOWN
+    track_gain = track_peak = album_gain = album_peak = None
+
+    def __init__(self, fileobj, offset=None):
+        """Parse MPEG stream information from a file-like object.
+
+        If an offset argument is given, it is used to start looking
+        for stream information and Xing headers; otherwise, ID3v2 tags
+        will be skipped automatically. A correct offset can make
+        loading files significantly faster.
+        """
+
+        try:
+            size = os.path.getsize(fileobj.name)
+        except (IOError, OSError, AttributeError):
+            fileobj.seek(0, 2)
+            size = fileobj.tell()
+
+        # If we don't get an offset, try to skip an ID3v2 tag.
+        if offset is None:
+            fileobj.seek(0, 0)
+            idata = fileobj.read(10)
+            try:
+                id3, insize = struct.unpack('>3sxxx4s', idata)
+            except struct.error:
+                id3, insize = b'', 0
+            insize = BitPaddedInt(insize)
+            if id3 == b'ID3' and insize > 0:
+                offset = insize + 10
+            else:
+                offset = 0
+
+        # Try to find two valid headers (meaning, very likely MPEG data)
+        # at the given offset, 30% through the file, 60% through the file,
+        # and 90% through the file.
+        for i in [offset, 0.3 * size, 0.6 * size, 0.9 * size]:
+            try:
+                self.__try(fileobj, int(i), size - offset)
+            except error:
+                pass
+            else:
+                break
+        # If we can't find any two consecutive frames, try to find just
+        # one frame back at the original offset given.
+        else:
+            self.__try(fileobj, offset, size - offset, False)
+            self.sketchy = True
+
+    def __try(self, fileobj, offset, real_size, check_second=True):
+        # This is going to be one really long function; bear with it,
+        # because there's not really a sane point to cut it up.
+        fileobj.seek(offset, 0)
+
+        # We "know" we have an MPEG file if we find two frames that look like
+        # valid MPEG data. If we can't find them in 32k of reads, something
+        # is horribly wrong (the longest frame can only be about 4k). This
+        # is assuming the offset didn't lie.
+        data = fileobj.read(32768)
+
+        frame_1 = data.find(b"\xff")
+        while 0 <= frame_1 <= (len(data) - 4):
+            frame_data = struct.unpack(">I", data[frame_1:frame_1 + 4])[0]
+            if ((frame_data >> 16) & 0xE0) != 0xE0:
+                frame_1 = data.find(b"\xff", frame_1 + 2)
+            else:
+                version = (frame_data >> 19) & 0x3
+                layer = (frame_data >> 17) & 0x3
+                protection = (frame_data >> 16) & 0x1
+                bitrate = (frame_data >> 12) & 0xF
+                sample_rate = (frame_data >> 10) & 0x3
+                padding = (frame_data >> 9) & 0x1
+                # private = (frame_data >> 8) & 0x1
+                self.mode = (frame_data >> 6) & 0x3
+                # mode_extension = (frame_data >> 4) & 0x3
+                # copyright = (frame_data >> 3) & 0x1
+                # original = (frame_data >> 2) & 0x1
+                # emphasis = (frame_data >> 0) & 0x3
+                if (version == 1 or layer == 0 or sample_rate == 0x3 or
+                        bitrate == 0 or bitrate == 0xF):
+                    frame_1 = data.find(b"\xff", frame_1 + 2)
+                else:
+                    break
+        else:
+            raise HeaderNotFoundError("can't sync to an MPEG frame")
+
+        self.channels = 1 if self.mode == MONO else 2
+
+        # There is a serious problem here, which is that many flags
+        # in an MPEG header are backwards.
+        self.version = [2.5, None, 2, 1][version]
+        self.layer = 4 - layer
+        self.protected = not protection
+        self.padding = bool(padding)
+
+        self.bitrate = self.__BITRATE[(self.version, self.layer)][bitrate]
+        self.bitrate *= 1000
+        self.sample_rate = self.__RATES[self.version][sample_rate]
+
+        if self.layer == 1:
+            frame_length = (
+                (12 * self.bitrate // self.sample_rate) + padding) * 4
+            frame_size = 384
+        elif self.version >= 2 and self.layer == 3:
+            frame_length = (72 * self.bitrate // self.sample_rate) + padding
+            frame_size = 576
+        else:
+            frame_length = (144 * self.bitrate // self.sample_rate) + padding
+            frame_size = 1152
+
+        if check_second:
+            possible = int(frame_1 + frame_length)
+            if possible > len(data) + 4:
+                raise HeaderNotFoundError("can't sync to second MPEG frame")
+            try:
+                frame_data = struct.unpack(
+                    ">H", data[possible:possible + 2])[0]
+            except struct.error:
+                raise HeaderNotFoundError("can't sync to second MPEG frame")
+            if (frame_data & 0xFFE0) != 0xFFE0:
+                raise HeaderNotFoundError("can't sync to second MPEG frame")
+
+        self.length = 8 * real_size / float(self.bitrate)
+
+        # Try to find/parse the Xing header, which trumps the above length
+        # and bitrate calculation.
+
+        if self.layer != 3:
+            return
+
+        # Xing
+        xing_offset = XingHeader.get_offset(self)
+        fileobj.seek(offset + frame_1 + xing_offset, 0)
+        try:
+            xing = XingHeader(fileobj)
+        except XingHeaderError:
+            pass
+        else:
+            lame = xing.lame_header
+            self.sketchy = False
+            self.bitrate_mode = _guess_xing_bitrate_mode(xing)
+            if xing.frames != -1:
+                samples = frame_size * xing.frames
+                if lame is not None:
+                    samples -= lame.encoder_delay_start
+                    samples -= lame.encoder_padding_end
+                self.length = float(samples) / self.sample_rate
+            if xing.bytes != -1 and self.length:
+                self.bitrate = int((xing.bytes * 8) / self.length)
+            if xing.lame_version:
+                self.encoder_info = u"LAME %s" % xing.lame_version
+            if lame is not None:
+                self.track_gain = lame.track_gain_adjustment
+                self.track_peak = lame.track_peak
+                self.album_gain = lame.album_gain_adjustment
+            return
+
+        # VBRI
+        vbri_offset = VBRIHeader.get_offset(self)
+        fileobj.seek(offset + frame_1 + vbri_offset, 0)
+        try:
+            vbri = VBRIHeader(fileobj)
+        except VBRIHeaderError:
+            pass
+        else:
+            self.bitrate_mode = BitrateMode.VBR
+            self.encoder_info = u"FhG"
+            self.sketchy = False
+            self.length = float(frame_size * vbri.frames) / self.sample_rate
+            if self.length:
+                self.bitrate = int((vbri.bytes * 8) / self.length)
+
+    def pprint(self):
+        info = str(self.bitrate_mode).split(".", 1)[-1]
+        if self.bitrate_mode == BitrateMode.UNKNOWN:
+            info = u"CBR?"
+        if self.encoder_info:
+            info += ", %s" % self.encoder_info
+        s = u"MPEG %s layer %d, %d bps (%s), %s Hz, %d chn, %.2f seconds" % (
+            self.version, self.layer, self.bitrate, info,
+            self.sample_rate, self.channels, self.length)
+        if self.sketchy:
+            s += u" (sketchy)"
+        return s
+
+
+class MP3(ID3FileType):
+    """An MPEG audio (usually MPEG-1 Layer 3) file.
+
+    :ivar info: :class:`MPEGInfo`
+    :ivar tags: :class:`ID3 <mutagen.id3.ID3>`
+    """
+
+    _Info = MPEGInfo
+
+    _mimes = ["audio/mpeg", "audio/mpg", "audio/x-mpeg"]
+
+    @property
+    def mime(self):
+        l = self.info.layer
+        return ["audio/mp%d" % l, "audio/x-mp%d" % l] + super(MP3, self).mime
+
+    @staticmethod
+    def score(filename, fileobj, header_data):
+        filename = filename.lower()
+
+        return (header_data.startswith(b"ID3") * 2 +
+                endswith(filename, b".mp3") +
+                endswith(filename, b".mp2") + endswith(filename, b".mpg") +
+                endswith(filename, b".mpeg"))
+
+
+Open = MP3
+
+
+class EasyMP3(MP3):
+    """Like MP3, but uses EasyID3 for tags.
+
+    :ivar info: :class:`MPEGInfo`
+    :ivar tags: :class:`EasyID3 <mutagen.easyid3.EasyID3>`
+    """
+
+    from mutagen.easyid3 import EasyID3 as ID3
+    ID3 = ID3
diff --git a/resources/lib/mutagen/mp4/__init__.py b/resources/lib/mutagen/mp4/__init__.py
new file mode 100644
index 00000000..bc242ee8
--- /dev/null
+++ b/resources/lib/mutagen/mp4/__init__.py
@@ -0,0 +1,1010 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Read and write MPEG-4 audio files with iTunes metadata.
+
+This module will read MPEG-4 audio information and metadata,
+as found in Apple's MP4 (aka M4A, M4B, M4P) files.
+
+There is no official specification for this format. The source code
+for TagLib, FAAD, and various MPEG specifications at
+
+* http://developer.apple.com/documentation/QuickTime/QTFF/
+* http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
+* http://standards.iso.org/ittf/PubliclyAvailableStandards/\
+c041828_ISO_IEC_14496-12_2005(E).zip
+* http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
+
+were all consulted.
+"""
+
+import struct
+import sys
+
+from mutagen import FileType, Metadata, StreamInfo, PaddingInfo
+from mutagen._constants import GENRES
+from mutagen._util import (cdata, insert_bytes, DictProxy, MutagenError,
+                           hashable, enum, get_size, resize_bytes)
+from mutagen._compat import (reraise, PY2, string_types, text_type, chr_,
+                             iteritems, PY3, cBytesIO, izip, xrange)
+from ._atom import Atoms, Atom, AtomError
+from ._util import parse_full_atom
+from ._as_entry import AudioSampleEntry, ASEntryError
+
+
+class error(IOError, MutagenError):
+    pass
+
+
+class MP4MetadataError(error):
+    pass
+
+
+class MP4StreamInfoError(error):
+    pass
+
+
+class MP4MetadataValueError(ValueError, MP4MetadataError):
+    pass
+
+
+__all__ = ['MP4', 'Open', 'delete', 'MP4Cover', 'MP4FreeForm', 'AtomDataType']
+
+
+@enum
+class AtomDataType(object):
+    """Enum for `dataformat` attribute of MP4FreeForm.
+
+    .. versionadded:: 1.25
+    """
+
+    IMPLICIT = 0
+    """for use with tags for which no type needs to be indicated because
+       only one type is allowed"""
+
+    UTF8 = 1
+    """without any count or null terminator"""
+
+    UTF16 = 2
+    """also known as UTF-16BE"""
+
+    SJIS = 3
+    """deprecated unless it is needed for special Japanese characters"""
+
+    HTML = 6
+    """the HTML file header specifies which HTML version"""
+
+    XML = 7
+    """the XML header must identify the DTD or schemas"""
+
+    UUID = 8
+    """also known as GUID; stored as 16 bytes in binary (valid as an ID)"""
+
+    ISRC = 9
+    """stored as UTF-8 text (valid as an ID)"""
+
+    MI3P = 10
+    """stored as UTF-8 text (valid as an ID)"""
+
+    GIF = 12
+    """(deprecated) a GIF image"""
+
+    JPEG = 13
+    """a JPEG image"""
+
+    PNG = 14
+    """PNG image"""
+
+    URL = 15
+    """absolute, in UTF-8 characters"""
+
+    DURATION = 16
+    """in milliseconds, 32-bit integer"""
+
+    DATETIME = 17
+    """in UTC, counting seconds since midnight, January 1, 1904;
+       32 or 64-bits"""
+
+    GENRES = 18
+    """a list of enumerated values"""
+
+    INTEGER = 21
+    """a signed big-endian integer with length one of { 1,2,3,4,8 } bytes"""
+
+    RIAA_PA = 24
+    """RIAA parental advisory; { -1=no, 1=yes, 0=unspecified },
+       8-bit ingteger"""
+
+    UPC = 25
+    """Universal Product Code, in text UTF-8 format (valid as an ID)"""
+
+    BMP = 27
+    """Windows bitmap image"""
+
+
+@hashable
+class MP4Cover(bytes):
+    """A cover artwork.
+
+    Attributes:
+
+    * imageformat -- format of the image (either FORMAT_JPEG or FORMAT_PNG)
+    """
+
+    FORMAT_JPEG = AtomDataType.JPEG
+    FORMAT_PNG = AtomDataType.PNG
+
+    def __new__(cls, data, *args, **kwargs):
+        return bytes.__new__(cls, data)
+
+    def __init__(self, data, imageformat=FORMAT_JPEG):
+        self.imageformat = imageformat
+
+    __hash__ = bytes.__hash__
+
+    def __eq__(self, other):
+        if not isinstance(other, MP4Cover):
+            return bytes(self) == other
+
+        return (bytes(self) == bytes(other) and
+                self.imageformat == other.imageformat)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __repr__(self):
+        return "%s(%r, %r)" % (
+            type(self).__name__, bytes(self),
+            AtomDataType(self.imageformat))
+
+
+@hashable
+class MP4FreeForm(bytes):
+    """A freeform value.
+
+    Attributes:
+
+    * dataformat -- format of the data (see AtomDataType)
+    """
+
+    FORMAT_DATA = AtomDataType.IMPLICIT  # deprecated
+    FORMAT_TEXT = AtomDataType.UTF8  # deprecated
+
+    def __new__(cls, data, *args, **kwargs):
+        return bytes.__new__(cls, data)
+
+    def __init__(self, data, dataformat=AtomDataType.UTF8, version=0):
+        self.dataformat = dataformat
+        self.version = version
+
+    __hash__ = bytes.__hash__
+
+    def __eq__(self, other):
+        if not isinstance(other, MP4FreeForm):
+            return bytes(self) == other
+
+        return (bytes(self) == bytes(other) and
+                self.dataformat == other.dataformat and
+                self.version == other.version)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __repr__(self):
+        return "%s(%r, %r)" % (
+            type(self).__name__, bytes(self),
+            AtomDataType(self.dataformat))
+
+
+
+def _name2key(name):
+    if PY2:
+        return name
+    return name.decode("latin-1")
+
+
+def _key2name(key):
+    if PY2:
+        return key
+    return key.encode("latin-1")
+
+
+def _find_padding(atom_path):
+    # Check for padding "free" atom
+    # XXX: we only use them if they are adjacent to ilst, and only one.
+    # and there also is a top level free atom which we could use maybe..?
+
+    meta, ilst = atom_path[-2:]
+    assert meta.name == b"meta" and ilst.name == b"ilst"
+    index = meta.children.index(ilst)
+    try:
+        prev = meta.children[index - 1]
+        if prev.name == b"free":
+            return prev
+    except IndexError:
+        pass
+
+    try:
+        next_ = meta.children[index + 1]
+        if next_.name == b"free":
+            return next_
+    except IndexError:
+        pass
+
+
+class MP4Tags(DictProxy, Metadata):
+    r"""Dictionary containing Apple iTunes metadata list key/values.
+
+    Keys are four byte identifiers, except for freeform ('----')
+    keys. Values are usually unicode strings, but some atoms have a
+    special structure:
+
+    Text values (multiple values per key are supported):
+
+    * '\\xa9nam' -- track title
+    * '\\xa9alb' -- album
+    * '\\xa9ART' -- artist
+    * 'aART' -- album artist
+    * '\\xa9wrt' -- composer
+    * '\\xa9day' -- year
+    * '\\xa9cmt' -- comment
+    * 'desc' -- description (usually used in podcasts)
+    * 'purd' -- purchase date
+    * '\\xa9grp' -- grouping
+    * '\\xa9gen' -- genre
+    * '\\xa9lyr' -- lyrics
+    * 'purl' -- podcast URL
+    * 'egid' -- podcast episode GUID
+    * 'catg' -- podcast category
+    * 'keyw' -- podcast keywords
+    * '\\xa9too' -- encoded by
+    * 'cprt' -- copyright
+    * 'soal' -- album sort order
+    * 'soaa' -- album artist sort order
+    * 'soar' -- artist sort order
+    * 'sonm' -- title sort order
+    * 'soco' -- composer sort order
+    * 'sosn' -- show sort order
+    * 'tvsh' -- show name
+
+    Boolean values:
+
+    * 'cpil' -- part of a compilation
+    * 'pgap' -- part of a gapless album
+    * 'pcst' -- podcast (iTunes reads this only on import)
+
+    Tuples of ints (multiple values per key are supported):
+
+    * 'trkn' -- track number, total tracks
+    * 'disk' -- disc number, total discs
+
+    Others:
+
+    * 'tmpo' -- tempo/BPM, 16 bit int
+    * 'covr' -- cover artwork, list of MP4Cover objects (which are
+      tagged strs)
+    * 'gnre' -- ID3v1 genre. Not supported, use '\\xa9gen' instead.
+
+    The freeform '----' frames use a key in the format '----:mean:name'
+    where 'mean' is usually 'com.apple.iTunes' and 'name' is a unique
+    identifier for this frame. The value is a str, but is probably
+    text that can be decoded as UTF-8. Multiple values per key are
+    supported.
+
+    MP4 tag data cannot exist outside of the structure of an MP4 file,
+    so this class should not be manually instantiated.
+
+    Unknown non-text tags and tags that failed to parse will be written
+    back as is.
+    """
+
+    def __init__(self, *args, **kwargs):
+        self._failed_atoms = {}
+        super(MP4Tags, self).__init__(*args, **kwargs)
+
+    def load(self, atoms, fileobj):
+        try:
+            path = atoms.path(b"moov", b"udta", b"meta", b"ilst")
+        except KeyError as key:
+            raise MP4MetadataError(key)
+
+        free = _find_padding(path)
+        self._padding = free.datalength if free is not None else 0
+
+        ilst = path[-1]
+        for atom in ilst.children:
+            ok, data = atom.read(fileobj)
+            if not ok:
+                raise MP4MetadataError("Not enough data")
+
+            try:
+                if atom.name in self.__atoms:
+                    info = self.__atoms[atom.name]
+                    info[0](self, atom, data)
+                else:
+                    # unknown atom, try as text
+                    self.__parse_text(atom, data, implicit=False)
+            except MP4MetadataError:
+                # parsing failed, save them so we can write them back
+                key = _name2key(atom.name)
+                self._failed_atoms.setdefault(key, []).append(data)
+
+    def __setitem__(self, key, value):
+        if not isinstance(key, str):
+            raise TypeError("key has to be str")
+        super(MP4Tags, self).__setitem__(key, value)
+
+    @classmethod
+    def _can_load(cls, atoms):
+        return b"moov.udta.meta.ilst" in atoms
+
+    @staticmethod
+    def _key_sort(item):
+        (key, v) = item
+        # iTunes always writes the tags in order of "relevance", try
+        # to copy it as closely as possible.
+        order = ["\xa9nam", "\xa9ART", "\xa9wrt", "\xa9alb",
+                 "\xa9gen", "gnre", "trkn", "disk",
+                 "\xa9day", "cpil", "pgap", "pcst", "tmpo",
+                 "\xa9too", "----", "covr", "\xa9lyr"]
+        order = dict(izip(order, xrange(len(order))))
+        last = len(order)
+        # If there's no key-based way to distinguish, order by length.
+        # If there's still no way, go by string comparison on the
+        # values, so we at least have something determinstic.
+        return (order.get(key[:4], last), len(repr(v)), repr(v))
+
+    def save(self, filename, padding=None):
+        """Save the metadata to the given filename."""
+
+        values = []
+        items = sorted(self.items(), key=self._key_sort)
+        for key, value in items:
+            atom_name = _key2name(key)[:4]
+            if atom_name in self.__atoms:
+                render_func = self.__atoms[atom_name][1]
+            else:
+                render_func = type(self).__render_text
+
+            try:
+                values.append(render_func(self, key, value))
+            except (TypeError, ValueError) as s:
+                reraise(MP4MetadataValueError, s, sys.exc_info()[2])
+
+        for key, failed in iteritems(self._failed_atoms):
+            # don't write atoms back if we have added a new one with
+            # the same name, this excludes freeform which can have
+            # multiple atoms with the same key (most parsers seem to be able
+            # to handle that)
+            if key in self:
+                assert _key2name(key) != b"----"
+                continue
+            for data in failed:
+                values.append(Atom.render(_key2name(key), data))
+
+        data = Atom.render(b"ilst", b"".join(values))
+
+        # Find the old atoms.
+        with open(filename, "rb+") as fileobj:
+            try:
+                atoms = Atoms(fileobj)
+            except AtomError as err:
+                reraise(error, err, sys.exc_info()[2])
+
+            self.__save(fileobj, atoms, data, padding)
+
+    def __save(self, fileobj, atoms, data, padding):
+        try:
+            path = atoms.path(b"moov", b"udta", b"meta", b"ilst")
+        except KeyError:
+            self.__save_new(fileobj, atoms, data, padding)
+        else:
+            self.__save_existing(fileobj, atoms, path, data, padding)
+
+    def __pad_ilst(self, data, length=None):
+        if length is None:
+            length = ((len(data) + 1023) & ~1023) - len(data)
+        return Atom.render(b"free", b"\x00" * length)
+
+    def __save_new(self, fileobj, atoms, ilst_data, padding_func):
+        hdlr = Atom.render(b"hdlr", b"\x00" * 8 + b"mdirappl" + b"\x00" * 9)
+        meta_data = b"\x00\x00\x00\x00" + hdlr + ilst_data
+
+        try:
+            path = atoms.path(b"moov", b"udta")
+        except KeyError:
+            path = atoms.path(b"moov")
+
+        offset = path[-1]._dataoffset
+
+        # ignoring some atom overhead... but we don't have padding left anyway
+        # and padding_size is guaranteed to be less than zero
+        content_size = get_size(fileobj) - offset
+        padding_size = -len(meta_data)
+        assert padding_size < 0
+        info = PaddingInfo(padding_size, content_size)
+        new_padding = info._get_padding(padding_func)
+        new_padding = min(0xFFFFFFFF, new_padding)
+
+        free = Atom.render(b"free", b"\x00" * new_padding)
+        meta = Atom.render(b"meta", meta_data + free)
+        if path[-1].name != b"udta":
+            # moov.udta not found -- create one
+            data = Atom.render(b"udta", meta)
+        else:
+            data = meta
+
+        insert_bytes(fileobj, len(data), offset)
+        fileobj.seek(offset)
+        fileobj.write(data)
+        self.__update_parents(fileobj, path, len(data))
+        self.__update_offsets(fileobj, atoms, len(data), offset)
+
+    def __save_existing(self, fileobj, atoms, path, ilst_data, padding_func):
+        # Replace the old ilst atom.
+        ilst = path[-1]
+        offset = ilst.offset
+        length = ilst.length
+
+        # Use adjacent free atom if there is one
+        free = _find_padding(path)
+        if free is not None:
+            offset = min(offset, free.offset)
+            length += free.length
+
+        # Always add a padding atom to make things easier
+        padding_overhead = len(Atom.render(b"free", b""))
+        content_size = get_size(fileobj) - (offset + length)
+        padding_size = length - (len(ilst_data) + padding_overhead)
+        info = PaddingInfo(padding_size, content_size)
+        new_padding = info._get_padding(padding_func)
+        # Limit padding size so we can be sure the free atom overhead is as we
+        # calculated above (see Atom.render)
+        new_padding = min(0xFFFFFFFF, new_padding)
+
+        ilst_data += Atom.render(b"free", b"\x00" * new_padding)
+
+        resize_bytes(fileobj, length, len(ilst_data), offset)
+        delta = len(ilst_data) - length
+
+        fileobj.seek(offset)
+        fileobj.write(ilst_data)
+        self.__update_parents(fileobj, path[:-1], delta)
+        self.__update_offsets(fileobj, atoms, delta, offset)
+
+    def __update_parents(self, fileobj, path, delta):
+        """Update all parent atoms with the new size."""
+
+        if delta == 0:
+            return
+
+        for atom in path:
+            fileobj.seek(atom.offset)
+            size = cdata.uint_be(fileobj.read(4))
+            if size == 1:  # 64bit
+                # skip name (4B) and read size (8B)
+                size = cdata.ulonglong_be(fileobj.read(12)[4:])
+                fileobj.seek(atom.offset + 8)
+                fileobj.write(cdata.to_ulonglong_be(size + delta))
+            else:  # 32bit
+                fileobj.seek(atom.offset)
+                fileobj.write(cdata.to_uint_be(size + delta))
+
+    def __update_offset_table(self, fileobj, fmt, atom, delta, offset):
+        """Update offset table in the specified atom."""
+        if atom.offset > offset:
+            atom.offset += delta
+        fileobj.seek(atom.offset + 12)
+        data = fileobj.read(atom.length - 12)
+        fmt = fmt % cdata.uint_be(data[:4])
+        offsets = struct.unpack(fmt, data[4:])
+        offsets = [o + (0, delta)[offset < o] for o in offsets]
+        fileobj.seek(atom.offset + 16)
+        fileobj.write(struct.pack(fmt, *offsets))
+
+    def __update_tfhd(self, fileobj, atom, delta, offset):
+        if atom.offset > offset:
+            atom.offset += delta
+        fileobj.seek(atom.offset + 9)
+        data = fileobj.read(atom.length - 9)
+        flags = cdata.uint_be(b"\x00" + data[:3])
+        if flags & 1:
+            o = cdata.ulonglong_be(data[7:15])
+            if o > offset:
+                o += delta
+            fileobj.seek(atom.offset + 16)
+            fileobj.write(cdata.to_ulonglong_be(o))
+
+    def __update_offsets(self, fileobj, atoms, delta, offset):
+        """Update offset tables in all 'stco' and 'co64' atoms."""
+        if delta == 0:
+            return
+        moov = atoms[b"moov"]
+        for atom in moov.findall(b'stco', True):
+            self.__update_offset_table(fileobj, ">%dI", atom, delta, offset)
+        for atom in moov.findall(b'co64', True):
+            self.__update_offset_table(fileobj, ">%dQ", atom, delta, offset)
+        try:
+            for atom in atoms[b"moof"].findall(b'tfhd', True):
+                self.__update_tfhd(fileobj, atom, delta, offset)
+        except KeyError:
+            pass
+
+    def __parse_data(self, atom, data):
+        pos = 0
+        while pos < atom.length - 8:
+            head = data[pos:pos + 12]
+            if len(head) != 12:
+                raise MP4MetadataError("truncated atom % r" % atom.name)
+            length, name = struct.unpack(">I4s", head[:8])
+            version = ord(head[8:9])
+            flags = struct.unpack(">I", b"\x00" + head[9:12])[0]
+            if name != b"data":
+                raise MP4MetadataError(
+                    "unexpected atom %r inside %r" % (name, atom.name))
+
+            chunk = data[pos + 16:pos + length]
+            if len(chunk) != length - 16:
+                raise MP4MetadataError("truncated atom % r" % atom.name)
+            yield version, flags, chunk
+            pos += length
+
+    def __add(self, key, value, single=False):
+        assert isinstance(key, str)
+
+        if single:
+            self[key] = value
+        else:
+            self.setdefault(key, []).extend(value)
+
+    def __render_data(self, key, version, flags, value):
+        return Atom.render(_key2name(key), b"".join([
+            Atom.render(
+                b"data", struct.pack(">2I", version << 24 | flags, 0) + data)
+            for data in value]))
+
+    def __parse_freeform(self, atom, data):
+        length = cdata.uint_be(data[:4])
+        mean = data[12:length]
+        pos = length
+        length = cdata.uint_be(data[pos:pos + 4])
+        name = data[pos + 12:pos + length]
+        pos += length
+        value = []
+        while pos < atom.length - 8:
+            length, atom_name = struct.unpack(">I4s", data[pos:pos + 8])
+            if atom_name != b"data":
+                raise MP4MetadataError(
+                    "unexpected atom %r inside %r" % (atom_name, atom.name))
+
+            version = ord(data[pos + 8:pos + 8 + 1])
+            flags = struct.unpack(">I", b"\x00" + data[pos + 9:pos + 12])[0]
+            value.append(MP4FreeForm(data[pos + 16:pos + length],
+                                     dataformat=flags, version=version))
+            pos += length
+
+        key = _name2key(atom.name + b":" + mean + b":" + name)
+        self.__add(key, value)
+
+    def __render_freeform(self, key, value):
+        if isinstance(value, bytes):
+            value = [value]
+
+        dummy, mean, name = _key2name(key).split(b":", 2)
+        mean = struct.pack(">I4sI", len(mean) + 12, b"mean", 0) + mean
+        name = struct.pack(">I4sI", len(name) + 12, b"name", 0) + name
+
+        data = b""
+        for v in value:
+            flags = AtomDataType.UTF8
+            version = 0
+            if isinstance(v, MP4FreeForm):
+                flags = v.dataformat
+                version = v.version
+
+            data += struct.pack(
+                ">I4s2I", len(v) + 16, b"data", version << 24 | flags, 0)
+            data += v
+
+        return Atom.render(b"----", mean + name + data)
+
+    def __parse_pair(self, atom, data):
+        key = _name2key(atom.name)
+        values = [struct.unpack(">2H", d[2:6]) for
+                  version, flags, d in self.__parse_data(atom, data)]
+        self.__add(key, values)
+
+    def __render_pair(self, key, value):
+        data = []
+        for (track, total) in value:
+            if 0 <= track < 1 << 16 and 0 <= total < 1 << 16:
+                data.append(struct.pack(">4H", 0, track, total, 0))
+            else:
+                raise MP4MetadataValueError(
+                    "invalid numeric pair %r" % ((track, total),))
+        return self.__render_data(key, 0, AtomDataType.IMPLICIT, data)
+
+    def __render_pair_no_trailing(self, key, value):
+        data = []
+        for (track, total) in value:
+            if 0 <= track < 1 << 16 and 0 <= total < 1 << 16:
+                data.append(struct.pack(">3H", 0, track, total))
+            else:
+                raise MP4MetadataValueError(
+                    "invalid numeric pair %r" % ((track, total),))
+        return self.__render_data(key, 0, AtomDataType.IMPLICIT, data)
+
+    def __parse_genre(self, atom, data):
+        values = []
+        for version, flags, data in self.__parse_data(atom, data):
+            # version = 0, flags = 0
+            if len(data) != 2:
+                raise MP4MetadataValueError("invalid genre")
+            genre = cdata.short_be(data)
+            # Translate to a freeform genre.
+            try:
+                genre = GENRES[genre - 1]
+            except IndexError:
+                # this will make us write it back at least
+                raise MP4MetadataValueError("unknown genre")
+            values.append(genre)
+        key = _name2key(b"\xa9gen")
+        self.__add(key, values)
+
+    def __parse_tempo(self, atom, data):
+        values = []
+        for version, flags, data in self.__parse_data(atom, data):
+            # version = 0, flags = 0 or 21
+            if len(data) != 2:
+                raise MP4MetadataValueError("invalid tempo")
+            values.append(cdata.ushort_be(data))
+        key = _name2key(atom.name)
+        self.__add(key, values)
+
+    def __render_tempo(self, key, value):
+        try:
+            if len(value) == 0:
+                return self.__render_data(key, 0, AtomDataType.INTEGER, b"")
+
+            if (min(value) < 0) or (max(value) >= 2 ** 16):
+                raise MP4MetadataValueError(
+                    "invalid 16 bit integers: %r" % value)
+        except TypeError:
+            raise MP4MetadataValueError(
+                "tmpo must be a list of 16 bit integers")
+
+        values = [cdata.to_ushort_be(v) for v in value]
+        return self.__render_data(key, 0, AtomDataType.INTEGER, values)
+
+    def __parse_bool(self, atom, data):
+        for version, flags, data in self.__parse_data(atom, data):
+            if len(data) != 1:
+                raise MP4MetadataValueError("invalid bool")
+
+            value = bool(ord(data))
+            key = _name2key(atom.name)
+            self.__add(key, value, single=True)
+
+    def __render_bool(self, key, value):
+        return self.__render_data(
+            key, 0, AtomDataType.INTEGER, [chr_(bool(value))])
+
+    def __parse_cover(self, atom, data):
+        values = []
+        pos = 0
+        while pos < atom.length - 8:
+            length, name, imageformat = struct.unpack(">I4sI",
+                                                      data[pos:pos + 12])
+            if name != b"data":
+                if name == b"name":
+                    pos += length
+                    continue
+                raise MP4MetadataError(
+                    "unexpected atom %r inside 'covr'" % name)
+            if imageformat not in (MP4Cover.FORMAT_JPEG, MP4Cover.FORMAT_PNG):
+                # Sometimes AtomDataType.IMPLICIT or simply wrong.
+                # In all cases it was jpeg, so default to it
+                imageformat = MP4Cover.FORMAT_JPEG
+            cover = MP4Cover(data[pos + 16:pos + length], imageformat)
+            values.append(cover)
+            pos += length
+
+        key = _name2key(atom.name)
+        self.__add(key, values)
+
+    def __render_cover(self, key, value):
+        atom_data = []
+        for cover in value:
+            try:
+                imageformat = cover.imageformat
+            except AttributeError:
+                imageformat = MP4Cover.FORMAT_JPEG
+            atom_data.append(Atom.render(
+                b"data", struct.pack(">2I", imageformat, 0) + cover))
+        return Atom.render(_key2name(key), b"".join(atom_data))
+
+    def __parse_text(self, atom, data, implicit=True):
+        # implicit = False, for parsing unknown atoms only take utf8 ones.
+        # For known ones we can assume the implicit are utf8 too.
+        values = []
+        for version, flags, atom_data in self.__parse_data(atom, data):
+            if implicit:
+                if flags not in (AtomDataType.IMPLICIT, AtomDataType.UTF8):
+                    raise MP4MetadataError(
+                        "Unknown atom type %r for %r" % (flags, atom.name))
+            else:
+                if flags != AtomDataType.UTF8:
+                    raise MP4MetadataError(
+                        "%r is not text, ignore" % atom.name)
+
+            try:
+                text = atom_data.decode("utf-8")
+            except UnicodeDecodeError as e:
+                raise MP4MetadataError("%s: %s" % (_name2key(atom.name), e))
+
+            values.append(text)
+
+        key = _name2key(atom.name)
+        self.__add(key, values)
+
+    def __render_text(self, key, value, flags=AtomDataType.UTF8):
+        if isinstance(value, string_types):
+            value = [value]
+
+        encoded = []
+        for v in value:
+            if not isinstance(v, text_type):
+                if PY3:
+                    raise TypeError("%r not str" % v)
+                v = v.decode("utf-8")
+            encoded.append(v.encode("utf-8"))
+
+        return self.__render_data(key, 0, flags, encoded)
+
+    def delete(self, filename):
+        """Remove the metadata from the given filename."""
+
+        self._failed_atoms.clear()
+        self.clear()
+        self.save(filename, padding=lambda x: 0)
+
+    __atoms = {
+        b"----": (__parse_freeform, __render_freeform),
+        b"trkn": (__parse_pair, __render_pair),
+        b"disk": (__parse_pair, __render_pair_no_trailing),
+        b"gnre": (__parse_genre, None),
+        b"tmpo": (__parse_tempo, __render_tempo),
+        b"cpil": (__parse_bool, __render_bool),
+        b"pgap": (__parse_bool, __render_bool),
+        b"pcst": (__parse_bool, __render_bool),
+        b"covr": (__parse_cover, __render_cover),
+        b"purl": (__parse_text, __render_text),
+        b"egid": (__parse_text, __render_text),
+    }
+
+    # these allow implicit flags and parse as text
+    for name in [b"\xa9nam", b"\xa9alb", b"\xa9ART", b"aART", b"\xa9wrt",
+                 b"\xa9day", b"\xa9cmt", b"desc", b"purd", b"\xa9grp",
+                 b"\xa9gen", b"\xa9lyr", b"catg", b"keyw", b"\xa9too",
+                 b"cprt", b"soal", b"soaa", b"soar", b"sonm", b"soco",
+                 b"sosn", b"tvsh"]:
+        __atoms[name] = (__parse_text, __render_text)
+
+    def pprint(self):
+
+        def to_line(key, value):
+            assert isinstance(key, text_type)
+            if isinstance(value, text_type):
+                return u"%s=%s" % (key, value)
+            return u"%s=%r" % (key, value)
+
+        values = []
+        for key, value in sorted(iteritems(self)):
+            if not isinstance(key, text_type):
+                key = key.decode("latin-1")
+            if key == "covr":
+                values.append(u"%s=%s" % (key, u", ".join(
+                    [u"[%d bytes of data]" % len(data) for data in value])))
+            elif isinstance(value, list):
+                for v in value:
+                    values.append(to_line(key, v))
+            else:
+                values.append(to_line(key, value))
+        return u"\n".join(values)
+
+
+class MP4Info(StreamInfo):
+    """MPEG-4 stream information.
+
+    Attributes:
+
+    * bitrate -- bitrate in bits per second, as an int
+    * length -- file length in seconds, as a float
+    * channels -- number of audio channels
+    * sample_rate -- audio sampling rate in Hz
+    * bits_per_sample -- bits per sample
+    * codec (string):
+        * if starting with ``"mp4a"`` uses an mp4a audio codec
+          (see the codec parameter in rfc6381 for details e.g. ``"mp4a.40.2"``)
+        * for everything else see a list of possible values at
+          http://www.mp4ra.org/codecs.html
+
+        e.g. ``"mp4a"``, ``"alac"``, ``"mp4a.40.2"``, ``"ac-3"`` etc.
+    * codec_description (string):
+        Name of the codec used (ALAC, AAC LC, AC-3...). Values might change in
+        the future, use for display purposes only.
+    """
+
+    bitrate = 0
+    channels = 0
+    sample_rate = 0
+    bits_per_sample = 0
+    codec = u""
+    codec_name = u""
+
+    def __init__(self, atoms, fileobj):
+        try:
+            moov = atoms[b"moov"]
+        except KeyError:
+            raise MP4StreamInfoError("not a MP4 file")
+
+        for trak in moov.findall(b"trak"):
+            hdlr = trak[b"mdia", b"hdlr"]
+            ok, data = hdlr.read(fileobj)
+            if not ok:
+                raise MP4StreamInfoError("Not enough data")
+            if data[8:12] == b"soun":
+                break
+        else:
+            raise MP4StreamInfoError("track has no audio data")
+
+        mdhd = trak[b"mdia", b"mdhd"]
+        ok, data = mdhd.read(fileobj)
+        if not ok:
+            raise MP4StreamInfoError("Not enough data")
+
+        try:
+            version, flags, data = parse_full_atom(data)
+        except ValueError as e:
+            raise MP4StreamInfoError(e)
+
+        if version == 0:
+            offset = 8
+            fmt = ">2I"
+        elif version == 1:
+            offset = 16
+            fmt = ">IQ"
+        else:
+            raise MP4StreamInfoError("Unknown mdhd version %d" % version)
+
+        end = offset + struct.calcsize(fmt)
+        unit, length = struct.unpack(fmt, data[offset:end])
+        try:
+            self.length = float(length) / unit
+        except ZeroDivisionError:
+            self.length = 0
+
+        try:
+            atom = trak[b"mdia", b"minf", b"stbl", b"stsd"]
+        except KeyError:
+            pass
+        else:
+            self._parse_stsd(atom, fileobj)
+
+    def _parse_stsd(self, atom, fileobj):
+        """Sets channels, bits_per_sample, sample_rate and optionally bitrate.
+
+        Can raise MP4StreamInfoError.
+        """
+
+        assert atom.name == b"stsd"
+
+        ok, data = atom.read(fileobj)
+        if not ok:
+            raise MP4StreamInfoError("Invalid stsd")
+
+        try:
+            version, flags, data = parse_full_atom(data)
+        except ValueError as e:
+            raise MP4StreamInfoError(e)
+
+        if version != 0:
+            raise MP4StreamInfoError("Unsupported stsd version")
+
+        try:
+            num_entries, offset = cdata.uint32_be_from(data, 0)
+        except cdata.error as e:
+            raise MP4StreamInfoError(e)
+
+        if num_entries == 0:
+            return
+
+        # look at the first entry if there is one
+        entry_fileobj = cBytesIO(data[offset:])
+        try:
+            entry_atom = Atom(entry_fileobj)
+        except AtomError as e:
+            raise MP4StreamInfoError(e)
+
+        try:
+            entry = AudioSampleEntry(entry_atom, entry_fileobj)
+        except ASEntryError as e:
+            raise MP4StreamInfoError(e)
+        else:
+            self.channels = entry.channels
+            self.bits_per_sample = entry.sample_size
+            self.sample_rate = entry.sample_rate
+            self.bitrate = entry.bitrate
+            self.codec = entry.codec
+            self.codec_description = entry.codec_description
+
+    def pprint(self):
+        return "MPEG-4 audio (%s), %.2f seconds, %d bps" % (
+            self.codec_description, self.length, self.bitrate)
+
+
+class MP4(FileType):
+    """An MPEG-4 audio file, probably containing AAC.
+
+    If more than one track is present in the file, the first is used.
+    Only audio ('soun') tracks will be read.
+
+    :ivar info: :class:`MP4Info`
+    :ivar tags: :class:`MP4Tags`
+    """
+
+    MP4Tags = MP4Tags
+
+    _mimes = ["audio/mp4", "audio/x-m4a", "audio/mpeg4", "audio/aac"]
+
+    def load(self, filename):
+        self.filename = filename
+        with open(filename, "rb") as fileobj:
+            try:
+                atoms = Atoms(fileobj)
+            except AtomError as err:
+                reraise(error, err, sys.exc_info()[2])
+
+            try:
+                self.info = MP4Info(atoms, fileobj)
+            except error:
+                raise
+            except Exception as err:
+                reraise(MP4StreamInfoError, err, sys.exc_info()[2])
+
+            if not MP4Tags._can_load(atoms):
+                self.tags = None
+                self._padding = 0
+            else:
+                try:
+                    self.tags = self.MP4Tags(atoms, fileobj)
+                except error:
+                    raise
+                except Exception as err:
+                    reraise(MP4MetadataError, err, sys.exc_info()[2])
+                else:
+                    self._padding = self.tags._padding
+
+    def add_tags(self):
+        if self.tags is None:
+            self.tags = self.MP4Tags()
+        else:
+            raise error("an MP4 tag already exists")
+
+    @staticmethod
+    def score(filename, fileobj, header_data):
+        return (b"ftyp" in header_data) + (b"mp4" in header_data)
+
+
+Open = MP4
+
+
+def delete(filename):
+    """Remove tags from a file."""
+
+    MP4(filename).delete()
diff --git a/resources/lib/mutagen/mp4/__pycache__/__init__.cpython-35.pyc b/resources/lib/mutagen/mp4/__pycache__/__init__.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..de968da940fc35ac7e446528711bd4b0f1e18e24
GIT binary patch
literal 31145
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

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/mp4/__pycache__/_as_entry.cpython-35.pyc b/resources/lib/mutagen/mp4/__pycache__/_as_entry.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..31483c463fa273b25f89cbb8fab1e7d451fa97b9
GIT binary patch
literal 14269
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
NQ`65(e{uSE{s(x{fz$v1

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/mp4/__pycache__/_atom.cpython-35.pyc b/resources/lib/mutagen/mp4/__pycache__/_atom.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f4abf385e2cb9aadc4560b18c414dfbf0ef1a9cc
GIT binary patch
literal 6248
zcmb_g&vV<>5ng~GMN*WhABki+X$&WEWyTVl#Ich}ZPl*hWZWc7VmWoANW*~$P=Z7P
z)C1^<Xgry*)0tkALuWF*r_)2He?vRdf1w9ld(!mK&g7O$`t3fDq7<jSls29o9v1Jt
z-Tij=dzhb^Du224?+?E|P4o{M`<2nZjw|{Sjf?*Vb%@q=-=vN~U6a;LGTa=w7P)zH
z3*=6aTO_wcZkgOkax1i5rgaEle2RR7)-8I=Jw<MnKF3T|^78a3N9zTQPLn$$qch}9
z$Y>Fxv*gan=-hA=lN+>Fd*TrLqDIX~bMJrja%ADA8;19#(|2P>I<a#0^dr~TBV8|h
z<aeT29!2q1N6Yv@TX(`xckp`qkY{f3TEm}#&UIYTG#U>!G1xvZh3v~rOE==MdrPTM
zHEOxE*lY$)*K0P@a<kbD-F}Drm1c9l?{xH&Kawh9Drvrb`R3~z?+@agFt~Ji;~jr%
zBl2T!rRTKvoNX`K==Nj$4K}*HS2mgs7OM9KinsU#@5n?`G35T(mU9@t&H@k7oY*qE
z2cCorhmQOle#(;vcZx?pCwhdDp=)uwUL<em<LoK?U11-?x%@hOUnRJdg)u%ua3uF3
zV8VHGsl`Teeh*vT2y8!a{rkQPgV>@{xoqQiBaW57)sMaCb-8ct_#IbyfxWU~cl;=}
zWkBAD><gi?8)4wtZFGV#SZRep?D&DFv_MU!=y<_)ydx9nUf8zd9X}G8W$dHX>AQZg
zZD)c`;M(jXdD(ShCmX-^15?<a`0!jm&fZ@>dYNb8m)c=p*;|9ylSj6!?`6w7*oPg(
zN>&To?Z}H|ov`J^e#mCRW<494L8#w~L$$K)--kike#Z;9cJ*wTHrj@FSvf7;SDqbV
za~;<XgU-O-^6Y-(xtKNfRN!=YXkfpq@l}^SG!$dK#pvN5|9ukIO<d6wu=c4zy8s6|
zL`{PR7b!Mr7ces95g=tUbRL<sVTw#%+5+7R&XL8k6SP+*wMm9y#7G=D7Jq2aM}a}V
z!lb(hEt3{_`Vzmoi!5W&o=NH(8k`@lhFP(Z@zt<CUdf+a=Wjo>j<pLooAj!Av`m5a
z5R!e_7SfSyD|k5EL{T=eZhVTUM?XjEj7XL;N!}1b0xFeRePi5GQ0lbao|a<0w4RV>
z;ozy^GUnJOCWli)yg>0J?G;G<%h=Y|uZRuG6i?CaDR~{SE7QSXJn6yuF?V4_ZO;W(
z^PdLaRA{$K@ifb^UKTCjMQCp9|Jk?~oXJ+u+s$6ic8u6xt4$?y?>YlpIez3Jc(WXs
zSgW^Ri4HlzC8q$}0AL2{IG7F@U5UB+!69eTWckV~_Ld(@Fbo5*<pd+NzI4Urc---~
zcRVG?OkNNqJ9ZrQRyy8&Pv;Mt-`a8Rdv@ZfFq`U-b7Qj1`Q>qwzqo9_WFvLFHnv8i
zHj`Sh*Xg7M9gS&0hf-ST2b_^pOL>l)7IaofEe`TDkIjWja&S7?WIeN&t;qBCFm&_1
z#@!pY8@KMPrNv=RLvdJp`}X_IwcG2r(%iNerv<0i^8#0K9Yz_!%bj*Qkwto%7j2Kq
zhAKgfYmFcHakIIEyJ!^+<*G)`xM)lpGq|{aUiww(SI|~4R?1Bq^Trt&E16ZJWSqsn
z=gnk_-B=&a+G2n4kN<KqkVL>4fzhB_4J(sQpI)mOsDFy<l3L(~om?~2Gu$85JH7Gp
zQEqMEvD7g#ld3F#^sL1`<R33S+5qdR<4Z#m*@pqoLy~onkO1NbCdC%x8hw__aRlSG
z&GpDkF5mIuzCw;|+en21Pb6ASmdKOrWx)lfUqMjM8FWI&6%^L;3gjtMa*lh?qDgZ=
z1@sqn&+>r9ik#3~vHTreQ2`C*=8Rb*5du-?MUN@W5@PWW(TPQ8=mKpQpc@A)vV}qF
z62+o9k|_qEDhL*Av9Oj63s=K1zk%ULFbY3KtzWB6Bv0M}=ukA3^NH4!H(ghgShB+w
z|L(KzKFg5~>Lkyh{eh}roL&7K{;C-?sm0NO5ypmkUPdAx!Fa;$1ypk^_7Makeni$Z
z@VH<mlj3;&^vzNI$+HBm7OsdJ_M<S<y%MP=9lV>3ys`OndH}6+oLHe97mQ1M`%6yP
zYLO1k%0rk(5-;HL#^xkFfGI4<?7u_z_7~|M%(GU@C714at-gv-X;C8E;3|TUaq@w>
z8TGuD-}aH&I0@7nccJ|mY*9?WaT~b66`&$`V7sIxHJmri%WUfwW9x*G)CI_j#;4FB
zL?_2_-Y6K0Mp6+wWs@HR3)|`@uIK?8Xc?8+Hfu)~E0S7fqyRNMXwp2ugwlKPi;)p8
za?rqF97Yh#(IOl2iogo@0Fpt!F==seZT|}0<GG{~vtTxvSg?@9?}Xu=-S64X7-w6y
zdtvMau`kqPWQ)$AuBi)CK(D@C7Z`rL#GHO<*<NA-zqDyzTeYvU@?!35depAh>#IUs
z_}xbG<auR-{sbXv3bQ872PI|&vq=R4{1l`ta5IOc#kakIuy=7CM%iQQa8?&|65LR|
zfcq0pLRN1gX@jYELGsz`oa`uHFf2H04lbKB=FMbU+%+cJ8fMfSq(|}nYuso@0lp80
z8Pz4)gVSEi*cA%^;%I?dw0UY<G`|LSf*rsGa1RJKKLNPmTKtDMwTHg{F1*~%^XtG!
zt<Xs9tLI@jAo&_hq{|!kbR|P?H8sk|HqUrl!xddZqdCcDjW<6^RM6!SA<m{{AJ`g*
z-4{99f136=^%&n8cFp99+EZyc>UI3M1Nt7J_L=n;#ptQUBPUG%ZTTu623ldaxB8Gb
zz!{=MIU_ljS(|5UHtSbA;L)9E6`i4c^4$MWvwS{Utxcq*X0z3CqNv$atmjc+>*~h7
zPOaV0ALjcs*Xl%C>O{svh1T1F3n-F^M#kg1e8xO$V$;KltEFnE^nwfxHNneFG@EYN
z!Yq<p&hb#Or^246;(^p1sVHeBig9LWAy{@oS1}!sJc5u%?^SM&HYPph55hILltr^>
zDw74XU{;rAt;)n~xmvDN<#`K_MnA5{*KtK$!aRftasXPXEc#ptBm#zfx|F)Z{o$7a
z?h8r9_X6(YfGJ-MxDVnilrqh;6Qkr0cS4S0?(;Fov5yWuS-LoRTGX;QaXI?1$S191
zED?vTNNB2!qm$Q)qyNW==~xcV!6-M`IzAd56HQ6R2nA$DgenS8##0`VBwJK&d4^~;
z4qoK6i}1xWm~jCC!}Z-A=7yXQ8i{Nc-@lBC#o=q|W1C`hN#U;L)4Z5N^OP!&|A-sS
zEypNIjv40>G6WQiu`h5%T;>VfkZbf5SD#bjoA1*@3vztDg)6#_24LTo^mFiIR_Tq+
zXQVtMRY@9va*HU=ZG#p?k!iXIUA2X{zCfkq@1qie`l~co(}&@1ujAo3-E(lThm%45
zS=X^5@+N4#ci^zxvW8Hdg)c1e_aZJS$4W~LXLu06Yzb#nPUMS&9M_enNno^$qN0=+
z^+J5Vz(<2Pl=C@HyKM8MSPr}8iZz->{Gd<QT%<I2I{XelP-AvV%@Tj4dCtBOGpTq-
z^QaSkVZ(lfOEafp2|jt+NXptL{MHDI9HmU)1dBCv1~@GW5EpT-o5x8Ur3J_E=lOWd
zC0mZ)fG5~b<ITI+*hRclGc{0thW-hR@aL?oz9L6{4FS*%{E*fdDkUSC9YW<uY_z=`
zBu8}RceufJkg~uhfE9csK#-_g5(@HV0T4l137#1W$np030vV6V18pN7Q{Y*km++YP
zhCD`UWyWLP1CK!-(mV!&Lmp#sSxmkffpCn+j3I4Fe70nFaSX<>;VAQn*X;`*ABWs#
zjZd5of1d$-Mi9|dav+G3&pqP^*Ql)vWNSw_`i6-_{fOE$PH>x|Vi2TdKl1VM$O&5B
zdU?x<JiT<9mwKD#`NLGCu0m8pPW+yoVZ`r`;XYDR{)AVgYS2Uh<!lx-wQ6RpXJ+X8
z5kqPXX%GKp_uu5#8T~*XasphW>jqGYe3;P}$!seS8okasaiS68@iaO*$!^XbBOYIv
z!Y~B0!17NZ2Q0sYKaZnwI4BVP6_*@BOO!l2-2M;q2uydGr(?rC{)8(v_b<a)X)Jf@
zSsS&|iAoQmhZ)atbDf(T-2C_Q{Zoki7MEn|%0$7e6f0AeiOLg-pVZ27He0V|pOEVa
z*)H;gAOmM2O#c*`v~=sB<@NZ3qyC0b(B)Bzg-kRS{3Gg+7O(1W+pAnNgxFNfX=@h8
L*-CESn(zGw2^KqW

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/mp4/__pycache__/_util.cpython-35.pyc b/resources/lib/mutagen/mp4/__pycache__/_util.cpython-35.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e7df30cc48b2f9edb33acfd449a0cb20fcf0e36c
GIT binary patch
literal 590
zcmYjN%}OId5U!rdY$R(|@u2L%rH?z{=CHe7L_`SQM1-KQG7viHPBLpx_t4!VMD*g%
z3-}hkQeQpg=6O$6w}G{os;R24s`{(i>~=d(qt~-P3BU_{5RBmv)lxJZKLQe<B5*-~
z@C0}Y4*(bFv|x~D3#@{i_-xFXV`CCRloT~X?W5Wm8V7I#a8JU#O91l?9QsMf+!1g|
zKyfq<eN5mhW^XZ@!rvC~EMn-8h;0QB>j~Vvcj25*5t*dOsC~@`x)PK*U0KSEpjX1!
zQY*oGbWqFEQ8yD6ZE9L7`h5xR(1~PIyF+U>mzwds*P$qVrl?`174)3RMjRQV%{#Ja
z;ZoOYN;@i)Zl*ID_BUbwMKCsA;v~;}Duwc$|NTDIhMRC^-)>6fh9i-uz7^Vidr8M<
zk<K%iRjm8>v%goI&fQF_U%SO|IVx=F#E+VdFWFSsqG}xeRZ-Qyi(%tR*{kP%t!BoG
z;iQpr7;f=7km{g;Y=f*On`E1qFBqNR<K5+iUhIzCA{Jl&V7gfESN1E-8z~M}FtTBf
G_4Ex6hm83E

literal 0
HcmV?d00001

diff --git a/resources/lib/mutagen/mp4/_as_entry.py b/resources/lib/mutagen/mp4/_as_entry.py
new file mode 100644
index 00000000..306d5720
--- /dev/null
+++ b/resources/lib/mutagen/mp4/_as_entry.py
@@ -0,0 +1,542 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+from mutagen._compat import cBytesIO, xrange
+from mutagen.aac import ProgramConfigElement
+from mutagen._util import BitReader, BitReaderError, cdata
+from mutagen._compat import text_type
+from ._util import parse_full_atom
+from ._atom import Atom, AtomError
+
+
+class ASEntryError(Exception):
+    pass
+
+
+class AudioSampleEntry(object):
+    """Parses an AudioSampleEntry atom.
+
+    Private API.
+
+    Attrs:
+        channels (int): number of channels
+        sample_size (int): sample size in bits
+        sample_rate (int): sample rate in Hz
+        bitrate (int): bits per second (0 means unknown)
+        codec (string):
+            audio codec, either 'mp4a[.*][.*]' (rfc6381) or 'alac'
+        codec_description (string): descriptive codec name e.g. "AAC LC+SBR"
+
+    Can raise ASEntryError.
+    """
+
+    channels = 0
+    sample_size = 0
+    sample_rate = 0
+    bitrate = 0
+    codec = None
+    codec_description = None
+
+    def __init__(self, atom, fileobj):
+        ok, data = atom.read(fileobj)
+        if not ok:
+            raise ASEntryError("too short %r atom" % atom.name)
+
+        fileobj = cBytesIO(data)
+        r = BitReader(fileobj)
+
+        try:
+            # SampleEntry
+            r.skip(6 * 8)  # reserved
+            r.skip(2 * 8)  # data_ref_index
+
+            # AudioSampleEntry
+            r.skip(8 * 8)  # reserved
+            self.channels = r.bits(16)
+            self.sample_size = r.bits(16)
+            r.skip(2 * 8)  # pre_defined
+            r.skip(2 * 8)  # reserved
+            self.sample_rate = r.bits(32) >> 16
+        except BitReaderError as e:
+            raise ASEntryError(e)
+
+        assert r.is_aligned()
+
+        try:
+            extra = Atom(fileobj)
+        except AtomError as e:
+            raise ASEntryError(e)
+
+        self.codec = atom.name.decode("latin-1")
+        self.codec_description = None
+
+        if atom.name == b"mp4a" and extra.name == b"esds":
+            self._parse_esds(extra, fileobj)
+        elif atom.name == b"alac" and extra.name == b"alac":
+            self._parse_alac(extra, fileobj)
+        elif atom.name == b"ac-3" and extra.name == b"dac3":
+            self._parse_dac3(extra, fileobj)
+
+        if self.codec_description is None:
+            self.codec_description = self.codec.upper()
+
+    def _parse_dac3(self, atom, fileobj):
+        # ETSI TS 102 366
+
+        assert atom.name == b"dac3"
+
+        ok, data = atom.read(fileobj)
+        if not ok:
+            raise ASEntryError("truncated %s atom" % atom.name)
+        fileobj = cBytesIO(data)
+        r = BitReader(fileobj)
+
+        # sample_rate in AudioSampleEntry covers values in
+        # fscod2 and not just fscod, so ignore fscod here.
+        try:
+            r.skip(2 + 5 + 3)  # fscod, bsid, bsmod
+            acmod = r.bits(3)
+            lfeon = r.bits(1)
+            bit_rate_code = r.bits(5)
+            r.skip(5)  # reserved
+        except BitReaderError as e:
+            raise ASEntryError(e)
+
+        self.channels = [2, 1, 2, 3, 3, 4, 4, 5][acmod] + lfeon
+
+        try:
+            self.bitrate = [
+                32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192,
+                224, 256, 320, 384, 448, 512, 576, 640][bit_rate_code] * 1000
+        except IndexError:
+            pass
+
+    def _parse_alac(self, atom, fileobj):
+        # https://alac.macosforge.org/trac/browser/trunk/
+        #    ALACMagicCookieDescription.txt
+
+        assert atom.name == b"alac"
+
+        ok, data = atom.read(fileobj)
+        if not ok:
+            raise ASEntryError("truncated %s atom" % atom.name)
+
+        try:
+            version, flags, data = parse_full_atom(data)
+        except ValueError as e:
+            raise ASEntryError(e)
+
+        if version != 0:
+            raise ASEntryError("Unsupported version %d" % version)
+
+        fileobj = cBytesIO(data)
+        r = BitReader(fileobj)
+
+        try:
+            # for some files the AudioSampleEntry values default to 44100/2chan
+            # and the real info is in the alac cookie, so prefer it
+            r.skip(32)  # frameLength
+            compatibleVersion = r.bits(8)
+            if compatibleVersion != 0:
+                return
+            self.sample_size = r.bits(8)
+            r.skip(8 + 8 + 8)
+            self.channels = r.bits(8)
+            r.skip(16 + 32)
+            self.bitrate = r.bits(32)
+            self.sample_rate = r.bits(32)
+        except BitReaderError as e:
+            raise ASEntryError(e)
+
+    def _parse_esds(self, esds, fileobj):
+        assert esds.name == b"esds"
+
+        ok, data = esds.read(fileobj)
+        if not ok:
+            raise ASEntryError("truncated %s atom" % esds.name)
+
+        try:
+            version, flags, data = parse_full_atom(data)
+        except ValueError as e:
+            raise ASEntryError(e)
+
+        if version != 0:
+            raise ASEntryError("Unsupported version %d" % version)
+
+        fileobj = cBytesIO(data)
+        r = BitReader(fileobj)
+
+        try:
+            tag = r.bits(8)
+            if tag != ES_Descriptor.TAG:
+                raise ASEntryError("unexpected descriptor: %d" % tag)
+            assert r.is_aligned()
+        except BitReaderError as e:
+            raise ASEntryError(e)
+
+        try:
+            decSpecificInfo = ES_Descriptor.parse(fileobj)
+        except DescriptorError as e:
+            raise ASEntryError(e)
+        dec_conf_desc = decSpecificInfo.decConfigDescr
+
+        self.bitrate = dec_conf_desc.avgBitrate
+        self.codec += dec_conf_desc.codec_param
+        self.codec_description = dec_conf_desc.codec_desc
+
+        decSpecificInfo = dec_conf_desc.decSpecificInfo
+        if decSpecificInfo is not None:
+            if decSpecificInfo.channels != 0:
+                self.channels = decSpecificInfo.channels
+
+            if decSpecificInfo.sample_rate != 0:
+                self.sample_rate = decSpecificInfo.sample_rate
+
+
+class DescriptorError(Exception):
+    pass
+
+
+class BaseDescriptor(object):
+
+    TAG = None
+
+    @classmethod
+    def _parse_desc_length_file(cls, fileobj):
+        """May raise ValueError"""
+
+        value = 0
+        for i in xrange(4):
+            try:
+                b = cdata.uint8(fileobj.read(1))
+            except cdata.error as e:
+                raise ValueError(e)
+            value = (value << 7) | (b & 0x7f)
+            if not b >> 7:
+                break
+        else:
+            raise ValueError("invalid descriptor length")
+
+        return value
+
+    @classmethod
+    def parse(cls, fileobj):
+        """Returns a parsed instance of the called type.
+        The file position is right after the descriptor after this returns.
+
+        Raises DescriptorError
+        """
+
+        try:
+            length = cls._parse_desc_length_file(fileobj)
+        except ValueError as e:
+            raise DescriptorError(e)
+        pos = fileobj.tell()
+        instance = cls(fileobj, length)
+        left = length - (fileobj.tell() - pos)
+        if left < 0:
+            raise DescriptorError("descriptor parsing read too much data")
+        fileobj.seek(left, 1)
+        return instance
+
+
+class ES_Descriptor(BaseDescriptor):
+
+    TAG = 0x3
+
+    def __init__(self, fileobj, length):
+        """Raises DescriptorError"""
+
+        r = BitReader(fileobj)
+        try:
+            self.ES_ID = r.bits(16)
+            self.streamDependenceFlag = r.bits(1)
+            self.URL_Flag = r.bits(1)
+            self.OCRstreamFlag = r.bits(1)
+            self.streamPriority = r.bits(5)
+            if self.streamDependenceFlag:
+                self.dependsOn_ES_ID = r.bits(16)
+            if self.URL_Flag:
+                URLlength = r.bits(8)
+                self.URLstring = r.bytes(URLlength)
+            if self.OCRstreamFlag:
+                self.OCR_ES_Id = r.bits(16)
+
+            tag = r.bits(8)
+        except BitReaderError as e:
+            raise DescriptorError(e)
+
+        if tag != DecoderConfigDescriptor.TAG:
+            raise DescriptorError("unexpected DecoderConfigDescrTag %d" % tag)
+
+        assert r.is_aligned()
+        self.decConfigDescr = DecoderConfigDescriptor.parse(fileobj)
+
+
+class DecoderConfigDescriptor(BaseDescriptor):
+
+    TAG = 0x4
+
+    decSpecificInfo = None
+    """A DecoderSpecificInfo, optional"""
+
+    def __init__(self, fileobj, length):
+        """Raises DescriptorError"""
+
+        r = BitReader(fileobj)
+
+        try:
+            self.objectTypeIndication = r.bits(8)
+            self.streamType = r.bits(6)
+            self.upStream = r.bits(1)
+            self.reserved = r.bits(1)
+            self.bufferSizeDB = r.bits(24)
+            self.maxBitrate = r.bits(32)
+            self.avgBitrate = r.bits(32)
+
+            if (self.objectTypeIndication, self.streamType) != (0x40, 0x5):
+                return
+
+            # all from here is optional
+            if length * 8 == r.get_position():
+                return
+
+            tag = r.bits(8)
+        except BitReaderError as e:
+            raise DescriptorError(e)
+
+        if tag == DecoderSpecificInfo.TAG:
+            assert r.is_aligned()
+            self.decSpecificInfo = DecoderSpecificInfo.parse(fileobj)
+
+    @property
+    def codec_param(self):
+        """string"""
+
+        param = u".%X" % self.objectTypeIndication
+        info = self.decSpecificInfo
+        if info is not None:
+            param += u".%d" % info.audioObjectType
+        return param
+
+    @property
+    def codec_desc(self):
+        """string or None"""
+
+        info = self.decSpecificInfo
+        desc = None
+        if info is not None:
+            desc = info.description
+        return desc
+
+
+class DecoderSpecificInfo(BaseDescriptor):
+
+    TAG = 0x5
+
+    _TYPE_NAMES = [
+        None, "AAC MAIN", "AAC LC", "AAC SSR", "AAC LTP", "SBR",
+        "AAC scalable", "TwinVQ", "CELP", "HVXC", None, None, "TTSI",
+        "Main synthetic", "Wavetable synthesis", "General MIDI",
+        "Algorithmic Synthesis and Audio FX", "ER AAC LC", None, "ER AAC LTP",
+        "ER AAC scalable", "ER Twin VQ", "ER BSAC", "ER AAC LD", "ER CELP",
+        "ER HVXC", "ER HILN", "ER Parametric", "SSC", "PS", "MPEG Surround",
+        None, "Layer-1", "Layer-2", "Layer-3", "DST", "ALS", "SLS",
+        "SLS non-core", "ER AAC ELD", "SMR Simple", "SMR Main", "USAC",
+        "SAOC", "LD MPEG Surround", "USAC"
+    ]
+
+    _FREQS = [
+        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000,
+        12000, 11025, 8000, 7350,
+    ]
+
+    @property
+    def description(self):
+        """string or None if unknown"""
+
+        name = None
+        try:
+            name = self._TYPE_NAMES[self.audioObjectType]
+        except IndexError:
+            pass
+        if name is None:
+            return
+        if self.sbrPresentFlag == 1:
+            name += "+SBR"
+        if self.psPresentFlag == 1:
+            name += "+PS"
+        return text_type(name)
+
+    @property
+    def sample_rate(self):
+        """0 means unknown"""
+
+        if self.sbrPresentFlag == 1:
+            return self.extensionSamplingFrequency
+        elif self.sbrPresentFlag == 0:
+            return self.samplingFrequency
+        else:
+            # these are all types that support SBR
+            aot_can_sbr = (1, 2, 3, 4, 6, 17, 19, 20, 22)
+            if self.audioObjectType not in aot_can_sbr:
+                return self.samplingFrequency
+            # there shouldn't be SBR for > 48KHz
+            if self.samplingFrequency > 24000:
+                return self.samplingFrequency
+            # either samplingFrequency or samplingFrequency * 2
+            return 0
+
+    @property
+    def channels(self):
+        """channel count or 0 for unknown"""
+
+        # from ProgramConfigElement()
+        if hasattr(self, "pce_channels"):
+            return self.pce_channels
+
+        conf = getattr(
+            self, "extensionChannelConfiguration", self.channelConfiguration)
+
+        if conf == 1:
+            if self.psPresentFlag == -1:
+                return 0
+            elif self.psPresentFlag == 1:
+                return 2
+            else:
+                return 1
+        elif conf == 7:
+            return 8
+        elif conf > 7:
+            return 0
+        else:
+            return conf
+
+    def _get_audio_object_type(self, r):
+        """Raises BitReaderError"""
+
+        audioObjectType = r.bits(5)
+        if audioObjectType == 31:
+            audioObjectTypeExt = r.bits(6)
+            audioObjectType = 32 + audioObjectTypeExt
+        return audioObjectType
+
+    def _get_sampling_freq(self, r):
+        """Raises BitReaderError"""
+
+        samplingFrequencyIndex = r.bits(4)
+        if samplingFrequencyIndex == 0xf:
+            samplingFrequency = r.bits(24)
+        else:
+            try:
+                samplingFrequency = self._FREQS[samplingFrequencyIndex]
+            except IndexError:
+                samplingFrequency = 0
+        return samplingFrequency
+
+    def __init__(self, fileobj, length):
+        """Raises DescriptorError"""
+
+        r = BitReader(fileobj)
+        try:
+            self._parse(r, length)
+        except BitReaderError as e:
+            raise DescriptorError(e)
+
+    def _parse(self, r, length):
+        """Raises BitReaderError"""
+
+        def bits_left():
+            return length * 8 - r.get_position()
+
+        self.audioObjectType = self._get_audio_object_type(r)
+        self.samplingFrequency = self._get_sampling_freq(r)
+        self.channelConfiguration = r.bits(4)
+
+        self.sbrPresentFlag = -1
+        self.psPresentFlag = -1
+        if self.audioObjectType in (5, 29):
+            self.extensionAudioObjectType = 5
+            self.sbrPresentFlag = 1
+            if self.audioObjectType == 29:
+                self.psPresentFlag = 1
+            self.extensionSamplingFrequency = self._get_sampling_freq(r)
+            self.audioObjectType = self._get_audio_object_type(r)
+            if self.audioObjectType == 22:
+                self.extensionChannelConfiguration = r.bits(4)
+        else:
+            self.extensionAudioObjectType = 0
+
+        if self.audioObjectType in (1, 2, 3, 4, 6, 7, 17, 19, 20, 21, 22, 23):
+            try:
+                GASpecificConfig(r, self)
+            except NotImplementedError:
+                # unsupported, (warn?)
+                return
+        else:
+            # unsupported
+            return
+
+        if self.audioObjectType in (
+                17, 19, 20, 21, 22, 23, 24, 25, 26, 27, 39):
+            epConfig = r.bits(2)
+            if epConfig in (2, 3):
+                # unsupported
+                return
+
+        if self.extensionAudioObjectType != 5 and bits_left() >= 16:
+            syncExtensionType = r.bits(11)
+            if syncExtensionType == 0x2b7:
+                self.extensionAudioObjectType = self._get_audio_object_type(r)
+
+                if self.extensionAudioObjectType == 5:
+                    self.sbrPresentFlag = r.bits(1)
+                    if self.sbrPresentFlag == 1:
+                        self.extensionSamplingFrequency = \
+                            self._get_sampling_freq(r)
+                        if bits_left() >= 12:
+                            syncExtensionType = r.bits(11)
+                            if syncExtensionType == 0x548:
+                                self.psPresentFlag = r.bits(1)
+
+                if self.extensionAudioObjectType == 22:
+                    self.sbrPresentFlag = r.bits(1)
+                    if self.sbrPresentFlag == 1:
+                        self.extensionSamplingFrequency = \
+                            self._get_sampling_freq(r)
+                    self.extensionChannelConfiguration = r.bits(4)
+
+
+def GASpecificConfig(r, info):
+    """Reads GASpecificConfig which is needed to get the data after that
+    (there is no length defined to skip it) and to read program_config_element
+    which can contain channel counts.
+
+    May raise BitReaderError on error or
+    NotImplementedError if some reserved data was set.
+    """
+
+    assert isinstance(info, DecoderSpecificInfo)
+
+    r.skip(1)  # frameLengthFlag
+    dependsOnCoreCoder = r.bits(1)
+    if dependsOnCoreCoder:
+        r.skip(14)
+    extensionFlag = r.bits(1)
+    if not info.channelConfiguration:
+        pce = ProgramConfigElement(r)
+        info.pce_channels = pce.channels
+    if info.audioObjectType == 6 or info.audioObjectType == 20:
+        r.skip(3)
+    if extensionFlag:
+        if info.audioObjectType == 22:
+            r.skip(5 + 11)
+        if info.audioObjectType in (17, 19, 20, 23):
+            r.skip(1 + 1 + 1)
+        extensionFlag3 = r.bits(1)
+        if extensionFlag3 != 0:
+            raise NotImplementedError("extensionFlag3 set")
diff --git a/resources/lib/mutagen/mp4/_atom.py b/resources/lib/mutagen/mp4/_atom.py
new file mode 100644
index 00000000..f73eb556
--- /dev/null
+++ b/resources/lib/mutagen/mp4/_atom.py
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+import struct
+
+from mutagen._compat import PY2
+
+# This is not an exhaustive list of container atoms, but just the
+# ones this module needs to peek inside.
+_CONTAINERS = [b"moov", b"udta", b"trak", b"mdia", b"meta", b"ilst",
+               b"stbl", b"minf", b"moof", b"traf"]
+_SKIP_SIZE = {b"meta": 4}
+
+
+class AtomError(Exception):
+    pass
+
+
+class Atom(object):
+    """An individual atom.
+
+    Attributes:
+    children -- list child atoms (or None for non-container atoms)
+    length -- length of this atom, including length and name
+    datalength = -- length of this atom without length, name
+    name -- four byte name of the atom, as a str
+    offset -- location in the constructor-given fileobj of this atom
+
+    This structure should only be used internally by Mutagen.
+    """
+
+    children = None
+
+    def __init__(self, fileobj, level=0):
+        """May raise AtomError"""
+
+        self.offset = fileobj.tell()
+        try:
+            self.length, self.name = struct.unpack(">I4s", fileobj.read(8))
+        except struct.error:
+            raise AtomError("truncated data")
+        self._dataoffset = self.offset + 8
+        if self.length == 1:
+            try:
+                self.length, = struct.unpack(">Q", fileobj.read(8))
+            except struct.error:
+                raise AtomError("truncated data")
+            self._dataoffset += 8
+            if self.length < 16:
+                raise AtomError(
+                    "64 bit atom length can only be 16 and higher")
+        elif self.length == 0:
+            if level != 0:
+                raise AtomError(
+                    "only a top-level atom can have zero length")
+            # Only the last atom is supposed to have a zero-length, meaning it
+            # extends to the end of file.
+            fileobj.seek(0, 2)
+            self.length = fileobj.tell() - self.offset
+            fileobj.seek(self.offset + 8, 0)
+        elif self.length < 8:
+            raise AtomError(
+                "atom length can only be 0, 1 or 8 and higher")
+
+        if self.name in _CONTAINERS:
+            self.children = []
+            fileobj.seek(_SKIP_SIZE.get(self.name, 0), 1)
+            while fileobj.tell() < self.offset + self.length:
+                self.children.append(Atom(fileobj, level + 1))
+        else:
+            fileobj.seek(self.offset + self.length, 0)
+
+    @property
+    def datalength(self):
+        return self.length - (self._dataoffset - self.offset)
+
+    def read(self, fileobj):
+        """Return if all data could be read and the atom payload"""
+
+        fileobj.seek(self._dataoffset, 0)
+        data = fileobj.read(self.datalength)
+        return len(data) == self.datalength, data
+
+    @staticmethod
+    def render(name, data):
+        """Render raw atom data."""
+        # this raises OverflowError if Py_ssize_t can't handle the atom data
+        size = len(data) + 8
+        if size <= 0xFFFFFFFF:
+            return struct.pack(">I4s", size, name) + data
+        else:
+            return struct.pack(">I4sQ", 1, name, size + 8) + data
+
+    def findall(self, name, recursive=False):
+        """Recursively find all child atoms by specified name."""
+        if self.children is not None:
+            for child in self.children:
+                if child.name == name:
+                    yield child
+                if recursive:
+                    for atom in child.findall(name, True):
+                        yield atom
+
+    def __getitem__(self, remaining):
+        """Look up a child atom, potentially recursively.
+
+        e.g. atom['udta', 'meta'] => <Atom name='meta' ...>
+        """
+        if not remaining:
+            return self
+        elif self.children is None:
+            raise KeyError("%r is not a container" % self.name)
+        for child in self.children:
+            if child.name == remaining[0]:
+                return child[remaining[1:]]
+        else:
+            raise KeyError("%r not found" % remaining[0])
+
+    def __repr__(self):
+        cls = self.__class__.__name__
+        if self.children is None:
+            return "<%s name=%r length=%r offset=%r>" % (
+                cls, self.name, self.length, self.offset)
+        else:
+            children = "\n".join([" " + line for child in self.children
+                                  for line in repr(child).splitlines()])
+            return "<%s name=%r length=%r offset=%r\n%s>" % (
+                cls, self.name, self.length, self.offset, children)
+
+
+class Atoms(object):
+    """Root atoms in a given file.
+
+    Attributes:
+    atoms -- a list of top-level atoms as Atom objects
+
+    This structure should only be used internally by Mutagen.
+    """
+
+    def __init__(self, fileobj):
+        self.atoms = []
+        fileobj.seek(0, 2)
+        end = fileobj.tell()
+        fileobj.seek(0)
+        while fileobj.tell() + 8 <= end:
+            self.atoms.append(Atom(fileobj))
+
+    def path(self, *names):
+        """Look up and return the complete path of an atom.
+
+        For example, atoms.path('moov', 'udta', 'meta') will return a
+        list of three atoms, corresponding to the moov, udta, and meta
+        atoms.
+        """
+
+        path = [self]
+        for name in names:
+            path.append(path[-1][name, ])
+        return path[1:]
+
+    def __contains__(self, names):
+        try:
+            self[names]
+        except KeyError:
+            return False
+        return True
+
+    def __getitem__(self, names):
+        """Look up a child atom.
+
+        'names' may be a list of atoms (['moov', 'udta']) or a string
+        specifying the complete path ('moov.udta').
+        """
+
+        if PY2:
+            if isinstance(names, basestring):
+                names = names.split(b".")
+        else:
+            if isinstance(names, bytes):
+                names = names.split(b".")
+
+        for child in self.atoms:
+            if child.name == names[0]:
+                return child[names[1:]]
+        else:
+            raise KeyError("%r not found" % names[0])
+
+    def __repr__(self):
+        return "\n".join([repr(child) for child in self.atoms])
diff --git a/resources/lib/mutagen/mp4/_util.py b/resources/lib/mutagen/mp4/_util.py
new file mode 100644
index 00000000..9583334a
--- /dev/null
+++ b/resources/lib/mutagen/mp4/_util.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+from mutagen._util import cdata
+
+
+def parse_full_atom(data):
+    """Some atoms are versioned. Split them up in (version, flags, payload).
+    Can raise ValueError.
+    """
+
+    if len(data) < 4:
+        raise ValueError("not enough data")
+
+    version = ord(data[0:1])
+    flags = cdata.uint_be(b"\x00" + data[1:4])
+    return version, flags, data[4:]
diff --git a/resources/lib/mutagen/musepack.py b/resources/lib/mutagen/musepack.py
new file mode 100644
index 00000000..7880958b
--- /dev/null
+++ b/resources/lib/mutagen/musepack.py
@@ -0,0 +1,270 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Lukas Lalinsky
+# Copyright (C) 2012  Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Musepack audio streams with APEv2 tags.
+
+Musepack is an audio format originally based on the MPEG-1 Layer-2
+algorithms. Stream versions 4 through 7 are supported.
+
+For more information, see http://www.musepack.net/.
+"""
+
+__all__ = ["Musepack", "Open", "delete"]
+
+import struct
+
+from ._compat import endswith, xrange
+from mutagen import StreamInfo
+from mutagen.apev2 import APEv2File, error, delete
+from mutagen.id3 import BitPaddedInt
+from mutagen._util import cdata
+
+
+class MusepackHeaderError(error):
+    pass
+
+
+RATES = [44100, 48000, 37800, 32000]
+
+
+def _parse_sv8_int(fileobj, limit=9):
+    """Reads (max limit) bytes from fileobj until the MSB is zero.
+    All 7 LSB will be merged to a big endian uint.
+
+    Raises ValueError in case not MSB is zero, or EOFError in
+    case the file ended before limit is reached.
+
+    Returns (parsed number, number of bytes read)
+    """
+
+    num = 0
+    for i in xrange(limit):
+        c = fileobj.read(1)
+        if len(c) != 1:
+            raise EOFError
+        c = bytearray(c)
+        num = (num << 7) | (c[0] & 0x7F)
+        if not c[0] & 0x80:
+            return num, i + 1
+    if limit > 0:
+        raise ValueError
+    return 0, 0
+
+
+def _calc_sv8_gain(gain):
+    # 64.82 taken from mpcdec
+    return 64.82 - gain / 256.0
+
+
+def _calc_sv8_peak(peak):
+    return (10 ** (peak / (256.0 * 20.0)) / 65535.0)
+
+
+class MusepackInfo(StreamInfo):
+    """Musepack stream information.
+
+    Attributes:
+
+    * channels -- number of audio channels
+    * length -- file length in seconds, as a float
+    * sample_rate -- audio sampling rate in Hz
+    * bitrate -- audio bitrate, in bits per second
+    * version -- Musepack stream version
+
+    Optional Attributes:
+
+    * title_gain, title_peak -- Replay Gain and peak data for this song
+    * album_gain, album_peak -- Replay Gain and peak data for this album
+
+    These attributes are only available in stream version 7/8. The
+    gains are a float, +/- some dB. The peaks are a percentage [0..1] of
+    the maximum amplitude. This means to get a number comparable to
+    VorbisGain, you must multiply the peak by 2.
+    """
+
+    def __init__(self, fileobj):
+        header = fileobj.read(4)
+        if len(header) != 4:
+            raise MusepackHeaderError("not a Musepack file")
+
+        # Skip ID3v2 tags
+        if header[:3] == b"ID3":
+            header = fileobj.read(6)
+            if len(header) != 6:
+                raise MusepackHeaderError("not a Musepack file")
+            size = 10 + BitPaddedInt(header[2:6])
+            fileobj.seek(size)
+            header = fileobj.read(4)
+            if len(header) != 4:
+                raise MusepackHeaderError("not a Musepack file")
+
+        if header.startswith(b"MPCK"):
+            self.__parse_sv8(fileobj)
+        else:
+            self.__parse_sv467(fileobj)
+
+        if not self.bitrate and self.length != 0:
+            fileobj.seek(0, 2)
+            self.bitrate = int(round(fileobj.tell() * 8 / self.length))
+
+    def __parse_sv8(self, fileobj):
+        # SV8 http://trac.musepack.net/trac/wiki/SV8Specification
+
+        key_size = 2
+        mandatory_packets = [b"SH", b"RG"]
+
+        def check_frame_key(key):
+            if ((len(frame_type) != key_size) or
+                    (not b'AA' <= frame_type <= b'ZZ')):
+                raise MusepackHeaderError("Invalid frame key.")
+
+        frame_type = fileobj.read(key_size)
+        check_frame_key(frame_type)
+
+        while frame_type not in (b"AP", b"SE") and mandatory_packets:
+            try:
+                frame_size, slen = _parse_sv8_int(fileobj)
+            except (EOFError, ValueError):
+                raise MusepackHeaderError("Invalid packet size.")
+            data_size = frame_size - key_size - slen
+            # packets can be at maximum data_size big and are padded with zeros
+
+            if frame_type == b"SH":
+                mandatory_packets.remove(frame_type)
+                self.__parse_stream_header(fileobj, data_size)
+            elif frame_type == b"RG":
+                mandatory_packets.remove(frame_type)
+                self.__parse_replaygain_packet(fileobj, data_size)
+            else:
+                fileobj.seek(data_size, 1)
+
+            frame_type = fileobj.read(key_size)
+            check_frame_key(frame_type)
+
+        if mandatory_packets:
+            raise MusepackHeaderError("Missing mandatory packets: %s." %
+                                      ", ".join(map(repr, mandatory_packets)))
+
+        self.length = float(self.samples) / self.sample_rate
+        self.bitrate = 0
+
+    def __parse_stream_header(self, fileobj, data_size):
+        # skip CRC
+        fileobj.seek(4, 1)
+        remaining_size = data_size - 4
+
+        try:
+            self.version = bytearray(fileobj.read(1))[0]
+        except TypeError:
+            raise MusepackHeaderError("SH packet ended unexpectedly.")
+
+        remaining_size -= 1
+
+        try:
+            samples, l1 = _parse_sv8_int(fileobj)
+            samples_skip, l2 = _parse_sv8_int(fileobj)
+        except (EOFError, ValueError):
+            raise MusepackHeaderError(
+                "SH packet: Invalid sample counts.")
+
+        self.samples = samples - samples_skip
+        remaining_size -= l1 + l2
+
+        data = fileobj.read(remaining_size)
+        if len(data) != remaining_size:
+            raise MusepackHeaderError("SH packet ended unexpectedly.")
+        self.sample_rate = RATES[bytearray(data)[0] >> 5]
+        self.channels = (bytearray(data)[1] >> 4) + 1
+
+    def __parse_replaygain_packet(self, fileobj, data_size):
+        data = fileobj.read(data_size)
+        if data_size < 9:
+            raise MusepackHeaderError("Invalid RG packet size.")
+        if len(data) != data_size:
+            raise MusepackHeaderError("RG packet ended unexpectedly.")
+        title_gain = cdata.short_be(data[1:3])
+        title_peak = cdata.short_be(data[3:5])
+        album_gain = cdata.short_be(data[5:7])
+        album_peak = cdata.short_be(data[7:9])
+        if title_gain:
+            self.title_gain = _calc_sv8_gain(title_gain)
+        if title_peak:
+            self.title_peak = _calc_sv8_peak(title_peak)
+        if album_gain:
+            self.album_gain = _calc_sv8_gain(album_gain)
+        if album_peak:
+            self.album_peak = _calc_sv8_peak(album_peak)
+
+    def __parse_sv467(self, fileobj):
+        fileobj.seek(-4, 1)
+        header = fileobj.read(32)
+        if len(header) != 32:
+            raise MusepackHeaderError("not a Musepack file")
+
+        # SV7
+        if header.startswith(b"MP+"):
+            self.version = bytearray(header)[3] & 0xF
+            if self.version < 7:
+                raise MusepackHeaderError("not a Musepack file")
+            frames = cdata.uint_le(header[4:8])
+            flags = cdata.uint_le(header[8:12])
+
+            self.title_peak, self.title_gain = struct.unpack(
+                "<Hh", header[12:16])
+            self.album_peak, self.album_gain = struct.unpack(
+                "<Hh", header[16:20])
+            self.title_gain /= 100.0
+            self.album_gain /= 100.0
+            self.title_peak /= 65535.0
+            self.album_peak /= 65535.0
+
+            self.sample_rate = RATES[(flags >> 16) & 0x0003]
+            self.bitrate = 0
+        # SV4-SV6
+        else:
+            header_dword = cdata.uint_le(header[0:4])
+            self.version = (header_dword >> 11) & 0x03FF
+            if self.version < 4 or self.version > 6:
+                raise MusepackHeaderError("not a Musepack file")
+            self.bitrate = (header_dword >> 23) & 0x01FF
+            self.sample_rate = 44100
+            if self.version >= 5:
+                frames = cdata.uint_le(header[4:8])
+            else:
+                frames = cdata.ushort_le(header[6:8])
+            if self.version < 6:
+                frames -= 1
+        self.channels = 2
+        self.length = float(frames * 1152 - 576) / self.sample_rate
+
+    def pprint(self):
+        rg_data = []
+        if hasattr(self, "title_gain"):
+            rg_data.append(u"%+0.2f (title)" % self.title_gain)
+        if hasattr(self, "album_gain"):
+            rg_data.append(u"%+0.2f (album)" % self.album_gain)
+        rg_data = (rg_data and ", Gain: " + ", ".join(rg_data)) or ""
+
+        return u"Musepack SV%d, %.2f seconds, %d Hz, %d bps%s" % (
+            self.version, self.length, self.sample_rate, self.bitrate, rg_data)
+
+
+class Musepack(APEv2File):
+    _Info = MusepackInfo
+    _mimes = ["audio/x-musepack", "audio/x-mpc"]
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        filename = filename.lower()
+
+        return (header.startswith(b"MP+") + header.startswith(b"MPCK") +
+                endswith(filename, b".mpc"))
+
+
+Open = Musepack
diff --git a/resources/lib/mutagen/ogg.py b/resources/lib/mutagen/ogg.py
new file mode 100644
index 00000000..9961a966
--- /dev/null
+++ b/resources/lib/mutagen/ogg.py
@@ -0,0 +1,548 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Read and write Ogg bitstreams and pages.
+
+This module reads and writes a subset of the Ogg bitstream format
+version 0. It does *not* read or write Ogg Vorbis files! For that,
+you should use mutagen.oggvorbis.
+
+This implementation is based on the RFC 3533 standard found at
+http://www.xiph.org/ogg/doc/rfc3533.txt.
+"""
+
+import struct
+import sys
+import zlib
+
+from mutagen import FileType
+from mutagen._util import cdata, resize_bytes, MutagenError
+from ._compat import cBytesIO, reraise, chr_, izip, xrange
+
+
+class error(IOError, MutagenError):
+    """Ogg stream parsing errors."""
+
+    pass
+
+
+class OggPage(object):
+    """A single Ogg page (not necessarily a single encoded packet).
+
+    A page is a header of 26 bytes, followed by the length of the
+    data, followed by the data.
+
+    The constructor is givin a file-like object pointing to the start
+    of an Ogg page. After the constructor is finished it is pointing
+    to the start of the next page.
+
+    Attributes:
+
+    * version -- stream structure version (currently always 0)
+    * position -- absolute stream position (default -1)
+    * serial -- logical stream serial number (default 0)
+    * sequence -- page sequence number within logical stream (default 0)
+    * offset -- offset this page was read from (default None)
+    * complete -- if the last packet on this page is complete (default True)
+    * packets -- list of raw packet data (default [])
+
+    Note that if 'complete' is false, the next page's 'continued'
+    property must be true (so set both when constructing pages).
+
+    If a file-like object is supplied to the constructor, the above
+    attributes will be filled in based on it.
+    """
+
+    version = 0
+    __type_flags = 0
+    position = 0
+    serial = 0
+    sequence = 0
+    offset = None
+    complete = True
+
+    def __init__(self, fileobj=None):
+        self.packets = []
+
+        if fileobj is None:
+            return
+
+        self.offset = fileobj.tell()
+
+        header = fileobj.read(27)
+        if len(header) == 0:
+            raise EOFError
+
+        try:
+            (oggs, self.version, self.__type_flags,
+             self.position, self.serial, self.sequence,
+             crc, segments) = struct.unpack("<4sBBqIIiB", header)
+        except struct.error:
+            raise error("unable to read full header; got %r" % header)
+
+        if oggs != b"OggS":
+            raise error("read %r, expected %r, at 0x%x" % (
+                oggs, b"OggS", fileobj.tell() - 27))
+
+        if self.version != 0:
+            raise error("version %r unsupported" % self.version)
+
+        total = 0
+        lacings = []
+        lacing_bytes = fileobj.read(segments)
+        if len(lacing_bytes) != segments:
+            raise error("unable to read %r lacing bytes" % segments)
+        for c in bytearray(lacing_bytes):
+            total += c
+            if c < 255:
+                lacings.append(total)
+                total = 0
+        if total:
+            lacings.append(total)
+            self.complete = False
+
+        self.packets = [fileobj.read(l) for l in lacings]
+        if [len(p) for p in self.packets] != lacings:
+            raise error("unable to read full data")
+
+    def __eq__(self, other):
+        """Two Ogg pages are the same if they write the same data."""
+        try:
+            return (self.write() == other.write())
+        except AttributeError:
+            return False
+
+    __hash__ = object.__hash__
+
+    def __repr__(self):
+        attrs = ['version', 'position', 'serial', 'sequence', 'offset',
+                 'complete', 'continued', 'first', 'last']
+        values = ["%s=%r" % (attr, getattr(self, attr)) for attr in attrs]
+        return "<%s %s, %d bytes in %d packets>" % (
+            type(self).__name__, " ".join(values), sum(map(len, self.packets)),
+            len(self.packets))
+
+    def write(self):
+        """Return a string encoding of the page header and data.
+
+        A ValueError is raised if the data is too big to fit in a
+        single page.
+        """
+
+        data = [
+            struct.pack("<4sBBqIIi", b"OggS", self.version, self.__type_flags,
+                        self.position, self.serial, self.sequence, 0)
+        ]
+
+        lacing_data = []
+        for datum in self.packets:
+            quot, rem = divmod(len(datum), 255)
+            lacing_data.append(b"\xff" * quot + chr_(rem))
+        lacing_data = b"".join(lacing_data)
+        if not self.complete and lacing_data.endswith(b"\x00"):
+            lacing_data = lacing_data[:-1]
+        data.append(chr_(len(lacing_data)))
+        data.append(lacing_data)
+        data.extend(self.packets)
+        data = b"".join(data)
+
+        # Python's CRC is swapped relative to Ogg's needs.
+        # crc32 returns uint prior to py2.6 on some platforms, so force uint
+        crc = (~zlib.crc32(data.translate(cdata.bitswap), -1)) & 0xffffffff
+        # Although we're using to_uint_be, this actually makes the CRC
+        # a proper le integer, since Python's CRC is byteswapped.
+        crc = cdata.to_uint_be(crc).translate(cdata.bitswap)
+        data = data[:22] + crc + data[26:]
+        return data
+
+    @property
+    def size(self):
+        """Total frame size."""
+
+        size = 27  # Initial header size
+        for datum in self.packets:
+            quot, rem = divmod(len(datum), 255)
+            size += quot + 1
+        if not self.complete and rem == 0:
+            # Packet contains a multiple of 255 bytes and is not
+            # terminated, so we don't have a \x00 at the end.
+            size -= 1
+        size += sum(map(len, self.packets))
+        return size
+
+    def __set_flag(self, bit, val):
+        mask = 1 << bit
+        if val:
+            self.__type_flags |= mask
+        else:
+            self.__type_flags &= ~mask
+
+    continued = property(
+        lambda self: cdata.test_bit(self.__type_flags, 0),
+        lambda self, v: self.__set_flag(0, v),
+        doc="The first packet is continued from the previous page.")
+
+    first = property(
+        lambda self: cdata.test_bit(self.__type_flags, 1),
+        lambda self, v: self.__set_flag(1, v),
+        doc="This is the first page of a logical bitstream.")
+
+    last = property(
+        lambda self: cdata.test_bit(self.__type_flags, 2),
+        lambda self, v: self.__set_flag(2, v),
+        doc="This is the last page of a logical bitstream.")
+
+    @staticmethod
+    def renumber(fileobj, serial, start):
+        """Renumber pages belonging to a specified logical stream.
+
+        fileobj must be opened with mode r+b or w+b.
+
+        Starting at page number 'start', renumber all pages belonging
+        to logical stream 'serial'. Other pages will be ignored.
+
+        fileobj must point to the start of a valid Ogg page; any
+        occuring after it and part of the specified logical stream
+        will be numbered. No adjustment will be made to the data in
+        the pages nor the granule position; only the page number, and
+        so also the CRC.
+
+        If an error occurs (e.g. non-Ogg data is found), fileobj will
+        be left pointing to the place in the stream the error occured,
+        but the invalid data will be left intact (since this function
+        does not change the total file size).
+        """
+
+        number = start
+        while True:
+            try:
+                page = OggPage(fileobj)
+            except EOFError:
+                break
+            else:
+                if page.serial != serial:
+                    # Wrong stream, skip this page.
+                    continue
+                # Changing the number can't change the page size,
+                # so seeking back based on the current size is safe.
+                fileobj.seek(-page.size, 1)
+            page.sequence = number
+            fileobj.write(page.write())
+            fileobj.seek(page.offset + page.size, 0)
+            number += 1
+
+    @staticmethod
+    def to_packets(pages, strict=False):
+        """Construct a list of packet data from a list of Ogg pages.
+
+        If strict is true, the first page must start a new packet,
+        and the last page must end the last packet.
+        """
+
+        serial = pages[0].serial
+        sequence = pages[0].sequence
+        packets = []
+
+        if strict:
+            if pages[0].continued:
+                raise ValueError("first packet is continued")
+            if not pages[-1].complete:
+                raise ValueError("last packet does not complete")
+        elif pages and pages[0].continued:
+            packets.append([b""])
+
+        for page in pages:
+            if serial != page.serial:
+                raise ValueError("invalid serial number in %r" % page)
+            elif sequence != page.sequence:
+                raise ValueError("bad sequence number in %r" % page)
+            else:
+                sequence += 1
+
+            if page.continued:
+                packets[-1].append(page.packets[0])
+            else:
+                packets.append([page.packets[0]])
+            packets.extend([p] for p in page.packets[1:])
+
+        return [b"".join(p) for p in packets]
+
+    @classmethod
+    def _from_packets_try_preserve(cls, packets, old_pages):
+        """Like from_packets but in case the size and number of the packets
+        is the same as in the given pages the layout of the pages will
+        be copied (the page size and number will match).
+
+        If the packets don't match this behaves like::
+
+            OggPage.from_packets(packets, sequence=old_pages[0].sequence)
+        """
+
+        old_packets = cls.to_packets(old_pages)
+
+        if [len(p) for p in packets] != [len(p) for p in old_packets]:
+            # doesn't match, fall back
+            return cls.from_packets(packets, old_pages[0].sequence)
+
+        new_data = b"".join(packets)
+        new_pages = []
+        for old in old_pages:
+            new = OggPage()
+            new.sequence = old.sequence
+            new.complete = old.complete
+            new.continued = old.continued
+            new.position = old.position
+            for p in old.packets:
+                data, new_data = new_data[:len(p)], new_data[len(p):]
+                new.packets.append(data)
+            new_pages.append(new)
+        assert not new_data
+
+        return new_pages
+
+    @staticmethod
+    def from_packets(packets, sequence=0, default_size=4096,
+                     wiggle_room=2048):
+        """Construct a list of Ogg pages from a list of packet data.
+
+        The algorithm will generate pages of approximately
+        default_size in size (rounded down to the nearest multiple of
+        255). However, it will also allow pages to increase to
+        approximately default_size + wiggle_room if allowing the
+        wiggle room would finish a packet (only one packet will be
+        finished in this way per page; if the next packet would fit
+        into the wiggle room, it still starts on a new page).
+
+        This method reduces packet fragmentation when packet sizes are
+        slightly larger than the default page size, while still
+        ensuring most pages are of the average size.
+
+        Pages are numbered started at 'sequence'; other information is
+        uninitialized.
+        """
+
+        chunk_size = (default_size // 255) * 255
+
+        pages = []
+
+        page = OggPage()
+        page.sequence = sequence
+
+        for packet in packets:
+            page.packets.append(b"")
+            while packet:
+                data, packet = packet[:chunk_size], packet[chunk_size:]
+                if page.size < default_size and len(page.packets) < 255:
+                    page.packets[-1] += data
+                else:
+                    # If we've put any packet data into this page yet,
+                    # we need to mark it incomplete. However, we can
+                    # also have just started this packet on an already
+                    # full page, in which case, just start the new
+                    # page with this packet.
+                    if page.packets[-1]:
+                        page.complete = False
+                        if len(page.packets) == 1:
+                            page.position = -1
+                    else:
+                        page.packets.pop(-1)
+                    pages.append(page)
+                    page = OggPage()
+                    page.continued = not pages[-1].complete
+                    page.sequence = pages[-1].sequence + 1
+                    page.packets.append(data)
+
+                if len(packet) < wiggle_room:
+                    page.packets[-1] += packet
+                    packet = b""
+
+        if page.packets:
+            pages.append(page)
+
+        return pages
+
+    @classmethod
+    def replace(cls, fileobj, old_pages, new_pages):
+        """Replace old_pages with new_pages within fileobj.
+
+        old_pages must have come from reading fileobj originally.
+        new_pages are assumed to have the 'same' data as old_pages,
+        and so the serial and sequence numbers will be copied, as will
+        the flags for the first and last pages.
+
+        fileobj will be resized and pages renumbered as necessary. As
+        such, it must be opened r+b or w+b.
+        """
+
+        if not len(old_pages) or not len(new_pages):
+            raise ValueError("empty pages list not allowed")
+
+        # Number the new pages starting from the first old page.
+        first = old_pages[0].sequence
+        for page, seq in izip(new_pages,
+                              xrange(first, first + len(new_pages))):
+            page.sequence = seq
+            page.serial = old_pages[0].serial
+
+        new_pages[0].first = old_pages[0].first
+        new_pages[0].last = old_pages[0].last
+        new_pages[0].continued = old_pages[0].continued
+
+        new_pages[-1].first = old_pages[-1].first
+        new_pages[-1].last = old_pages[-1].last
+        new_pages[-1].complete = old_pages[-1].complete
+        if not new_pages[-1].complete and len(new_pages[-1].packets) == 1:
+            new_pages[-1].position = -1
+
+        new_data = [cls.write(p) for p in new_pages]
+
+        # Add dummy data or merge the remaining data together so multiple
+        # new pages replace an old one
+        pages_diff = len(old_pages) - len(new_data)
+        if pages_diff > 0:
+            new_data.extend([b""] * pages_diff)
+        elif pages_diff < 0:
+            new_data[pages_diff - 1:] = [b"".join(new_data[pages_diff - 1:])]
+
+        # Replace pages one by one. If the sizes match no resize happens.
+        offset_adjust = 0
+        new_data_end = None
+        assert len(old_pages) == len(new_data)
+        for old_page, data in izip(old_pages, new_data):
+            offset = old_page.offset + offset_adjust
+            data_size = len(data)
+            resize_bytes(fileobj, old_page.size, data_size, offset)
+            fileobj.seek(offset, 0)
+            fileobj.write(data)
+            new_data_end = offset + data_size
+            offset_adjust += (data_size - old_page.size)
+
+        # Finally, if there's any discrepency in length, we need to
+        # renumber the pages for the logical stream.
+        if len(old_pages) != len(new_pages):
+            fileobj.seek(new_data_end, 0)
+            serial = new_pages[-1].serial
+            sequence = new_pages[-1].sequence + 1
+            cls.renumber(fileobj, serial, sequence)
+
+    @staticmethod
+    def find_last(fileobj, serial):
+        """Find the last page of the stream 'serial'.
+
+        If the file is not multiplexed this function is fast. If it is,
+        it must read the whole the stream.
+
+        This finds the last page in the actual file object, or the last
+        page in the stream (with eos set), whichever comes first.
+        """
+
+        # For non-muxed streams, look at the last page.
+        try:
+            fileobj.seek(-256 * 256, 2)
+        except IOError:
+            # The file is less than 64k in length.
+            fileobj.seek(0)
+        data = fileobj.read()
+        try:
+            index = data.rindex(b"OggS")
+        except ValueError:
+            raise error("unable to find final Ogg header")
+        bytesobj = cBytesIO(data[index:])
+        best_page = None
+        try:
+            page = OggPage(bytesobj)
+        except error:
+            pass
+        else:
+            if page.serial == serial:
+                if page.last:
+                    return page
+                else:
+                    best_page = page
+            else:
+                best_page = None
+
+        # The stream is muxed, so use the slow way.
+        fileobj.seek(0)
+        try:
+            page = OggPage(fileobj)
+            while not page.last:
+                page = OggPage(fileobj)
+                while page.serial != serial:
+                    page = OggPage(fileobj)
+                best_page = page
+            return page
+        except error:
+            return best_page
+        except EOFError:
+            return best_page
+
+
+class OggFileType(FileType):
+    """An generic Ogg file."""
+
+    _Info = None
+    _Tags = None
+    _Error = None
+    _mimes = ["application/ogg", "application/x-ogg"]
+
+    def load(self, filename):
+        """Load file information from a filename."""
+
+        self.filename = filename
+        with open(filename, "rb") as fileobj:
+            try:
+                self.info = self._Info(fileobj)
+                self.tags = self._Tags(fileobj, self.info)
+                self.info._post_tags(fileobj)
+            except error as e:
+                reraise(self._Error, e, sys.exc_info()[2])
+            except EOFError:
+                raise self._Error("no appropriate stream found")
+
+    def delete(self, filename=None):
+        """Remove tags from a file.
+
+        If no filename is given, the one most recently loaded is used.
+        """
+
+        if filename is None:
+            filename = self.filename
+
+        self.tags.clear()
+        # TODO: we should delegate the deletion to the subclass and not through
+        # _inject.
+        with open(filename, "rb+") as fileobj:
+            try:
+                self.tags._inject(fileobj, lambda x: 0)
+            except error as e:
+                reraise(self._Error, e, sys.exc_info()[2])
+            except EOFError:
+                raise self._Error("no appropriate stream found")
+
+    def add_tags(self):
+        raise self._Error
+
+    def save(self, filename=None, padding=None):
+        """Save a tag to a file.
+
+        If no filename is given, the one most recently loaded is used.
+        """
+
+        if filename is None:
+            filename = self.filename
+        fileobj = open(filename, "rb+")
+        try:
+            try:
+                self.tags._inject(fileobj, padding)
+            except error as e:
+                reraise(self._Error, e, sys.exc_info()[2])
+            except EOFError:
+                raise self._Error("no appropriate stream found")
+        finally:
+            fileobj.close()
diff --git a/resources/lib/mutagen/oggflac.py b/resources/lib/mutagen/oggflac.py
new file mode 100644
index 00000000..b86226ca
--- /dev/null
+++ b/resources/lib/mutagen/oggflac.py
@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Read and write Ogg FLAC comments.
+
+This module handles FLAC files wrapped in an Ogg bitstream. The first
+FLAC stream found is used. For 'naked' FLACs, see mutagen.flac.
+
+This module is based off the specification at
+http://flac.sourceforge.net/ogg_mapping.html.
+"""
+
+__all__ = ["OggFLAC", "Open", "delete"]
+
+import struct
+
+from ._compat import cBytesIO
+
+from mutagen import StreamInfo
+from mutagen.flac import StreamInfo as FLACStreamInfo, error as FLACError
+from mutagen._vorbis import VCommentDict
+from mutagen.ogg import OggPage, OggFileType, error as OggError
+
+
+class error(OggError):
+    pass
+
+
+class OggFLACHeaderError(error):
+    pass
+
+
+class OggFLACStreamInfo(StreamInfo):
+    """Ogg FLAC stream info."""
+
+    length = 0
+    """File length in seconds, as a float"""
+
+    channels = 0
+    """Number of channels"""
+
+    sample_rate = 0
+    """Sample rate in Hz"""
+
+    def __init__(self, fileobj):
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"\x7FFLAC"):
+            page = OggPage(fileobj)
+        major, minor, self.packets, flac = struct.unpack(
+            ">BBH4s", page.packets[0][5:13])
+        if flac != b"fLaC":
+            raise OggFLACHeaderError("invalid FLAC marker (%r)" % flac)
+        elif (major, minor) != (1, 0):
+            raise OggFLACHeaderError(
+                "unknown mapping version: %d.%d" % (major, minor))
+        self.serial = page.serial
+
+        # Skip over the block header.
+        stringobj = cBytesIO(page.packets[0][17:])
+
+        try:
+            flac_info = FLACStreamInfo(stringobj)
+        except FLACError as e:
+            raise OggFLACHeaderError(e)
+
+        for attr in ["min_blocksize", "max_blocksize", "sample_rate",
+                     "channels", "bits_per_sample", "total_samples", "length"]:
+            setattr(self, attr, getattr(flac_info, attr))
+
+    def _post_tags(self, fileobj):
+        if self.length:
+            return
+        page = OggPage.find_last(fileobj, self.serial)
+        self.length = page.position / float(self.sample_rate)
+
+    def pprint(self):
+        return u"Ogg FLAC, %.2f seconds, %d Hz" % (
+            self.length, self.sample_rate)
+
+
+class OggFLACVComment(VCommentDict):
+
+    def __init__(self, fileobj, info):
+        # data should be pointing at the start of an Ogg page, after
+        # the first FLAC page.
+        pages = []
+        complete = False
+        while not complete:
+            page = OggPage(fileobj)
+            if page.serial == info.serial:
+                pages.append(page)
+                complete = page.complete or (len(page.packets) > 1)
+        comment = cBytesIO(OggPage.to_packets(pages)[0][4:])
+        super(OggFLACVComment, self).__init__(comment, framing=False)
+
+    def _inject(self, fileobj, padding_func):
+        """Write tag data into the FLAC Vorbis comment packet/page."""
+
+        # Ogg FLAC has no convenient data marker like Vorbis, but the
+        # second packet - and second page - must be the comment data.
+        fileobj.seek(0)
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"\x7FFLAC"):
+            page = OggPage(fileobj)
+
+        first_page = page
+        while not (page.sequence == 1 and page.serial == first_page.serial):
+            page = OggPage(fileobj)
+
+        old_pages = [page]
+        while not (old_pages[-1].complete or len(old_pages[-1].packets) > 1):
+            page = OggPage(fileobj)
+            if page.serial == first_page.serial:
+                old_pages.append(page)
+
+        packets = OggPage.to_packets(old_pages, strict=False)
+
+        # Set the new comment block.
+        data = self.write(framing=False)
+        data = packets[0][:1] + struct.pack(">I", len(data))[-3:] + data
+        packets[0] = data
+
+        new_pages = OggPage.from_packets(packets, old_pages[0].sequence)
+        OggPage.replace(fileobj, old_pages, new_pages)
+
+
+class OggFLAC(OggFileType):
+    """An Ogg FLAC file."""
+
+    _Info = OggFLACStreamInfo
+    _Tags = OggFLACVComment
+    _Error = OggFLACHeaderError
+    _mimes = ["audio/x-oggflac"]
+
+    info = None
+    """A `OggFLACStreamInfo`"""
+
+    tags = None
+    """A `VCommentDict`"""
+
+    def save(self, filename=None):
+        return super(OggFLAC, self).save(filename)
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return (header.startswith(b"OggS") * (
+            (b"FLAC" in header) + (b"fLaC" in header)))
+
+
+Open = OggFLAC
+
+
+def delete(filename):
+    """Remove tags from a file."""
+
+    OggFLAC(filename).delete()
diff --git a/resources/lib/mutagen/oggopus.py b/resources/lib/mutagen/oggopus.py
new file mode 100644
index 00000000..7154e479
--- /dev/null
+++ b/resources/lib/mutagen/oggopus.py
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2012, 2013  Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Read and write Ogg Opus comments.
+
+This module handles Opus files wrapped in an Ogg bitstream. The
+first Opus stream found is used.
+
+Based on http://tools.ietf.org/html/draft-terriberry-oggopus-01
+"""
+
+__all__ = ["OggOpus", "Open", "delete"]
+
+import struct
+
+from mutagen import StreamInfo
+from mutagen._compat import BytesIO
+from mutagen._util import get_size
+from mutagen._tags import PaddingInfo
+from mutagen._vorbis import VCommentDict
+from mutagen.ogg import OggPage, OggFileType, error as OggError
+
+
+class error(OggError):
+    pass
+
+
+class OggOpusHeaderError(error):
+    pass
+
+
+class OggOpusInfo(StreamInfo):
+    """Ogg Opus stream information."""
+
+    length = 0
+    """File length in seconds, as a float"""
+
+    channels = 0
+    """Number of channels"""
+
+    def __init__(self, fileobj):
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"OpusHead"):
+            page = OggPage(fileobj)
+
+        self.serial = page.serial
+
+        if not page.first:
+            raise OggOpusHeaderError(
+                "page has ID header, but doesn't start a stream")
+
+        (version, self.channels, pre_skip, orig_sample_rate, output_gain,
+         channel_map) = struct.unpack("<BBHIhB", page.packets[0][8:19])
+
+        self.__pre_skip = pre_skip
+
+        # only the higher 4 bits change on incombatible changes
+        major = version >> 4
+        if major != 0:
+            raise OggOpusHeaderError("version %r unsupported" % major)
+
+    def _post_tags(self, fileobj):
+        page = OggPage.find_last(fileobj, self.serial)
+        self.length = (page.position - self.__pre_skip) / float(48000)
+
+    def pprint(self):
+        return u"Ogg Opus, %.2f seconds" % (self.length)
+
+
+class OggOpusVComment(VCommentDict):
+    """Opus comments embedded in an Ogg bitstream."""
+
+    def __get_comment_pages(self, fileobj, info):
+        # find the first tags page with the right serial
+        page = OggPage(fileobj)
+        while ((info.serial != page.serial) or
+                not page.packets[0].startswith(b"OpusTags")):
+            page = OggPage(fileobj)
+
+        # get all comment pages
+        pages = [page]
+        while not (pages[-1].complete or len(pages[-1].packets) > 1):
+            page = OggPage(fileobj)
+            if page.serial == pages[0].serial:
+                pages.append(page)
+
+        return pages
+
+    def __init__(self, fileobj, info):
+        pages = self.__get_comment_pages(fileobj, info)
+        data = OggPage.to_packets(pages)[0][8:]  # Strip OpusTags
+        fileobj = BytesIO(data)
+        super(OggOpusVComment, self).__init__(fileobj, framing=False)
+        self._padding = len(data) - self._size
+
+        # in case the LSB of the first byte after v-comment is 1, preserve the
+        # following data
+        padding_flag = fileobj.read(1)
+        if padding_flag and ord(padding_flag) & 0x1:
+            self._pad_data = padding_flag + fileobj.read()
+            self._padding = 0  # we have to preserve, so no padding
+        else:
+            self._pad_data = b""
+
+    def _inject(self, fileobj, padding_func):
+        fileobj.seek(0)
+        info = OggOpusInfo(fileobj)
+        old_pages = self.__get_comment_pages(fileobj, info)
+
+        packets = OggPage.to_packets(old_pages)
+        vcomment_data = b"OpusTags" + self.write(framing=False)
+
+        if self._pad_data:
+            # if we have padding data to preserver we can't add more padding
+            # as long as we don't know the structure of what follows
+            packets[0] = vcomment_data + self._pad_data
+        else:
+            content_size = get_size(fileobj) - len(packets[0])  # approx
+            padding_left = len(packets[0]) - len(vcomment_data)
+            info = PaddingInfo(padding_left, content_size)
+            new_padding = info._get_padding(padding_func)
+            packets[0] = vcomment_data + b"\x00" * new_padding
+
+        new_pages = OggPage._from_packets_try_preserve(packets, old_pages)
+        OggPage.replace(fileobj, old_pages, new_pages)
+
+
+class OggOpus(OggFileType):
+    """An Ogg Opus file."""
+
+    _Info = OggOpusInfo
+    _Tags = OggOpusVComment
+    _Error = OggOpusHeaderError
+    _mimes = ["audio/ogg", "audio/ogg; codecs=opus"]
+
+    info = None
+    """A `OggOpusInfo`"""
+
+    tags = None
+    """A `VCommentDict`"""
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return (header.startswith(b"OggS") * (b"OpusHead" in header))
+
+
+Open = OggOpus
+
+
+def delete(filename):
+    """Remove tags from a file."""
+
+    OggOpus(filename).delete()
diff --git a/resources/lib/mutagen/oggspeex.py b/resources/lib/mutagen/oggspeex.py
new file mode 100644
index 00000000..9b16930b
--- /dev/null
+++ b/resources/lib/mutagen/oggspeex.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2006 Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Read and write Ogg Speex comments.
+
+This module handles Speex files wrapped in an Ogg bitstream. The
+first Speex stream found is used.
+
+Read more about Ogg Speex at http://www.speex.org/. This module is
+based on the specification at http://www.speex.org/manual2/node7.html
+and clarifications after personal communication with Jean-Marc,
+http://lists.xiph.org/pipermail/speex-dev/2006-July/004676.html.
+"""
+
+__all__ = ["OggSpeex", "Open", "delete"]
+
+from mutagen import StreamInfo
+from mutagen._vorbis import VCommentDict
+from mutagen.ogg import OggPage, OggFileType, error as OggError
+from mutagen._util import cdata, get_size
+from mutagen._tags import PaddingInfo
+
+
+class error(OggError):
+    pass
+
+
+class OggSpeexHeaderError(error):
+    pass
+
+
+class OggSpeexInfo(StreamInfo):
+    """Ogg Speex stream information."""
+
+    length = 0
+    """file length in seconds, as a float"""
+
+    channels = 0
+    """number of channels"""
+
+    bitrate = 0
+    """nominal bitrate in bits per second.
+
+    The reference encoder does not set the bitrate; in this case,
+    the bitrate will be 0.
+    """
+
+    def __init__(self, fileobj):
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"Speex   "):
+            page = OggPage(fileobj)
+        if not page.first:
+            raise OggSpeexHeaderError(
+                "page has ID header, but doesn't start a stream")
+        self.sample_rate = cdata.uint_le(page.packets[0][36:40])
+        self.channels = cdata.uint_le(page.packets[0][48:52])
+        self.bitrate = max(0, cdata.int_le(page.packets[0][52:56]))
+        self.serial = page.serial
+
+    def _post_tags(self, fileobj):
+        page = OggPage.find_last(fileobj, self.serial)
+        self.length = page.position / float(self.sample_rate)
+
+    def pprint(self):
+        return u"Ogg Speex, %.2f seconds" % self.length
+
+
+class OggSpeexVComment(VCommentDict):
+    """Speex comments embedded in an Ogg bitstream."""
+
+    def __init__(self, fileobj, info):
+        pages = []
+        complete = False
+        while not complete:
+            page = OggPage(fileobj)
+            if page.serial == info.serial:
+                pages.append(page)
+                complete = page.complete or (len(page.packets) > 1)
+        data = OggPage.to_packets(pages)[0]
+        super(OggSpeexVComment, self).__init__(data, framing=False)
+        self._padding = len(data) - self._size
+
+    def _inject(self, fileobj, padding_func):
+        """Write tag data into the Speex comment packet/page."""
+
+        fileobj.seek(0)
+
+        # Find the first header page, with the stream info.
+        # Use it to get the serial number.
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"Speex   "):
+            page = OggPage(fileobj)
+
+        # Look for the next page with that serial number, it'll start
+        # the comment packet.
+        serial = page.serial
+        page = OggPage(fileobj)
+        while page.serial != serial:
+            page = OggPage(fileobj)
+
+        # Then find all the pages with the comment packet.
+        old_pages = [page]
+        while not (old_pages[-1].complete or len(old_pages[-1].packets) > 1):
+            page = OggPage(fileobj)
+            if page.serial == old_pages[0].serial:
+                old_pages.append(page)
+
+        packets = OggPage.to_packets(old_pages, strict=False)
+
+        content_size = get_size(fileobj) - len(packets[0])  # approx
+        vcomment_data = self.write(framing=False)
+        padding_left = len(packets[0]) - len(vcomment_data)
+
+        info = PaddingInfo(padding_left, content_size)
+        new_padding = info._get_padding(padding_func)
+
+        # Set the new comment packet.
+        packets[0] = vcomment_data + b"\x00" * new_padding
+
+        new_pages = OggPage._from_packets_try_preserve(packets, old_pages)
+        OggPage.replace(fileobj, old_pages, new_pages)
+
+
+class OggSpeex(OggFileType):
+    """An Ogg Speex file."""
+
+    _Info = OggSpeexInfo
+    _Tags = OggSpeexVComment
+    _Error = OggSpeexHeaderError
+    _mimes = ["audio/x-speex"]
+
+    info = None
+    """A `OggSpeexInfo`"""
+
+    tags = None
+    """A `VCommentDict`"""
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return (header.startswith(b"OggS") * (b"Speex   " in header))
+
+
+Open = OggSpeex
+
+
+def delete(filename):
+    """Remove tags from a file."""
+
+    OggSpeex(filename).delete()
diff --git a/resources/lib/mutagen/oggtheora.py b/resources/lib/mutagen/oggtheora.py
new file mode 100644
index 00000000..122e7d4b
--- /dev/null
+++ b/resources/lib/mutagen/oggtheora.py
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2006 Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Read and write Ogg Theora comments.
+
+This module handles Theora files wrapped in an Ogg bitstream. The
+first Theora stream found is used.
+
+Based on the specification at http://theora.org/doc/Theora_I_spec.pdf.
+"""
+
+__all__ = ["OggTheora", "Open", "delete"]
+
+import struct
+
+from mutagen import StreamInfo
+from mutagen._vorbis import VCommentDict
+from mutagen._util import cdata, get_size
+from mutagen._tags import PaddingInfo
+from mutagen.ogg import OggPage, OggFileType, error as OggError
+
+
+class error(OggError):
+    pass
+
+
+class OggTheoraHeaderError(error):
+    pass
+
+
+class OggTheoraInfo(StreamInfo):
+    """Ogg Theora stream information."""
+
+    length = 0
+    """File length in seconds, as a float"""
+
+    fps = 0
+    """Video frames per second, as a float"""
+
+    bitrate = 0
+    """Bitrate in bps (int)"""
+
+    def __init__(self, fileobj):
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"\x80theora"):
+            page = OggPage(fileobj)
+        if not page.first:
+            raise OggTheoraHeaderError(
+                "page has ID header, but doesn't start a stream")
+        data = page.packets[0]
+        vmaj, vmin = struct.unpack("2B", data[7:9])
+        if (vmaj, vmin) != (3, 2):
+            raise OggTheoraHeaderError(
+                "found Theora version %d.%d != 3.2" % (vmaj, vmin))
+        fps_num, fps_den = struct.unpack(">2I", data[22:30])
+        self.fps = fps_num / float(fps_den)
+        self.bitrate = cdata.uint_be(b"\x00" + data[37:40])
+        self.granule_shift = (cdata.ushort_be(data[40:42]) >> 5) & 0x1F
+        self.serial = page.serial
+
+    def _post_tags(self, fileobj):
+        page = OggPage.find_last(fileobj, self.serial)
+        position = page.position
+        mask = (1 << self.granule_shift) - 1
+        frames = (position >> self.granule_shift) + (position & mask)
+        self.length = frames / float(self.fps)
+
+    def pprint(self):
+        return u"Ogg Theora, %.2f seconds, %d bps" % (self.length,
+                                                      self.bitrate)
+
+
+class OggTheoraCommentDict(VCommentDict):
+    """Theora comments embedded in an Ogg bitstream."""
+
+    def __init__(self, fileobj, info):
+        pages = []
+        complete = False
+        while not complete:
+            page = OggPage(fileobj)
+            if page.serial == info.serial:
+                pages.append(page)
+                complete = page.complete or (len(page.packets) > 1)
+        data = OggPage.to_packets(pages)[0][7:]
+        super(OggTheoraCommentDict, self).__init__(data, framing=False)
+        self._padding = len(data) - self._size
+
+    def _inject(self, fileobj, padding_func):
+        """Write tag data into the Theora comment packet/page."""
+
+        fileobj.seek(0)
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"\x81theora"):
+            page = OggPage(fileobj)
+
+        old_pages = [page]
+        while not (old_pages[-1].complete or len(old_pages[-1].packets) > 1):
+            page = OggPage(fileobj)
+            if page.serial == old_pages[0].serial:
+                old_pages.append(page)
+
+        packets = OggPage.to_packets(old_pages, strict=False)
+
+        content_size = get_size(fileobj) - len(packets[0])  # approx
+        vcomment_data = b"\x81theora" + self.write(framing=False)
+        padding_left = len(packets[0]) - len(vcomment_data)
+
+        info = PaddingInfo(padding_left, content_size)
+        new_padding = info._get_padding(padding_func)
+
+        packets[0] = vcomment_data + b"\x00" * new_padding
+
+        new_pages = OggPage._from_packets_try_preserve(packets, old_pages)
+        OggPage.replace(fileobj, old_pages, new_pages)
+
+
+class OggTheora(OggFileType):
+    """An Ogg Theora file."""
+
+    _Info = OggTheoraInfo
+    _Tags = OggTheoraCommentDict
+    _Error = OggTheoraHeaderError
+    _mimes = ["video/x-theora"]
+
+    info = None
+    """A `OggTheoraInfo`"""
+
+    tags = None
+    """A `VCommentDict`"""
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return (header.startswith(b"OggS") *
+                ((b"\x80theora" in header) + (b"\x81theora" in header)) * 2)
+
+
+Open = OggTheora
+
+
+def delete(filename):
+    """Remove tags from a file."""
+
+    OggTheora(filename).delete()
diff --git a/resources/lib/mutagen/oggvorbis.py b/resources/lib/mutagen/oggvorbis.py
new file mode 100644
index 00000000..b058a0c1
--- /dev/null
+++ b/resources/lib/mutagen/oggvorbis.py
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2006 Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""Read and write Ogg Vorbis comments.
+
+This module handles Vorbis files wrapped in an Ogg bitstream. The
+first Vorbis stream found is used.
+
+Read more about Ogg Vorbis at http://vorbis.com/. This module is based
+on the specification at http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html.
+"""
+
+__all__ = ["OggVorbis", "Open", "delete"]
+
+import struct
+
+from mutagen import StreamInfo
+from mutagen._vorbis import VCommentDict
+from mutagen._util import get_size
+from mutagen._tags import PaddingInfo
+from mutagen.ogg import OggPage, OggFileType, error as OggError
+
+
+class error(OggError):
+    pass
+
+
+class OggVorbisHeaderError(error):
+    pass
+
+
+class OggVorbisInfo(StreamInfo):
+    """Ogg Vorbis stream information."""
+
+    length = 0
+    """File length in seconds, as a float"""
+
+    channels = 0
+    """Number of channels"""
+
+    bitrate = 0
+    """Nominal ('average') bitrate in bits per second, as an int"""
+
+    sample_rate = 0
+    """Sample rate in Hz"""
+
+    def __init__(self, fileobj):
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"\x01vorbis"):
+            page = OggPage(fileobj)
+        if not page.first:
+            raise OggVorbisHeaderError(
+                "page has ID header, but doesn't start a stream")
+        (self.channels, self.sample_rate, max_bitrate, nominal_bitrate,
+         min_bitrate) = struct.unpack("<B4i", page.packets[0][11:28])
+        self.serial = page.serial
+
+        max_bitrate = max(0, max_bitrate)
+        min_bitrate = max(0, min_bitrate)
+        nominal_bitrate = max(0, nominal_bitrate)
+
+        if nominal_bitrate == 0:
+            self.bitrate = (max_bitrate + min_bitrate) // 2
+        elif max_bitrate and max_bitrate < nominal_bitrate:
+            # If the max bitrate is less than the nominal, we know
+            # the nominal is wrong.
+            self.bitrate = max_bitrate
+        elif min_bitrate > nominal_bitrate:
+            self.bitrate = min_bitrate
+        else:
+            self.bitrate = nominal_bitrate
+
+    def _post_tags(self, fileobj):
+        page = OggPage.find_last(fileobj, self.serial)
+        self.length = page.position / float(self.sample_rate)
+
+    def pprint(self):
+        return u"Ogg Vorbis, %.2f seconds, %d bps" % (
+            self.length, self.bitrate)
+
+
+class OggVCommentDict(VCommentDict):
+    """Vorbis comments embedded in an Ogg bitstream."""
+
+    def __init__(self, fileobj, info):
+        pages = []
+        complete = False
+        while not complete:
+            page = OggPage(fileobj)
+            if page.serial == info.serial:
+                pages.append(page)
+                complete = page.complete or (len(page.packets) > 1)
+        data = OggPage.to_packets(pages)[0][7:]  # Strip off "\x03vorbis".
+        super(OggVCommentDict, self).__init__(data)
+        self._padding = len(data) - self._size
+
+    def _inject(self, fileobj, padding_func):
+        """Write tag data into the Vorbis comment packet/page."""
+
+        # Find the old pages in the file; we'll need to remove them,
+        # plus grab any stray setup packet data out of them.
+        fileobj.seek(0)
+        page = OggPage(fileobj)
+        while not page.packets[0].startswith(b"\x03vorbis"):
+            page = OggPage(fileobj)
+
+        old_pages = [page]
+        while not (old_pages[-1].complete or len(old_pages[-1].packets) > 1):
+            page = OggPage(fileobj)
+            if page.serial == old_pages[0].serial:
+                old_pages.append(page)
+
+        packets = OggPage.to_packets(old_pages, strict=False)
+
+        content_size = get_size(fileobj) - len(packets[0])  # approx
+        vcomment_data = b"\x03vorbis" + self.write()
+        padding_left = len(packets[0]) - len(vcomment_data)
+
+        info = PaddingInfo(padding_left, content_size)
+        new_padding = info._get_padding(padding_func)
+
+        # Set the new comment packet.
+        packets[0] = vcomment_data + b"\x00" * new_padding
+
+        new_pages = OggPage._from_packets_try_preserve(packets, old_pages)
+        OggPage.replace(fileobj, old_pages, new_pages)
+
+
+class OggVorbis(OggFileType):
+    """An Ogg Vorbis file."""
+
+    _Info = OggVorbisInfo
+    _Tags = OggVCommentDict
+    _Error = OggVorbisHeaderError
+    _mimes = ["audio/vorbis", "audio/x-vorbis"]
+
+    info = None
+    """A `OggVorbisInfo`"""
+
+    tags = None
+    """A `VCommentDict`"""
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return (header.startswith(b"OggS") * (b"\x01vorbis" in header))
+
+
+Open = OggVorbis
+
+
+def delete(filename):
+    """Remove tags from a file."""
+
+    OggVorbis(filename).delete()
diff --git a/resources/lib/mutagen/optimfrog.py b/resources/lib/mutagen/optimfrog.py
new file mode 100644
index 00000000..0d85a818
--- /dev/null
+++ b/resources/lib/mutagen/optimfrog.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Lukas Lalinsky
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""OptimFROG audio streams with APEv2 tags.
+
+OptimFROG is a lossless audio compression program. Its main goal is to
+reduce at maximum the size of audio files, while permitting bit
+identical restoration for all input. It is similar with the ZIP
+compression, but it is highly specialized to compress audio data.
+
+Only versions 4.5 and higher are supported.
+
+For more information, see http://www.losslessaudio.org/
+"""
+
+__all__ = ["OptimFROG", "Open", "delete"]
+
+import struct
+
+from ._compat import endswith
+from mutagen import StreamInfo
+from mutagen.apev2 import APEv2File, error, delete
+
+
+class OptimFROGHeaderError(error):
+    pass
+
+
+class OptimFROGInfo(StreamInfo):
+    """OptimFROG stream information.
+
+    Attributes:
+
+    * channels - number of audio channels
+    * length - file length in seconds, as a float
+    * sample_rate - audio sampling rate in Hz
+    """
+
+    def __init__(self, fileobj):
+        header = fileobj.read(76)
+        if (len(header) != 76 or not header.startswith(b"OFR ") or
+                struct.unpack("<I", header[4:8])[0] not in [12, 15]):
+            raise OptimFROGHeaderError("not an OptimFROG file")
+        (total_samples, total_samples_high, sample_type, self.channels,
+         self.sample_rate) = struct.unpack("<IHBBI", header[8:20])
+        total_samples += total_samples_high << 32
+        self.channels += 1
+        if self.sample_rate:
+            self.length = float(total_samples) / (self.channels *
+                                                  self.sample_rate)
+        else:
+            self.length = 0.0
+
+    def pprint(self):
+        return u"OptimFROG, %.2f seconds, %d Hz" % (self.length,
+                                                    self.sample_rate)
+
+
+class OptimFROG(APEv2File):
+    _Info = OptimFROGInfo
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        filename = filename.lower()
+
+        return (header.startswith(b"OFR") + endswith(filename, b".ofr") +
+                endswith(filename, b".ofs"))
+
+Open = OptimFROG
diff --git a/resources/lib/mutagen/trueaudio.py b/resources/lib/mutagen/trueaudio.py
new file mode 100644
index 00000000..1c8d56c4
--- /dev/null
+++ b/resources/lib/mutagen/trueaudio.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2006  Joe Wreschnig
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+
+"""True Audio audio stream information and tags.
+
+True Audio is a lossless format designed for real-time encoding and
+decoding. This module is based on the documentation at
+http://www.true-audio.com/TTA_Lossless_Audio_Codec\_-_Format_Description
+
+True Audio files use ID3 tags.
+"""
+
+__all__ = ["TrueAudio", "Open", "delete", "EasyTrueAudio"]
+
+from ._compat import endswith
+from mutagen import StreamInfo
+from mutagen.id3 import ID3FileType, delete
+from mutagen._util import cdata, MutagenError
+
+
+class error(RuntimeError, MutagenError):
+    pass
+
+
+class TrueAudioHeaderError(error, IOError):
+    pass
+
+
+class TrueAudioInfo(StreamInfo):
+    """True Audio stream information.
+
+    Attributes:
+
+    * length - audio length, in seconds
+    * sample_rate - audio sample rate, in Hz
+    """
+
+    def __init__(self, fileobj, offset):
+        fileobj.seek(offset or 0)
+        header = fileobj.read(18)
+        if len(header) != 18 or not header.startswith(b"TTA"):
+            raise TrueAudioHeaderError("TTA header not found")
+        self.sample_rate = cdata.int_le(header[10:14])
+        samples = cdata.uint_le(header[14:18])
+        self.length = float(samples) / self.sample_rate
+
+    def pprint(self):
+        return u"True Audio, %.2f seconds, %d Hz." % (
+            self.length, self.sample_rate)
+
+
+class TrueAudio(ID3FileType):
+    """A True Audio file.
+
+    :ivar info: :class:`TrueAudioInfo`
+    :ivar tags: :class:`ID3 <mutagen.id3.ID3>`
+    """
+
+    _Info = TrueAudioInfo
+    _mimes = ["audio/x-tta"]
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return (header.startswith(b"ID3") + header.startswith(b"TTA") +
+                endswith(filename.lower(), b".tta") * 2)
+
+
+Open = TrueAudio
+
+
+class EasyTrueAudio(TrueAudio):
+    """Like MP3, but uses EasyID3 for tags.
+
+    :ivar info: :class:`TrueAudioInfo`
+    :ivar tags: :class:`EasyID3 <mutagen.easyid3.EasyID3>`
+    """
+
+    from mutagen.easyid3 import EasyID3 as ID3
+    ID3 = ID3
diff --git a/resources/lib/mutagen/wavpack.py b/resources/lib/mutagen/wavpack.py
new file mode 100644
index 00000000..80710f6d
--- /dev/null
+++ b/resources/lib/mutagen/wavpack.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2006 Joe Wreschnig
+#           2014 Christoph Reiter
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+"""WavPack reading and writing.
+
+WavPack is a lossless format that uses APEv2 tags. Read
+
+* http://www.wavpack.com/
+* http://www.wavpack.com/file_format.txt
+
+for more information.
+"""
+
+__all__ = ["WavPack", "Open", "delete"]
+
+from mutagen import StreamInfo
+from mutagen.apev2 import APEv2File, error, delete
+from mutagen._util import cdata
+
+
+class WavPackHeaderError(error):
+    pass
+
+RATES = [6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100,
+         48000, 64000, 88200, 96000, 192000]
+
+
+class _WavPackHeader(object):
+
+    def __init__(self, block_size, version, track_no, index_no, total_samples,
+                 block_index, block_samples, flags, crc):
+
+        self.block_size = block_size
+        self.version = version
+        self.track_no = track_no
+        self.index_no = index_no
+        self.total_samples = total_samples
+        self.block_index = block_index
+        self.block_samples = block_samples
+        self.flags = flags
+        self.crc = crc
+
+    @classmethod
+    def from_fileobj(cls, fileobj):
+        """A new _WavPackHeader or raises WavPackHeaderError"""
+
+        header = fileobj.read(32)
+        if len(header) != 32 or not header.startswith(b"wvpk"):
+            raise WavPackHeaderError("not a WavPack header: %r" % header)
+
+        block_size = cdata.uint_le(header[4:8])
+        version = cdata.ushort_le(header[8:10])
+        track_no = ord(header[10:11])
+        index_no = ord(header[11:12])
+        samples = cdata.uint_le(header[12:16])
+        if samples == 2 ** 32 - 1:
+            samples = -1
+        block_index = cdata.uint_le(header[16:20])
+        block_samples = cdata.uint_le(header[20:24])
+        flags = cdata.uint_le(header[24:28])
+        crc = cdata.uint_le(header[28:32])
+
+        return _WavPackHeader(block_size, version, track_no, index_no,
+                              samples, block_index, block_samples, flags, crc)
+
+
+class WavPackInfo(StreamInfo):
+    """WavPack stream information.
+
+    Attributes:
+
+    * channels - number of audio channels (1 or 2)
+    * length - file length in seconds, as a float
+    * sample_rate - audio sampling rate in Hz
+    * version - WavPack stream version
+    """
+
+    def __init__(self, fileobj):
+        try:
+            header = _WavPackHeader.from_fileobj(fileobj)
+        except WavPackHeaderError:
+            raise WavPackHeaderError("not a WavPack file")
+
+        self.version = header.version
+        self.channels = bool(header.flags & 4) or 2
+        self.sample_rate = RATES[(header.flags >> 23) & 0xF]
+
+        if header.total_samples == -1 or header.block_index != 0:
+            # TODO: we could make this faster by using the tag size
+            # and search backwards for the last block, then do
+            # last.block_index + last.block_samples - initial.block_index
+            samples = header.block_samples
+            while 1:
+                fileobj.seek(header.block_size - 32 + 8, 1)
+                try:
+                    header = _WavPackHeader.from_fileobj(fileobj)
+                except WavPackHeaderError:
+                    break
+                samples += header.block_samples
+        else:
+            samples = header.total_samples
+
+        self.length = float(samples) / self.sample_rate
+
+    def pprint(self):
+        return u"WavPack, %.2f seconds, %d Hz" % (self.length,
+                                                  self.sample_rate)
+
+
+class WavPack(APEv2File):
+    _Info = WavPackInfo
+    _mimes = ["audio/x-wavpack"]
+
+    @staticmethod
+    def score(filename, fileobj, header):
+        return header.startswith(b"wvpk") * 2
+
+
+Open = WavPack
diff --git a/resources/lib/utils.py b/resources/lib/utils.py
index aca6b790..956f63bb 100644
--- a/resources/lib/utils.py
+++ b/resources/lib/utils.py
@@ -62,7 +62,7 @@ def settings(setting, value=None):
 def language(stringid):
     # Central string retrieval
     addon = xbmcaddon.Addon(id='plugin.video.emby')
-    string = addon.getLocalizedString(stringid)
+    string = addon.getLocalizedString(stringid).decode("utf-8")
 
     return string