diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/common/__init__.py b/mythtv/programs/scripts/metadata/Music/lyrics/common/__init__.py
index b1b9bddeaf..391fdc6bbe 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/common/__init__.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/common/__init__.py
@@ -1,3 +1,4 @@
 __version__ = "1.0.0"
 
-import utilities, audiofile
+from . import utilities, audiofile
+
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/common/utilities.py b/mythtv/programs/scripts/metadata/Music/lyrics/common/utilities.py
index 4ad657d21b..dfca3b2cd6 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/common/utilities.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/common/utilities.py
@@ -2,9 +2,11 @@ import sys
 import os
 import unicodedata
 
+IS_PY2 = sys.version_info[0] == 2
+
 def log(debug, txt):
     if debug:
-        print txt
+        print(txt)
 
 class Lyrics:
     def __init__(self):
@@ -19,3 +21,12 @@ class Lyrics:
 
 def deAccent(str):
     return unicodedata.normalize('NFKD', unicode(str, 'utf-8'))
+    
+def convert_etree(etostr):
+    """lxml.etree.tostring is a bytes object in python3, and a str in python2.
+    """
+    if IS_PY2:
+        return(etostr)
+    else:
+        return(etostr.decode())
+
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/darklyrics.py b/mythtv/programs/scripts/metadata/Music/lyrics/darklyrics.py
index 8c3363d459..9b0a9250a8 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/darklyrics.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/darklyrics.py
@@ -6,7 +6,11 @@ scraper by smory
 """
 
 import hashlib
-import urllib2
+try:
+    from urllib2 import quote, urlopen
+except ImportError:
+    from urllib.request import urlopen
+    from urllib.parse import quote
 import re
 import chardet
 import sys
@@ -16,9 +20,9 @@ from common import utilities
 __author__      = "Paul Harrison and smory'"
 __title__       = "DarkLyrics"
 __description__ = "Search http://www.darklyrics.com/ - the largest metal lyrics archive on the Web"
-__priority__    = "180";
+__priority__    = "180"
 __version__     = "0.1"
-__syncronized__ = False;
+__syncronized__ = False
 
 debug = False
 
@@ -29,76 +33,79 @@ class LyricsFetcher:
         self.searchUrl = "http://www.darklyrics.com/search?q=%term%"
 
     def search(self, artist, title):
-        term = urllib2.quote((artist if artist else "") + " " + (title if title else ""));
+        term = quote((artist if artist else "") + " " + (title if title else ""))
 
         try:
-            request = urllib2.urlopen(self.searchUrl.replace("%term%", term))
-            searchResponse = request.read();
+            request = urlopen(self.searchUrl.replace("%term%", term))
+            searchResponse = request.read()
         except:
             return None
 
-        searchResult = re.findall("<h2><a\shref=\"(.*?#([0-9]+))\".*?>(.*?)</a></h2>", searchResponse);
+        searchResult = re.findall(b"<h2><a\shref=\"(.*?#([0-9]+))\".*?>(.*?)</a></h2>", searchResponse)
 
         if len(searchResult) == 0:
-            return None;
+            return None
 
-        links = [];
+        links = []
 
-        i = 0;
+        i = 0
         for result in searchResult:
-            a = [];
-            a.append(result[2] + ( " " + self.getAlbumName(self.base_url + result[0]) if i < 6 else "")); # title from server + album nane
-            a.append(self.base_url + result[0]);  # url with lyrics
-            a.append(artist);
-            a.append(title);
-            a.append(result[1]); # id of the side part containing this song lyrics
-            links.append(a);
-            i += 1;
+            a = []
+            a.append(result[2] + ( b" " + self.getAlbumName(self.base_url + result[0].decode('utf-8') )if i < 6 else b"")) # title from server + album nane
+            a.append(self.base_url + result[0].decode('utf-8'))  # url with lyrics
+            a.append(artist)
+            a.append(title)
+            a.append(result[1]) # id of the side part containing this song lyrics
+            links.append(a)
+            i += 1
 
-        return links;
+        return links
 
     def findLyrics(self, url, index):
         try:
-            request = urllib2.urlopen(url);
-            res = request.read();
+            request = urlopen(url)
+            res = request.read()
         except:
             return None
 
-        pattern = "<a\sname=\"%index%\">(.*?)(?:<h3>|<div)";  # require multi line and dot all mode
-        pattern = pattern.replace("%index%", index);
+        pattern = b"<a\sname=\"%index%\">(.*?)(?:<h3>|<div)"  # require multi line and dot all mode
+        pattern = pattern.replace(b"%index%", index)
 
-        match = re.search(pattern, res, re.MULTILINE | re.DOTALL);
+        match = re.search(pattern, res, re.MULTILINE | re.DOTALL)
         if match:  
-            s = match.group(1);
-            s = s.replace("<br />", "");
-            s = s.replace("<i>", "");
-            s = s.replace("</i>", "");
-            s = s.replace("</a>", "");
-            s = s.replace("</h3>", "");
-            return s;
+            s = match.group(1)
+            s = s.replace(b"<br />", b"")
+            s = s.replace(b"<i>", b"")
+            s = s.replace(b"</i>", b"")
+            s = s.replace(b"</a>", b"")
+            s = s.replace(b"</h3>", b"")
+            return s
         else:
-            return None;
+            return None
 
     def getAlbumName(self, url):
         try:
-            request = urllib2.urlopen(url);
-            res = request.read();
+            request = urlopen(url)
+            res = request.read()
         except:
-            return "";
+            return b""
 
-        match = re.search("<h2>(?:album|single|ep|live):?\s?(.*?)</h2>", res, re.IGNORECASE);
+        match = re.search(b"<h2>(?:album|single|ep|live):?\s?(.*?)</h2>", res, re.IGNORECASE)
 
         if match:
-            return ("(" + match.group(1) + ")").replace("\"", "");
+            ret = (b"(" + match.group(1) + b")").replace(b"\"", b"")
         else:
-            return "";
+            ret = b""
+        return(ret)
+            
+            
 
     def get_lyrics(self, lyrics):
         utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
-        links = self.search(lyrics.artist, lyrics.title);
+        links = self.search(lyrics.artist, lyrics.title)
 
         if(links == None or len(links) == 0):
-            return False;
+            return False
         elif len(links) > 1:
             lyrics.list = links
 
@@ -109,11 +116,11 @@ class LyricsFetcher:
         enc = chardet.detect(lyr)
         lyr = lyr.decode(enc['encoding'], 'ignore')
         lyrics.lyrics = lyr
-        return True;
+        return True
 
     def get_lyrics_from_list(self, link):
-        title, url, artist, song, index = link;
-        return self.findLyrics(url, index);
+        title, url, artist, song, index = link
+        return self.findLyrics(url, index)
 
 def performSelfTest():
     found = False
@@ -129,6 +136,7 @@ def performSelfTest():
 
     if found:
         utilities.log(True, "Everything appears in order.")
+        buildLyrics(lyrics)
         sys.exit(0)
 
     utilities.log(True, "The lyrics for the test search failed!")
@@ -148,8 +156,8 @@ def buildLyrics(lyrics):
         line2 = re.sub(u'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\u10000-\u10FFFF]+', '', line)
         etree.SubElement(xml, "lyric").text = line2
 
-    utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
-                                    xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+                                      pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def buildVersion():
@@ -164,8 +172,8 @@ def buildVersion():
     etree.SubElement(version, "priority").text = __priority__
     etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
 
-    utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                                    xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+                                      pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def main():
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/embedlrc.py b/mythtv/programs/scripts/metadata/Music/lyrics/embedlrc.py
index f3d36484f6..3f5815c243 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/embedlrc.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/embedlrc.py
@@ -124,7 +124,11 @@ class LyricsFetcher:
     def get_lyrics(self, lyrics):
         utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
 
-        filename = lyrics.filename.decode("utf-8")
+        if utilities.IS_PY2:
+            filename = lyrics.filename.decode("utf-8")
+        else:
+            filename = lyrics.filename
+
         ext = os.path.splitext(filename)[1].lower()
         lry = None
 
@@ -173,6 +177,7 @@ def performSelfTest():
 
     if found:
         utilities.log(True, "Everything appears in order.")
+        buildLyrics(lyrics)
         sys.exit(0)
 
     utilities.log(True, "The lyrics for the test search failed!")
@@ -191,8 +196,8 @@ def buildLyrics(lyrics):
     for line in lines:
         etree.SubElement(xml, "lyric").text = line
 
-    utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
-                                    xml_declaration=True))
+    utilities.log(True,  utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+                                                 pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def buildVersion():
@@ -207,8 +212,8 @@ def buildVersion():
     etree.SubElement(version, "priority").text = __priority__
     etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
 
-    utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                                    xml_declaration=True))
+    utilities.log(True,  utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+                                                 pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def main():
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/filelyrics.py b/mythtv/programs/scripts/metadata/Music/lyrics/filelyrics.py
index bf34f2e29d..7aadb9dd52 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/filelyrics.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/filelyrics.py
@@ -26,7 +26,10 @@ class LyricsFetcher:
     def get_lyrics(self, lyrics):
         utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
 
-        filename = lyrics.filename.decode("utf-8")
+        if utilities.IS_PY2:
+            filename = lyrics.filename.decode("utf-8")
+        else:
+            filename = lyrics.filename
         filename = os.path.splitext(filename)[0]
 
         # look for a file ending in .lrc with the same filename as the track minus the extension
@@ -83,8 +86,8 @@ def buildLyrics(lyrics):
     for line in lines:
         etree.SubElement(xml, "lyric").text = line
 
-    utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
-                                    xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+                                      pretty_print=True, xml_declaration=True)))
 
 def buildVersion():
     from lxml import etree
@@ -98,8 +101,8 @@ def buildVersion():
     etree.SubElement(version, "priority").text = __priority__
     etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
 
-    utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                                    xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+                                      pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def main():
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/genius.py b/mythtv/programs/scripts/metadata/Music/lyrics/genius.py
index a07d1ff489..3b163b781e 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/genius.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/genius.py
@@ -5,14 +5,18 @@ Scraper for http://www.genius.com
 taxigps
 """
 import sys
-import urllib
-import urllib2
+try:
+    from urllib2 import quote, urlopen, Request
+except ImportError:	
+    from urllib.request import urlopen, Request
+    from urllib.parse import quote
+try: 
+    import HTMLParser as html_parser
+except ImportError:		
+	from html import parser as html_parser	
 import socket
 import re
-import chardet
-import HTMLParser
 from hashlib import md5
-import chardet
 import difflib
 from optparse import OptionParser
 from common import utilities
@@ -42,10 +46,10 @@ class LyricsFetcher:
         utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
 
         try:
-            request = urllib2.Request(self.url % (urllib2.quote(lyrics.artist), '%20', urllib2.quote(lyrics.title)))
+            request = Request(self.url % (quote(lyrics.artist), '%20', quote(lyrics.title)))
             request.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0')
-            req = urllib2.urlopen(request)
-            response = req.read()
+            req = urlopen(request)
+            response = req.read().decode('utf-8')
         except:
             return False
 
@@ -60,23 +64,23 @@ class LyricsFetcher:
         utilities.log(debug, "%s: search url: %s" % (__title__, self.page))
 
         try:
-            request = urllib2.Request(self.page)
+            request = Request(self.page)
             request.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; rv:25.0) Gecko/20100101 Firefox/25.0')
-            req = urllib2.urlopen(request)
-            response = req.read()
+            req = urlopen(request)
+            response = req.read().decode('utf-8')
         except:
             return False
 
         req.close()
-        matchcode = re.search('<div class="lyrics">(.*?)</div>', response, flags=re.DOTALL)
+        matchcode = re.search(u'<div class="lyrics">(.*?)</div>', response, flags=re.DOTALL)
 
         try:
             lyricscode = (matchcode.group(1))
-            htmlparser = HTMLParser.HTMLParser()
-            lyricstext = htmlparser.unescape(lyricscode).replace('<br />', '\n')
-            templyr = re.sub('<[^<]+?>', '', lyricstext)
-            lyr = re.sub('\[(.*?)\]', '', templyr)
-            lyrics.lyrics = lyr.strip().replace('\n\n\n', '\n\n')
+            htmlparser = html_parser.HTMLParser()
+            lyricstext = htmlparser.unescape(lyricscode).replace(u'<br />', u'\n')
+            templyr = re.sub(u'<[^<]+?>', '', lyricstext)
+            lyr = re.sub(u'\[(.*?)\]', '', templyr)
+            lyrics.lyrics = lyr.strip().replace(u'\n\n\n', u'\n\n')
             return True
         except:
             return False
@@ -96,6 +100,7 @@ def performSelfTest():
 
     if found:
         utilities.log(True, "Everything appears in order.")
+        buildLyrics(lyrics)
         sys.exit(0)
 
     utilities.log(True, "The lyrics for the test search failed!")
@@ -114,8 +119,8 @@ def buildLyrics(lyrics):
     for line in lines:
         etree.SubElement(xml, "lyric").text = line
 
-    utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
-                                       xml_declaration=True))
+    utilities.log(True,  utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+                                                 pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def buildVersion():
@@ -130,8 +135,8 @@ def buildVersion():
     etree.SubElement(version, "priority").text = __priority__
     etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
 
-    utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                                       xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+                                                pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def main():
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/lyricscom.py b/mythtv/programs/scripts/metadata/Music/lyrics/lyricscom.py
index 14993c8fb4..9ce9e1a185 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/lyricscom.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/lyricscom.py
@@ -7,8 +7,13 @@ ronie
 
 import sys
 import re
-import urllib
-import urllib2
+try:
+    from urllib import quote_plus
+    from urllib2 import urlopen
+except ImportError:	
+    from urllib.parse import quote_plus
+    from urllib.request import urlopen
+
 import socket
 import difflib
 from optparse import OptionParser
@@ -39,7 +44,7 @@ class LyricsFetcher:
             return False
 
         try:
-            request = urllib2.urlopen(self.url % urllib.quote_plus(lyrics.artist))
+            request = urlopen(self.url % quote_plus(lyrics.artist))
             response = request.read()
         except:
             return False
@@ -54,8 +59,8 @@ class LyricsFetcher:
         if url:
             utilities.log(debug, "%s: Artist url is %s"  % (__title__, url))
             try:
-                req = urllib2.urlopen(url)
-                resp = req.read()
+                req = urlopen(url)
+                resp = req.read().decode('utf-8')
             except:
                 return False
             req.close()
@@ -70,16 +75,16 @@ class LyricsFetcher:
                 utilities.log(debug, "%s: Song url is %s"  % (__title__, url))
 
                 try:
-                    req2 = urllib2.urlopen(url)
-                    resp2 = req2.read()
+                    req2 = urlopen(url)
+                    resp2 = req2.read().decode('utf-8')
                 except:
                     return False
                 req2.close()
 
-                matchcode = re.search('<pre.*?>(.*?)</pre>', resp2, flags=re.DOTALL)
+                matchcode = re.search(u'<pre.*?>(.*?)</pre>', resp2, flags=re.DOTALL)
                 if matchcode:
                     lyricscode = (matchcode.group(1))
-                    lyr = re.sub('<[^<]+?>', '', lyricscode)
+                    lyr = re.sub(u'<[^<]+?>', '', lyricscode)
                     lyrics.lyrics = lyr.replace('\\n','\n')
                     return True
 
@@ -105,6 +110,7 @@ def performSelfTest():
 
     if found:
         utilities.log(True, "Everything appears in order.")
+        buildLyrics(lyrics)
         sys.exit(0)
 
     utilities.log(True, "The lyrics for the test search failed!")
@@ -123,8 +129,8 @@ def buildLyrics(lyrics):
     for line in lines:
         etree.SubElement(xml, "lyric").text = line
 
-    utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
-                                    xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+                                                pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def buildVersion():
@@ -139,8 +145,8 @@ def buildVersion():
     etree.SubElement(version, "priority").text = __priority__
     etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
 
-    utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                                    xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+                                                pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def main():
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/lyricsmode.py b/mythtv/programs/scripts/metadata/Music/lyrics/lyricsmode.py
index b89c085416..e194c4171f 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/lyricsmode.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/lyricsmode.py
@@ -1,6 +1,12 @@
 #-*- coding: UTF-8 -*-
 import sys
-import urllib
+
+try:
+    from urllib import urlopen, quote_plus
+except ImportError:	
+    from urllib.request import urlopen
+    from urllib.parse import quote_plus
+
 import re
 from optparse import OptionParser
 from common import utilities
@@ -32,7 +38,7 @@ class LyricsFetcher:
             lyrics_found = False
             while True:
                 utilities.log(debug, "%s: search url: %s" % (__title__, url))
-                song_search = urllib.urlopen(url).read()
+                song_search = urlopen(url).read()
                 if song_search.find("<p id=\"lyrics_text\" class=\"ui-annotatable\">") >= 0:
                     break
 
@@ -43,7 +49,7 @@ class LyricsFetcher:
 
                 # Let's try to use the research box if we didn't yet
                 if not 'search' in url:
-                    url = "http://www.lyricsmode.com/search.php?what=songs&s=" + urllib.quote_plus(title.lower())
+                    url = "http://www.lyricsmode.com/search.php?what=songs&s=" + quote_plus(title.lower())
                 else:
                     # the search gave more than on result, let's try to find our song
                     url = ""
@@ -102,6 +108,7 @@ def performSelfTest():
 
     if found:
         utilities.log(True, "Everything appears in order.")
+        buildLyrics(lyrics)
         sys.exit(0)
 
     utilities.log(True, "The lyrics for the test search failed!")
@@ -120,8 +127,8 @@ def buildLyrics(lyrics):
     for line in lines:
         etree.SubElement(xml, "lyric").text = line
 
-    utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
-                                       xml_declaration=True))
+    utilities.log(True,  utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+                                                 pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def buildVersion():
@@ -136,8 +143,8 @@ def buildVersion():
     etree.SubElement(version, "priority").text = __priority__
     etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
 
-    utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                                       xml_declaration=True))
+    utilities.log(True,  utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+                                                 pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def main():
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/lyricswiki.py b/mythtv/programs/scripts/metadata/Music/lyrics/lyricswiki.py
index bcda8959f7..34179c180c 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/lyricswiki.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/lyricswiki.py
@@ -1,5 +1,18 @@
 #-*- coding: UTF-8 -*-
-import sys, re, urllib2, socket, HTMLParser
+
+import sys, re, socket
+
+try:
+    from urllib2 import quote, urlopen, HTTPError
+except ImportError:	
+    from urllib.request import urlopen, HTTPError
+    from urllib.parse import quote	
+	
+try: 
+    import HTMLParser as html_parser
+except ImportError:		
+	from html import parser as html_parser
+
 from optparse import OptionParser
 
 if sys.version_info < (2, 7):
@@ -31,8 +44,8 @@ class LyricsFetcher:
         utilities.log(debug,  "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
 
         try:
-            req = urllib2.urlopen(self.url % (urllib2.quote(lyrics.artist), urllib2.quote(lyrics.title)))
-            response = req.read()
+            req = urlopen(self.url % (quote(lyrics.artist), quote(lyrics.title)))
+            response = req.read().decode('utf-8')
         except:
             return False
         req.close()
@@ -44,18 +57,18 @@ class LyricsFetcher:
         if not self.page.endswith('action=edit'):
             utilities.log(debug, "%s: search url: %s" % (__title__, self.page))
             try:
-                req = urllib2.urlopen(self.page)
-                response = req.read()
-            except urllib2.HTTPError, error: # strange... sometimes lyrics are returned with a 404 error
+                req = urlopen(self.page)
+                response = req.read().decode('utf-8')
+            except HTTPError as error: # strange... sometimes lyrics are returned with a 404 error
                 if error.code == 404:
-                    response = error.read()
+                    response = error.read().decode('utf-8')
                 else:
                     return False
             req.close()
             matchcode = re.search("lyricbox'>.*?(&#.*?)<div", response)
             try:
                 lyricscode = (matchcode.group(1))
-                htmlparser = HTMLParser.HTMLParser()
+                htmlparser = html_parser.HTMLParser()
                 lyricstext = htmlparser.unescape(lyricscode).replace('<br />', '\n')
                 lyr = re.sub('<[^<]+?>', '', lyricstext)
                 if LIC_TXT in lyr:
@@ -81,6 +94,7 @@ def performSelfTest():
 
     if found:
         utilities.log(True, "Everything appears in order.")
+        buildLyrics(lyrics)
         sys.exit(0)
 
     utilities.log(True, "The lyrics for the test search failed!")
@@ -99,8 +113,8 @@ def buildLyrics(lyrics):
     for line in lines:
         etree.SubElement(xml, "lyric").text = line
 
-    utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
-                                       xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+                                                pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def buildVersion():
@@ -115,8 +129,8 @@ def buildVersion():
     etree.SubElement(version, "priority").text = __priority__
     etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
 
-    utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                                       xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+                                                pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def main():
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/ttplayer.py b/mythtv/programs/scripts/metadata/Music/lyrics/ttplayer.py
index a573bce1c9..48a3898615 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/ttplayer.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/ttplayer.py
@@ -8,7 +8,12 @@ taxigps
 import os
 import sys
 import socket
-import urllib
+
+try:
+    from urllib import urlopen
+except ImportError:
+    from urllib.request import urlopen
+
 import re
 import chardet
 import random
@@ -108,13 +113,18 @@ class ttpClient(object):
         return tmp1
 
     @staticmethod
-    def EncodeArtTit(str):
+    def EncodeArtTit(mystr):
         rtn = ''
-        uni = unicode(str, 'UTF-8')
-        str = uni.encode('UTF-16')[2:]
-        for i in range(len(str)):
-            rtn += '%02x' % ord(str[i])
-
+        if utilities.IS_PY2:
+            uni = unicode(mystr, 'UTF-8')
+            mystr = uni.encode('UTF-16')[2:]
+            for i in range(len(mystr)):
+                rtn += '%02x' % ord(mystr[i])
+        else:
+            uni = str(mystr)
+            mystr = uni.encode('UTF-16')[2:]
+            for i in range(len(mystr)):
+                rtn += '%02x' % (mystr[i])
         return rtn
 
 
@@ -145,8 +155,8 @@ class LyricsFetcher:
 
         try:
             url = self.LIST_URL %(ttpClient.EncodeArtTit(artist.replace(' ','').lower()), ttpClient.EncodeArtTit(title.replace(' ','').lower()))
-            f = urllib.urlopen(url)
-            Page = f.read()
+            f = urlopen(url)
+            Page = f.read().decode('utf-8')
         except:
             utilities.log(True, "%s: %s::%s (%d) [%s]" % (
                    __title__, self.__class__.__name__,
@@ -168,7 +178,7 @@ class LyricsFetcher:
             lyrics.list = links
         for link in links:
             lyr = self.get_lyrics_from_list(link)
-            if lyr and lyr.startswith('['):
+            if lyr and lyr.startswith(b'['):
                 enc = chardet.detect(lyr)
                 lyr = lyr.decode(enc['encoding'], 'ignore')
                 lyrics.lyrics = lyr
@@ -180,7 +190,7 @@ class LyricsFetcher:
         utilities.log(debug, '%s %s %s' %(Id, artist, song))
         try:
             url = self.LYRIC_URL %(int(Id),ttpClient.CodeFunc(int(Id), artist + song), random.randint(0,0xFFFFFFFFFFFF))
-            f = urllib.urlopen(url)
+            f = urlopen(url)
             Page = f.read()
         except:
             utilities.log(True, "%s: %s::%s (%d) [%s]" % (
@@ -190,7 +200,7 @@ class LyricsFetcher:
                    sys.exc_info()[ 1 ]
                    ))
             return None
-        if Page.startswith('['):
+        if Page.startswith(b'['):
             return Page
         return ''
 
@@ -208,6 +218,7 @@ def performSelfTest():
 
     if found:
         utilities.log(True, "Everything appears in order.")
+        buildLyrics(lyrics)
         sys.exit(0)
 
     utilities.log(True, "The lyrics for the test search failed!")
@@ -226,8 +237,8 @@ def buildLyrics(lyrics):
     for line in lines:
         etree.SubElement(xml, "lyric").text = line
 
-    utilities.log(True, etree.tostring(xml, encoding='UTF-8', pretty_print=True,
-                                       xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+                                                pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def buildVersion():
@@ -242,8 +253,8 @@ def buildVersion():
     etree.SubElement(version, "priority").text = __priority__
     etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
 
-    utilities.log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                                       xml_declaration=True))
+    utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+                                                pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def main():
diff --git a/mythtv/programs/scripts/metadata/Music/mbutils.py b/mythtv/programs/scripts/metadata/Music/mbutils.py
index 2e63371f38..fa4fe6965a 100755
--- a/mythtv/programs/scripts/metadata/Music/mbutils.py
+++ b/mythtv/programs/scripts/metadata/Music/mbutils.py
@@ -19,6 +19,7 @@ from __future__ import print_function
 from __future__ import unicode_literals
 from optparse import OptionParser
 import musicbrainzngs
+from musicbrainzngs.compat import is_py2
 import sys
 import pprint
 
@@ -42,6 +43,14 @@ musicbrainzngs.set_hostname("musicbrainz-mirror.eu:5000")
 def log(debug, txt):
     if debug:
         print(txt)
+        
+def convert_etree(etostr):
+    """lxml.etree.tostring is a bytes object in python3, and a str in python2.
+    """
+    if is_py2:
+        return(etostr)
+    else:
+        return(etostr.decode())
 
 def search_releases(artist, album, limit):
     from lxml import etree
@@ -52,7 +61,7 @@ def search_releases(artist, album, limit):
 
     if not result['release-list']:
         etree.SubElement(root, "error").text = "No Releases found"
-        log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+        log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
         sys.exit(1)
 
     for (idx, release) in enumerate(result['release-list']):
@@ -72,7 +81,7 @@ def search_releases(artist, album, limit):
         if 'release-group' in release:
             etree.SubElement(relNode, "releasegroup").text = release["release-group"]["id"]
 
-    log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+    log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
 
     sys.exit(0)
 
@@ -93,10 +102,10 @@ def search_artists(artist, limit):
             etree.SubElement(artistNode, "sort-name").text = artist['sort-name']
     else:
         etree.SubElement(root, "error").text = "No Artists found"
-        log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+        log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
         sys.exit(1)
 
-    log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+    log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
 
     sys.exit(0)
 
@@ -125,10 +134,10 @@ def get_artist(artistid):
                     node.set("type", url['type'])
     else:
         etree.SubElement(root, "error").text = "MusicBrainz ID was not found"
-        log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+        log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
         sys.exit(1)
 
-    log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+    log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def find_coverart(release):
@@ -141,11 +150,11 @@ def find_coverart(release):
     except musicbrainzngs.ResponseError as err:
         if err.cause.code == 404:
             etree.SubElement(root, "error").text = "Release ID not found"
-            log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+            log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
             sys.exit(1)
         else:
             etree.SubElement(root, "error").text = "Received bad response from the MB server"
-            log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+            log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
             sys.exit(1)
 
     if debug:
@@ -161,7 +170,7 @@ def find_coverart(release):
         etree.SubElement(imageNode, "thumb-small").text = image["thumbnails"]["small"]
         etree.SubElement(imageNode, "thumb-large").text = image["thumbnails"]["large"]
 
-    log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+    log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def find_coverart_releasegroup(releaseGroup):
@@ -174,11 +183,11 @@ def find_coverart_releasegroup(releaseGroup):
     except musicbrainzngs.ResponseError as err:
         if err.cause.code == 404:
             etree.SubElement(root, "error").text = "Release ID not found"
-            log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+            log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
             sys.exit(1)
         else:
             etree.SubElement(root, "error").text = "Received bad response from the MB server"
-            log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+            log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
             sys.exit(1)
 
     if debug:
@@ -194,7 +203,7 @@ def find_coverart_releasegroup(releaseGroup):
         etree.SubElement(imageNode, "thumb-small").text = image["thumbnails"]["small"]
         etree.SubElement(imageNode, "thumb-large").text = image["thumbnails"]["large"]
 
-    log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+    log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def find_disc(cddrive):
@@ -209,7 +218,7 @@ def find_disc(cddrive):
         toc = disc.toc_string
     except discid.DiscError as err:
         etree.SubElement(root, "error").text = "Failed to get discid ({})".format(str(err))
-        log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+        log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
         sys.exit(1)
 
     etree.SubElement(root, "discid").text = id
@@ -221,11 +230,11 @@ def find_disc(cddrive):
     except musicbrainzngs.ResponseError as err:
         if err.cause.code == 404:
             etree.SubElement(root, "error").text = "Disc not found"
-            log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+            log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
             sys.exit(1)
         else:
             etree.SubElement(root, "error").text = "Received bad response from the MB server"
-            log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+            log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
             sys.exit(1)
 
     # The result can either be a "disc" or a "cdstub"
@@ -266,10 +275,10 @@ def find_disc(cddrive):
             etree.SubElement(stubnode, "barcode").text = result['cdstub']['barcode']
     else:
         etree.SubElement(root, "error").text = "No valid results"
-        log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+        log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
         sys.exit(1)
 
-    log(True, etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True))
+    log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
     sys.exit(0)
 
 def buildVersion():
@@ -281,8 +290,8 @@ def buildVersion():
     etree.SubElement(version, "description").text = __description__
     etree.SubElement(version, "version").text = __version__
 
-    log(True, etree.tostring(version, encoding='UTF-8', pretty_print=True,
-                             xml_declaration=True))
+    log(True, convert_etree(etree.tostring(version, encoding='UTF-8', pretty_print=True,
+                             xml_declaration=True)))
     sys.exit(0)
 
 def performSelfTest():
