diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/README b/mythtv/programs/scripts/metadata/Music/lyrics/README
index b1b5d6d9dc4..e2aebc19351 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/README
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/README
@@ -90,15 +90,17 @@ Options:
Current Grabbers, Their Priority And Whether Synchronized
=========================================================
-EmbeddedLyrics 100 Yes/No
-FileLyrics 105 Yes/No
-TTPlayer 110 Yes
-Alsong 120 Yes
-LetsSingIt 130 No
-LyricsCom 140 No
-LyricsWiki 150 No
-Genius 160 No
-LyricsMode 170 No
-DarkLyrics 180 No
-GomAudio 200 Yes
-Baidu 210 Yes
+EmbeddedLyrics 50 Yes/No
+FileLyrics 90 Yes/No
+musixmatchlrc 100 Yes NEW in v34
+lrclib 110 Yes NEW in v34
+lyricsify 130 Yes NEW in v34
+genius 200 No
+musixmatch 210 No NEW in v34
+lyricsmode 220 No
+azlyrics 230 No NEW in v34
+lyricscom 240 No
+supermusic 250 No NEW in v34
+darklyrics 260 No
+megalobiz 400 Yes (too slow to be earlier, was 140)
+music163 500 Yes (returns incomplete results, was 120)
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/azlyrics.py b/mythtv/programs/scripts/metadata/Music/lyrics/azlyrics.py
new file mode 100644
index 00000000000..7ddaa8b40a1
--- /dev/null
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/azlyrics.py
@@ -0,0 +1,159 @@
+#-*- coding: UTF-8 -*-
+"""
+Scraper for http://www.azlyrics.com/
+ronie
+"""
+
+import sys
+import re
+import requests
+import html
+from optparse import OptionParser
+from common import utilities
+
+__author__ = "ronie"
+__title__ = "Azlyrics"
+__description__ = "Search http://www.azlyrics.com/ for lyrics"
+__version__ = "0.1"
+__priority__ = "230"
+__syncronized__ = False
+
+debug = False
+
+class LyricsFetcher:
+ def __init__( self ):
+ self.url = 'https://www.azlyrics.com/lyrics/%s/%s.html'
+
+ def get_lyrics(self, lyrics):
+ utilities.log(debug, '%s: searching lyrics for %s - %s' % (__title__, lyrics.artist, lyrics.title))
+ artist = re.sub("[^a-zA-Z0-9]+", "", lyrics.artist).lower().lstrip('the ')
+ title = re.sub("[^a-zA-Z0-9]+", "", lyrics.title).lower()
+ try:
+ req = requests.get(self.url % (artist, title), timeout=10)
+ response = req.text
+
+ except:
+ return False
+ req.close()
+ try:
+ lyricscode = response.split('t. -->')[1].split('', '\n')
+ lyr = re.sub('<[^<]+?>', '', lyricstext)
+ lyrics.lyrics = lyr
+ return True
+ except:
+ return False
+
+def performSelfTest():
+ found = False
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+ lyrics.artist = 'Dire Straits'
+ lyrics.album = 'Brothers In Arms'
+ lyrics.title = 'Money For Nothing'
+
+ fetcher = LyricsFetcher()
+ found = fetcher.get_lyrics(lyrics)
+
+ if found:
+ utilities.log(True, "Everything appears in order.")
+ buildLyrics(lyrics)
+ sys.exit(0)
+
+ utilities.log(True, "The lyrics for the test search failed!")
+ sys.exit(1)
+
+def buildLyrics(lyrics):
+ from lxml import etree
+ xml = etree.XML(u'')
+ etree.SubElement(xml, "artist").text = lyrics.artist
+ etree.SubElement(xml, "album").text = lyrics.album
+ etree.SubElement(xml, "title").text = lyrics.title
+ etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
+ etree.SubElement(xml, "grabber").text = lyrics.source
+
+ lines = lyrics.lyrics.splitlines()
+ for line in lines:
+ etree.SubElement(xml, "lyric").text = line
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def buildVersion():
+ from lxml import etree
+ version = etree.XML(u'')
+ etree.SubElement(version, "name").text = __title__
+ etree.SubElement(version, "author").text = __author__
+ etree.SubElement(version, "command").text = 'azlyrics.py'
+ etree.SubElement(version, "type").text = 'lyrics'
+ etree.SubElement(version, "description").text = __description__
+ etree.SubElement(version, "version").text = __version__
+ etree.SubElement(version, "priority").text = __priority__
+ etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def main():
+ global debug
+
+ parser = OptionParser()
+
+ parser.add_option('-v', "--version", action="store_true", default=False,
+ dest="version", help="Display version and author")
+ parser.add_option('-t', "--test", action="store_true", default=False,
+ dest="test", help="Perform self-test for dependencies.")
+ parser.add_option('-s', "--search", action="store_true", default=False,
+ dest="search", help="Search for lyrics.")
+ parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
+ dest="artist", help="Artist of track.")
+ parser.add_option('-b', "--album", metavar="ALBUM", default=None,
+ dest="album", help="Album of track.")
+ parser.add_option('-n', "--title", metavar="TITLE", default=None,
+ dest="title", help="Title of track.")
+ parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
+ dest="filename", help="Filename of track.")
+ parser.add_option('-d', '--debug', action="store_true", default=False,
+ dest="debug", help=("Show debug messages"))
+
+ opts, args = parser.parse_args()
+
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+
+ if opts.debug:
+ debug = True
+
+ if opts.version:
+ buildVersion()
+
+ if opts.test:
+ performSelfTest()
+
+ if opts.artist:
+ lyrics.artist = opts.artist
+ if opts.album:
+ lyrics.album = opts.album
+ if opts.title:
+ lyrics.title = opts.title
+ if opts.filename:
+ lyrics.filename = opts.filename
+
+ if (len(args) > 0):
+ utilities.log('ERROR: invalid arguments found')
+ sys.exit(1)
+
+ fetcher = LyricsFetcher()
+ if fetcher.get_lyrics(lyrics):
+ buildLyrics(lyrics)
+ sys.exit(0)
+ else:
+ utilities.log(True, "No lyrics found for this track")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/common/utilities.py b/mythtv/programs/scripts/metadata/Music/lyrics/common/utilities.py
index ef524f31140..bec61393340 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/common/utilities.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/common/utilities.py
@@ -25,3 +25,20 @@ def convert_etree(etostr):
"""
return(etostr.decode())
+def getCacheDir():
+ confdir = os.environ.get('MYTHCONFDIR', '')
+
+ if (not confdir) or (confdir == '/'):
+ confdir = os.environ.get('HOME', '')
+
+ if (not confdir) or (confdir == '/'):
+ print ("Unable to find MythTV directory for metadata cache.")
+ return '/tmp'
+
+ confdir = os.path.join(confdir, '.mythtv')
+ cachedir = os.path.join(confdir, 'cache')
+
+ if not os.path.exists(cachedir):
+ os.makedirs(cachedir)
+
+ return cachedir
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/genius.py b/mythtv/programs/scripts/metadata/Music/lyrics/genius.py
index f06351bc81a..68e5daf7d89 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/genius.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/genius.py
@@ -49,7 +49,7 @@ def get_lyrics(self, lyrics):
return False
except:
return False
- utilities.log(True, '%s: search url: %s' % (__title__, self.page))
+ utilities.log(debug, '%s: search url: %s' % (__title__, self.page))
try:
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; rv:77.0) Gecko/20100101 Firefox/77.0'}
req = requests.get(self.page, headers=headers, timeout=10)
@@ -72,8 +72,6 @@ def get_lyrics(self, lyrics):
except:
return False
-
-
def performSelfTest():
found = False
lyrics = utilities.Lyrics()
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/lrclib.py b/mythtv/programs/scripts/metadata/Music/lyrics/lrclib.py
new file mode 100644
index 00000000000..b2f2db2f5d7
--- /dev/null
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/lrclib.py
@@ -0,0 +1,181 @@
+#-*- coding: UTF-8 -*-
+'''
+Scraper for https://lrclib.net/
+
+lrclib
+
+https://github.com/rtcq/syncedlyrics
+'''
+
+import requests
+import difflib
+
+import sys
+from optparse import OptionParser
+from common import *
+
+__author__ = "Paul Harrison and ronie"
+__title__ = "LrcLib"
+__description__ = "Search https://lrclib.net for lyrics"
+__priority__ = "110"
+__version__ = "0.1"
+__syncronized__ = True
+
+
+debug = False
+
+class LyricsFetcher:
+ def __init__( self ):
+ self.SEARCH_URL = 'https://lrclib.net/api/search?q=%s-%s'
+ self.LYRIC_URL = 'https://lrclib.net/api/get/%i'
+
+ def get_lyrics(self, lyrics):
+ utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
+
+ try:
+ url = self.SEARCH_URL % (lyrics.artist, lyrics.title)
+ response = requests.get(url, timeout=10)
+ result = response.json()
+ except:
+ return False
+ links = []
+ for item in result:
+ artistname = item['artistName']
+ songtitle = item['name']
+ songid = item['id']
+ if (difflib.SequenceMatcher(None, lyrics.artist.lower(), artistname.lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, lyrics.title.lower(), songtitle.lower()).ratio() > 0.8):
+ links.append((artistname + ' - ' + songtitle, self.LYRIC_URL % songid, artistname, songtitle))
+ if len(links) == 0:
+ return False
+ elif len(links) > 1:
+ lyrics.list = links
+ for link in links:
+ lyr = self.get_lyrics_from_list(link)
+ if lyr:
+ lyrics.lyrics = lyr
+ return True
+ return False
+
+ def get_lyrics_from_list(self, link):
+ title,url,artist,song = link
+ try:
+ utilities.log(debug, '%s: search url: %s' % (__title__, url))
+ response = requests.get(url, timeout=10)
+ result = response.json()
+ except:
+ return None
+ if 'syncedLyrics' in result:
+ lyrics = result['syncedLyrics']
+ return lyrics
+
+
+def performSelfTest():
+ found = False
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+ lyrics.artist = 'Dire Straits'
+ lyrics.album = 'Brothers In Arms'
+ lyrics.title = 'Money For Nothing'
+
+ fetcher = LyricsFetcher()
+ found = fetcher.get_lyrics(lyrics)
+
+ if found:
+ utilities.log(True, "Everything appears in order.")
+ buildLyrics(lyrics)
+ sys.exit(0)
+
+ utilities.log(True, "The lyrics for the test search failed!")
+ sys.exit(1)
+
+def buildLyrics(lyrics):
+ from lxml import etree
+ xml = etree.XML(u'')
+ etree.SubElement(xml, "artist").text = lyrics.artist
+ etree.SubElement(xml, "album").text = lyrics.album
+ etree.SubElement(xml, "title").text = lyrics.title
+ etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
+ etree.SubElement(xml, "grabber").text = lyrics.source
+
+ lines = lyrics.lyrics.splitlines()
+ for line in lines:
+ etree.SubElement(xml, "lyric").text = line
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def buildVersion():
+ from lxml import etree
+ version = etree.XML(u'')
+ etree.SubElement(version, "name").text = __title__
+ etree.SubElement(version, "author").text = __author__
+ etree.SubElement(version, "command").text = 'lrclib.py'
+ etree.SubElement(version, "type").text = 'lyrics'
+ etree.SubElement(version, "description").text = __description__
+ etree.SubElement(version, "version").text = __version__
+ etree.SubElement(version, "priority").text = __priority__
+ etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def main():
+ global debug
+
+ parser = OptionParser()
+
+ parser.add_option('-v', "--version", action="store_true", default=False,
+ dest="version", help="Display version and author")
+ parser.add_option('-t', "--test", action="store_true", default=False,
+ dest="test", help="Test grabber with a know good search")
+ parser.add_option('-s', "--search", action="store_true", default=False,
+ dest="search", help="Search for lyrics.")
+ parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
+ dest="artist", help="Artist of track.")
+ parser.add_option('-b', "--album", metavar="ALBUM", default=None,
+ dest="album", help="Album of track.")
+ parser.add_option('-n', "--title", metavar="TITLE", default=None,
+ dest="title", help="Title of track.")
+ parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
+ dest="filename", help="Filename of track.")
+ parser.add_option('-d', '--debug', action="store_true", default=False,
+ dest="debug", help=("Show debug messages"))
+
+ opts, args = parser.parse_args()
+
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+
+ if opts.debug:
+ debug = True
+
+ if opts.version:
+ buildVersion()
+
+ if opts.test:
+ performSelfTest()
+
+ if opts.artist:
+ lyrics.artist = opts.artist
+ if opts.album:
+ lyrics.album = opts.album
+ if opts.title:
+ lyrics.title = opts.title
+ if opts.filename:
+ lyrics.filename = opts.filename
+
+ fetcher = LyricsFetcher()
+ if fetcher.get_lyrics(lyrics):
+ buildLyrics(lyrics)
+ sys.exit(0)
+ else:
+ utilities.log(True, "No lyrics found for this track")
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/lyricsify.py b/mythtv/programs/scripts/metadata/Music/lyrics/lyricsify.py
new file mode 100644
index 00000000000..89314e0e9c1
--- /dev/null
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/lyricsify.py
@@ -0,0 +1,192 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+"""
+Scraper for https://www.lyricsify.com/
+
+ronie
+"""
+
+import requests
+import re
+import difflib
+from bs4 import BeautifulSoup
+
+import sys
+from optparse import OptionParser
+from common import utilities
+
+
+__author__ = "Paul Harrison and ronie"
+__title__ = "Lyricsify"
+__description__ = "Search https://www.lyricsify.com for lyrics"
+__priority__ = "130"
+__version__ = "0.1"
+__syncronized__ = True
+
+debug = False
+
+UserAgent = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"}
+
+class LyricsFetcher:
+ def __init__( self ):
+ self.SEARCH_URL = 'https://www.lyricsify.com/lyrics/%s/%s'
+ self.LYRIC_URL = 'https://www.lyricsify.com%s'
+
+ def get_lyrics(self, lyrics):
+ utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
+
+ artist = lyrics.artist.replace(' ', '-')
+ title = lyrics.title.replace(' ', '-')
+ try:
+ url = self.SEARCH_URL % (artist, title)
+ search = requests.get(url, headers=UserAgent, timeout=10)
+ response = search.text
+ except:
+ return False
+ links = []
+ soup = BeautifulSoup(response, 'html.parser')
+ for link in soup.find_all('a'):
+ if link.string and link.get('href').startswith('/lrc/'):
+ foundartist = link.string.split(' - ', 1)[0]
+ # some links don't have a proper 'artist - title' format
+ try:
+ foundsong = link.string.split(' - ', 1)[1].rstrip('.lrc')
+ except:
+ continue
+ if (difflib.SequenceMatcher(None, artist.lower(), foundartist.lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, title.lower(), foundsong.lower()).ratio() > 0.8):
+ links.append((foundartist + ' - ' + foundsong, self.LYRIC_URL % link.get('href'), foundartist, foundsong))
+ if len(links) == 0:
+ return False
+ elif len(links) > 1:
+ lyrics.list = links
+ for link in links:
+ lyr = self.get_lyrics_from_list(link)
+ if lyr:
+ lyrics.lyrics = lyr
+ return True
+ return False
+
+ def get_lyrics_from_list(self, link):
+ title,url,artist,song = link
+ try:
+ utilities.log(debug, '%s: search url: %s' % (__title__, url))
+ search = requests.get(url, headers=UserAgent, timeout=10)
+ response = search.text
+ except:
+ return None
+ matchcode = re.search('/h3>(.*?)', '', lyricscode)
+ return cleanlyrics
+
+
+def performSelfTest():
+ found = False
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+ lyrics.artist = 'Dire Straits'
+ lyrics.album = 'Brothers In Arms'
+ lyrics.title = 'Money For Nothing'
+
+ fetcher = LyricsFetcher()
+ found = fetcher.get_lyrics(lyrics)
+
+ if found:
+ utilities.log(True, "Everything appears in order.")
+ buildLyrics(lyrics)
+ sys.exit(0)
+
+ utilities.log(True, "The lyrics for the test search failed!")
+ sys.exit(1)
+
+def buildLyrics(lyrics):
+ from lxml import etree
+ xml = etree.XML(u'')
+ etree.SubElement(xml, "artist").text = lyrics.artist
+ etree.SubElement(xml, "album").text = lyrics.album
+ etree.SubElement(xml, "title").text = lyrics.title
+ etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
+ etree.SubElement(xml, "grabber").text = lyrics.source
+
+ lines = lyrics.lyrics.splitlines()
+ for line in lines:
+ etree.SubElement(xml, "lyric").text = line
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def buildVersion():
+ from lxml import etree
+ version = etree.XML(u'')
+ etree.SubElement(version, "name").text = __title__
+ etree.SubElement(version, "author").text = __author__
+ etree.SubElement(version, "command").text = 'lyricsify.py'
+ etree.SubElement(version, "type").text = 'lyrics'
+ etree.SubElement(version, "description").text = __description__
+ etree.SubElement(version, "version").text = __version__
+ etree.SubElement(version, "priority").text = __priority__
+ etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def main():
+ global debug
+
+ parser = OptionParser()
+
+ parser.add_option('-v', "--version", action="store_true", default=False,
+ dest="version", help="Display version and author")
+ parser.add_option('-t', "--test", action="store_true", default=False,
+ dest="test", help="Test grabber with a know good search")
+ parser.add_option('-s', "--search", action="store_true", default=False,
+ dest="search", help="Search for lyrics.")
+ parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
+ dest="artist", help="Artist of track.")
+ parser.add_option('-b', "--album", metavar="ALBUM", default=None,
+ dest="album", help="Album of track.")
+ parser.add_option('-n', "--title", metavar="TITLE", default=None,
+ dest="title", help="Title of track.")
+ parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
+ dest="filename", help="Filename of track.")
+ parser.add_option('-d', '--debug', action="store_true", default=False,
+ dest="debug", help=("Show debug messages"))
+
+ opts, args = parser.parse_args()
+
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+
+ if opts.debug:
+ debug = True
+
+ if opts.version:
+ buildVersion()
+
+ if opts.test:
+ performSelfTest()
+
+ if opts.artist:
+ lyrics.artist = opts.artist
+ if opts.album:
+ lyrics.album = opts.album
+ if opts.title:
+ lyrics.title = opts.title
+ if opts.filename:
+ lyrics.filename = opts.filename
+
+ fetcher = LyricsFetcher()
+ if fetcher.get_lyrics(lyrics):
+ buildLyrics(lyrics)
+ sys.exit(0)
+ else:
+ utilities.log(True, "No lyrics found for this track")
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/lyricsmode.py b/mythtv/programs/scripts/metadata/Music/lyrics/lyricsmode.py
index e368a5aef2d..d0719dfa88b 100644
--- a/mythtv/programs/scripts/metadata/Music/lyrics/lyricsmode.py
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/lyricsmode.py
@@ -36,18 +36,18 @@ def get_lyrics(self, lyrics):
def direct_url(self, url):
try:
- utilities.log(True, '%s: direct url: %s' % (__title__, url))
+ utilities.log(debug, '%s: direct url: %s' % (__title__, url))
song_search = requests.get(url, timeout=10)
response = song_search.text
if response.find('lyrics_text') >= 0:
return response
except:
- utilities.log(True, 'error in direct url')
+ utilities.log(debug, 'error in direct url')
def search_url(self, artist, title):
try:
url = 'http://www.lyricsmode.com/search.php?search=' + urllib.parse.quote_plus(artist.lower() + ' ' + title.lower())
- utilities.log(True, '%s: search url: %s' % (__title__, url))
+ utilities.log(debug, '%s: search url: %s' % (__title__, url))
song_search = requests.get(url, timeout=10)
response = song_search.text
matchcode = re.search('lm-list__cell-title">.*? 1:
+ lyrics.list = links
+ for link in links:
+ lyr = self.get_lyrics_from_list(link)
+ if lyr:
+ lyrics.lyrics = lyr
+ return True
+ return False
+
+ def get_lyrics_from_list(self, link):
+ title,url,artist,song = link
+ try:
+ utilities.log(debug, '%s: search url: %s' % (__title__, url))
+ response = requests.get(url, timeout=10)
+ result = response.text
+ except:
+ return None
+ matchcode = re.search('span id="lrc_[0-9]+_lyrics">(.*?)', '', lyricscode)
+ return cleanlyrics
+
+
+def performSelfTest():
+ found = False
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+ lyrics.artist = 'Dire Straits'
+ lyrics.album = 'Brothers In Arms'
+ lyrics.title = 'Money For Nothing'
+
+ fetcher = LyricsFetcher()
+ found = fetcher.get_lyrics(lyrics)
+
+ if found:
+ utilities.log(True, "Everything appears in order.")
+ buildLyrics(lyrics)
+ sys.exit(0)
+
+ utilities.log(True, "The lyrics for the test search failed!")
+ sys.exit(1)
+
+def buildLyrics(lyrics):
+ from lxml import etree
+ xml = etree.XML(u'')
+ etree.SubElement(xml, "artist").text = lyrics.artist
+ etree.SubElement(xml, "album").text = lyrics.album
+ etree.SubElement(xml, "title").text = lyrics.title
+ etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
+ etree.SubElement(xml, "grabber").text = lyrics.source
+
+ lines = lyrics.lyrics.splitlines()
+ for line in lines:
+ etree.SubElement(xml, "lyric").text = line
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def buildVersion():
+ from lxml import etree
+ version = etree.XML(u'')
+ etree.SubElement(version, "name").text = __title__
+ etree.SubElement(version, "author").text = __author__
+ etree.SubElement(version, "command").text = 'megalobiz.py'
+ etree.SubElement(version, "type").text = 'lyrics'
+ etree.SubElement(version, "description").text = __description__
+ etree.SubElement(version, "version").text = __version__
+ etree.SubElement(version, "priority").text = __priority__
+ etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def main():
+ global debug
+
+ parser = OptionParser()
+
+ parser.add_option('-v', "--version", action="store_true", default=False,
+ dest="version", help="Display version and author")
+ parser.add_option('-t', "--test", action="store_true", default=False,
+ dest="test", help="Perform self-test for dependencies.")
+ parser.add_option('-s', "--search", action="store_true", default=False,
+ dest="search", help="Search for lyrics.")
+ parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
+ dest="artist", help="Artist of track.")
+ parser.add_option('-b', "--album", metavar="ALBUM", default=None,
+ dest="album", help="Album of track.")
+ parser.add_option('-n', "--title", metavar="TITLE", default=None,
+ dest="title", help="Title of track.")
+ parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
+ dest="filename", help="Filename of track.")
+ parser.add_option('-d', '--debug', action="store_true", default=False,
+ dest="debug", help=("Show debug messages"))
+
+ opts, args = parser.parse_args()
+
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+
+ if opts.debug:
+ debug = True
+
+ if opts.version:
+ buildVersion()
+
+ if opts.test:
+ performSelfTest()
+
+ if opts.artist:
+ lyrics.artist = opts.artist
+ if opts.album:
+ lyrics.album = opts.album
+ if opts.title:
+ lyrics.title = opts.title
+ if opts.filename:
+ lyrics.filename = opts.filename
+
+ if (len(args) > 0):
+ utilities.log('ERROR: invalid arguments found')
+ sys.exit(1)
+
+ fetcher = LyricsFetcher()
+ if fetcher.get_lyrics(lyrics):
+ buildLyrics(lyrics)
+ sys.exit(0)
+ else:
+ utilities.log(True, "No lyrics found for this track")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/music163.py b/mythtv/programs/scripts/metadata/Music/lyrics/music163.py
new file mode 100644
index 00000000000..721474edcde
--- /dev/null
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/music163.py
@@ -0,0 +1,184 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+"""
+Scraper for http://music.163.com/
+
+osdlyrics
+"""
+
+import requests
+import re
+import random
+import difflib
+
+import sys
+from optparse import OptionParser
+from common import utilities
+
+__author__ = "Paul Harrison and ronie"
+__title__ = "Music163"
+__description__ = "Lyrics scraper for http://music.163.com/"
+__priority__ = "500"
+__version__ = "0.1"
+__syncronized__ = True
+
+debug = False
+
+headers = {}
+headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'
+
+class LyricsFetcher:
+ def __init__( self ):
+ self.SEARCH_URL = 'http://music.163.com/api/search/get'
+ self.LYRIC_URL = 'http://music.163.com/api/song/lyric'
+
+
+ def get_lyrics(self, lyrics):
+ utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
+
+ artist = lyrics.artist.replace(' ', '+')
+ title = lyrics.title.replace(' ', '+')
+ search = '?s=%s+%s&type=1' % (artist, title)
+ try:
+ url = self.SEARCH_URL + search
+ response = requests.get(url, headers=headers, timeout=10)
+ result = response.json()
+ except:
+ return False
+ links = []
+ if 'result' in result and 'songs' in result['result']:
+ for item in result['result']['songs']:
+ artists = "+&+".join([a["name"] for a in item["artists"]])
+ if (difflib.SequenceMatcher(None, artist.lower(), artists.lower()).ratio() > 0.6) and (difflib.SequenceMatcher(None, title.lower(), item['name'].lower()).ratio() > 0.8):
+ links.append((artists + ' - ' + item['name'], self.LYRIC_URL + '?id=' + str(item['id']) + '&lv=-1&kv=-1&tv=-1', artists, item['name']))
+ if len(links) == 0:
+ return False
+ elif len(links) > 1:
+ lyrics.list = links
+ for link in links:
+ lyr = self.get_lyrics_from_list(link)
+ if lyr and lyr.startswith('['):
+ lyrics.lyrics = lyr
+ return True
+ return None
+
+ def get_lyrics_from_list(self, link):
+ title,url,artist,song = link
+ try:
+ utilities.log(debug, '%s: search url: %s' % (__title__, url))
+ response = requests.get(url, headers=headers, timeout=10)
+ result = response.json()
+ except:
+ return None
+ if 'lrc' in result:
+ return result['lrc']['lyric']
+
+
+def performSelfTest():
+ found = False
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+ lyrics.artist = 'Dire Straits'
+ lyrics.album = 'Brothers In Arms'
+ lyrics.title = 'Money For Nothing'
+
+ fetcher = LyricsFetcher()
+ found = fetcher.get_lyrics(lyrics)
+
+ if found:
+ utilities.log(True, "Everything appears in order.")
+ buildLyrics(lyrics)
+ sys.exit(0)
+
+ utilities.log(True, "The lyrics for the test search failed!")
+ sys.exit(1)
+
+def buildLyrics(lyrics):
+ from lxml import etree
+ xml = etree.XML(u'')
+ etree.SubElement(xml, "artist").text = lyrics.artist
+ etree.SubElement(xml, "album").text = lyrics.album
+ etree.SubElement(xml, "title").text = lyrics.title
+ etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
+ etree.SubElement(xml, "grabber").text = lyrics.source
+
+ lines = lyrics.lyrics.splitlines()
+ for line in lines:
+ etree.SubElement(xml, "lyric").text = line
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def buildVersion():
+ from lxml import etree
+ version = etree.XML(u'')
+ etree.SubElement(version, "name").text = __title__
+ etree.SubElement(version, "author").text = __author__
+ etree.SubElement(version, "command").text = 'music163.py'
+ etree.SubElement(version, "type").text = 'lyrics'
+ etree.SubElement(version, "description").text = __description__
+ etree.SubElement(version, "version").text = __version__
+ etree.SubElement(version, "priority").text = __priority__
+ etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def main():
+ global debug
+
+ parser = OptionParser()
+
+ parser.add_option('-v', "--version", action="store_true", default=False,
+ dest="version", help="Display version and author")
+ parser.add_option('-t', "--test", action="store_true", default=False,
+ dest="test", help="Test grabber with a know good search")
+ parser.add_option('-s', "--search", action="store_true", default=False,
+ dest="search", help="Search for lyrics.")
+ parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
+ dest="artist", help="Artist of track.")
+ parser.add_option('-b', "--album", metavar="ALBUM", default=None,
+ dest="album", help="Album of track.")
+ parser.add_option('-n', "--title", metavar="TITLE", default=None,
+ dest="title", help="Title of track.")
+ parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
+ dest="filename", help="Filename of track.")
+ parser.add_option('-d', '--debug', action="store_true", default=False,
+ dest="debug", help=("Show debug messages"))
+
+ opts, args = parser.parse_args()
+
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+
+ if opts.debug:
+ debug = True
+
+ if opts.version:
+ buildVersion()
+
+ if opts.test:
+ performSelfTest()
+
+ if opts.artist:
+ lyrics.artist = opts.artist
+ if opts.album:
+ lyrics.album = opts.album
+ if opts.title:
+ lyrics.title = opts.title
+ if opts.filename:
+ lyrics.filename = opts.filename
+
+ fetcher = LyricsFetcher()
+ if fetcher.get_lyrics(lyrics):
+ buildLyrics(lyrics)
+ sys.exit(0)
+ else:
+ utilities.log(True, "No lyrics found for this track")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/musixmatch.py b/mythtv/programs/scripts/metadata/Music/lyrics/musixmatch.py
new file mode 100644
index 00000000000..896163b5c07
--- /dev/null
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/musixmatch.py
@@ -0,0 +1,201 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+"""
+Scraper for https://www.musixmatch.com
+
+taxigps
+"""
+
+import os
+import requests
+import re
+import random
+import difflib
+from bs4 import BeautifulSoup
+
+import sys
+from optparse import OptionParser
+from common import utilities
+
+__author__ = "Paul Harrison and 'ronie'"
+__title__ = "Musixmatch"
+__description__ = "Search https://www.musixmatch.com for lyrics"
+__priority__ = "210"
+__version__ = "0.1"
+__syncronized__ = False
+
+debug = False
+
+headers = {}
+headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0'
+
+
+class LyricsFetcher:
+ def __init__( self ):
+ self.SEARCH_URL = 'https://www.musixmatch.com/search/'
+ self.LYRIC_URL = 'https://www.musixmatch.com'
+
+
+ def get_lyrics(self, lyrics):
+ utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
+
+ artist = lyrics.artist.replace(' ', '+')
+ title = lyrics.title.replace(' ', '+')
+ search = '%s+%s' % (artist, title)
+ try:
+ url = self.SEARCH_URL + search
+ response = requests.get(url, headers=headers, timeout=10)
+ result = response.text
+ except:
+ return False
+ links = []
+ soup = BeautifulSoup(result, 'html.parser')
+ for item in soup.find_all('li', {'class': 'showArtist'}):
+ artistname = item.find('a', {'class': 'artist'}).get_text()
+ songtitle = item.find('a', {'class': 'title'}).get_text()
+ url = item.find('a', {'class': 'title'}).get('href')
+ if (difflib.SequenceMatcher(None, artist.lower(), artistname.lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, title.lower(), songtitle.lower()).ratio() > 0.8):
+ links.append((artistname + ' - ' + songtitle, self.LYRIC_URL + url, artistname, songtitle))
+ if len(links) == 0:
+ return False
+ elif len(links) > 1:
+ lyrics.list = links
+ for link in links:
+ lyr = self.get_lyrics_from_list(link)
+ if lyr:
+ lyrics.lyrics = lyr
+ return True
+ return False
+
+ def get_lyrics_from_list(self, link):
+ title,url,artist,song = link
+ try:
+ utilities.log(debug, '%s: search url: %s' % (__title__, url))
+ response = requests.get(url, headers=headers, timeout=10)
+ result = response.text
+ except:
+ return None
+ soup = BeautifulSoup(result, 'html.parser')
+ lyr = soup.find_all('span', {'class': 'lyrics__content__ok'})
+ if lyr:
+ lyrics = ''
+ for part in lyr:
+ lyrics = lyrics + part.get_text() + '\n'
+ return lyrics
+ else:
+ lyr = soup.find_all('span', {'class': 'lyrics__content__error'})
+ if lyr:
+ lyrics = ''
+ for part in lyr:
+ lyrics = lyrics + part.get_text() + '\n'
+ return lyrics
+
+
+def performSelfTest():
+ found = False
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+ lyrics.artist = 'Dire Straits'
+ lyrics.album = 'Brothers In Arms'
+ lyrics.title = 'Money For Nothing'
+
+ fetcher = LyricsFetcher()
+ found = fetcher.get_lyrics(lyrics)
+
+ if found:
+ utilities.log(True, "Everything appears in order.")
+ buildLyrics(lyrics)
+ sys.exit(0)
+
+ utilities.log(True, "The lyrics for the test search failed!")
+ sys.exit(1)
+
+def buildLyrics(lyrics):
+ from lxml import etree
+ xml = etree.XML(u'')
+ etree.SubElement(xml, "artist").text = lyrics.artist
+ etree.SubElement(xml, "album").text = lyrics.album
+ etree.SubElement(xml, "title").text = lyrics.title
+ etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
+ etree.SubElement(xml, "grabber").text = lyrics.source
+
+ lines = lyrics.lyrics.splitlines()
+ for line in lines:
+ etree.SubElement(xml, "lyric").text = line
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def buildVersion():
+ from lxml import etree
+ version = etree.XML(u'')
+ etree.SubElement(version, "name").text = __title__
+ etree.SubElement(version, "author").text = __author__
+ etree.SubElement(version, "command").text = 'musixmatch.py'
+ etree.SubElement(version, "type").text = 'lyrics'
+ etree.SubElement(version, "description").text = __description__
+ etree.SubElement(version, "version").text = __version__
+ etree.SubElement(version, "priority").text = __priority__
+ etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def main():
+ global debug
+
+ parser = OptionParser()
+
+ parser.add_option('-v', "--version", action="store_true", default=False,
+ dest="version", help="Display version and author")
+ parser.add_option('-t', "--test", action="store_true", default=False,
+ dest="test", help="Test grabber with a know good search")
+ parser.add_option('-s', "--search", action="store_true", default=False,
+ dest="search", help="Search for lyrics.")
+ parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
+ dest="artist", help="Artist of track.")
+ parser.add_option('-b', "--album", metavar="ALBUM", default=None,
+ dest="album", help="Album of track.")
+ parser.add_option('-n', "--title", metavar="TITLE", default=None,
+ dest="title", help="Title of track.")
+ parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
+ dest="filename", help="Filename of track.")
+ parser.add_option('-d', '--debug', action="store_true", default=False,
+ dest="debug", help=("Show debug messages"))
+
+ opts, args = parser.parse_args()
+
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+
+ if opts.debug:
+ debug = True
+
+ if opts.version:
+ buildVersion()
+
+ if opts.test:
+ performSelfTest()
+
+ if opts.artist:
+ lyrics.artist = opts.artist
+ if opts.album:
+ lyrics.album = opts.album
+ if opts.title:
+ lyrics.title = opts.title
+ if opts.filename:
+ lyrics.filename = opts.filename
+
+ fetcher = LyricsFetcher()
+ if fetcher.get_lyrics(lyrics):
+ buildLyrics(lyrics)
+ sys.exit(0)
+ else:
+ utilities.log(True, "No lyrics found for this track")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/musixmatchlrc.py b/mythtv/programs/scripts/metadata/Music/lyrics/musixmatchlrc.py
new file mode 100644
index 00000000000..14d8abbc6f6
--- /dev/null
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/musixmatchlrc.py
@@ -0,0 +1,228 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+"""
+Scraper for https://www.musixmatch.com/
+
+ronie
+https://github.com/rtcq/syncedlyrics
+"""
+
+import requests
+import json
+import time
+import difflib
+
+import os
+import sys
+from optparse import OptionParser
+from common import utilities
+
+__author__ = "Paul Harrison and ronie"
+__title__ = "MusixMatchLRC"
+__description__ = "Search http://musixmatch.com for lyrics"
+__priority__ = "100"
+__version__ = "0.1"
+__syncronized__ = True
+
+debug = False
+
+class LyricsFetcher:
+ def __init__( self ):
+ self.SEARCH_URL = 'https://apic-desktop.musixmatch.com/ws/1.1/%s'
+ self.session = requests.Session()
+ self.session.headers.update(
+ {
+ "authority": "apic-desktop.musixmatch.com",
+ "cookie": "AWSELBCORS=0; AWSELB=0",
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0",
+ }
+ )
+ self.current_time = int(time.time())
+
+ def get_token(self):
+ self.token = ''
+ tokenpath = os.path.join(utilities.getCacheDir(), 'musixmatch_token')
+ if os.path.exists(tokenpath):
+ tokenfile = open(tokenpath, 'r')
+ tokendata = json.load(tokenfile)
+ tokenfile.close()
+ cached_token = tokendata.get("token")
+ expiration_time = tokendata.get("expiration_time")
+ if cached_token and expiration_time and self.current_time < expiration_time:
+ self.token = cached_token
+ if not self.token:
+ try:
+ url = self.SEARCH_URL % 'token.get'
+ query = [('user_language', 'en'), ('app_id', 'web-desktop-app-v1.0'), ('t', self.current_time)]
+ response = self.session.get(url, params=query, timeout=10)
+ result = response.json()
+ except:
+ return None
+ if 'message' in result and 'body' in result["message"] and 'user_token' in result["message"]["body"]:
+ self.token = result["message"]["body"]["user_token"]
+ expiration_time = self.current_time + 600
+ tokendata = {}
+ tokendata['token'] = self.token
+ tokendata['expiration_time'] = expiration_time
+ tokenfile = open(tokenpath, 'w')
+ json.dump(tokendata, tokenfile)
+ tokenfile.close()
+ return self.token
+
+ def get_lyrics(self, lyrics):
+ utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
+
+ self.token = self.get_token()
+ if not self.token:
+ return False
+ artist = lyrics.artist.replace(' ', '+')
+ title = lyrics.title.replace(' ', '+')
+ search = '%s - %s' % (artist, title)
+ try:
+ url = self.SEARCH_URL % 'track.search'
+ query = [('q', search), ('page_size', '5'), ('page', '1'), ('s_track_rating', 'desc'), ('quorum_factor', '1.0'), ('app_id', 'web-desktop-app-v1.0'), ('usertoken', self.token), ('t', self.current_time)]
+ response = requests.get(url, params=query, timeout=10)
+ result = response.json()
+ except:
+ return False
+ links = []
+ if 'message' in result and 'body' in result["message"] and 'track_list' in result["message"]["body"] and result["message"]["body"]["track_list"]:
+ for item in result["message"]["body"]["track_list"]:
+ artistname = item['track']['artist_name']
+ songtitle = item['track']['track_name']
+ trackid = item['track']['track_id']
+ if (difflib.SequenceMatcher(None, artist.lower(), artistname.lower()).ratio() > 0.8) and (difflib.SequenceMatcher(None, title.lower(), songtitle.lower()).ratio() > 0.8):
+ links.append((artistname + ' - ' + songtitle, trackid, artistname, songtitle))
+ if len(links) == 0:
+ return False
+ elif len(links) > 1:
+ lyrics.list = links
+ for link in links:
+ lyr = self.get_lyrics_from_list(link)
+ if lyr:
+ lyrics.lyrics = lyr
+ return True
+ return False
+
+ def get_lyrics_from_list(self, link):
+ title,trackid,artist,song = link
+ try:
+ utilities.log(debug, '%s: search track id: %s' % (__title__, trackid))
+ url = self.SEARCH_URL % 'track.subtitle.get'
+ query = [('track_id', trackid), ('subtitle_format', 'lrc'), ('app_id', 'web-desktop-app-v1.0'), ('usertoken', self.token), ('t', self.current_time)]
+ response = requests.get(url, params=query, timeout=10)
+ result = response.json()
+ except:
+ return None
+ if 'message' in result and 'body' in result["message"] and 'subtitle' in result["message"]["body"] and 'subtitle_body' in result["message"]["body"]["subtitle"]:
+ lyrics = result["message"]["body"]["subtitle"]["subtitle_body"]
+ return lyrics
+
+def performSelfTest():
+ found = False
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+ lyrics.artist = 'Dire Straits'
+ lyrics.album = 'Brothers In Arms'
+ lyrics.title = 'Money For Nothing'
+
+ fetcher = LyricsFetcher()
+ found = fetcher.get_lyrics(lyrics)
+
+ if found:
+ utilities.log(True, "Everything appears in order.")
+ buildLyrics(lyrics)
+ sys.exit(0)
+
+ utilities.log(True, "The lyrics for the test search failed!")
+ sys.exit(1)
+
+def buildLyrics(lyrics):
+ from lxml import etree
+ xml = etree.XML(u'')
+ etree.SubElement(xml, "artist").text = lyrics.artist
+ etree.SubElement(xml, "album").text = lyrics.album
+ etree.SubElement(xml, "title").text = lyrics.title
+ etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
+ etree.SubElement(xml, "grabber").text = lyrics.source
+
+ lines = lyrics.lyrics.splitlines()
+ for line in lines:
+ etree.SubElement(xml, "lyric").text = line
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def buildVersion():
+ from lxml import etree
+ version = etree.XML(u'')
+ etree.SubElement(version, "name").text = __title__
+ etree.SubElement(version, "author").text = __author__
+ etree.SubElement(version, "command").text = 'musixmatchlrc.py'
+ etree.SubElement(version, "type").text = 'lyrics'
+ etree.SubElement(version, "description").text = __description__
+ etree.SubElement(version, "version").text = __version__
+ etree.SubElement(version, "priority").text = __priority__
+ etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def main():
+ global debug
+
+ parser = OptionParser()
+
+ parser.add_option('-v', "--version", action="store_true", default=False,
+ dest="version", help="Display version and author")
+ parser.add_option('-t', "--test", action="store_true", default=False,
+ dest="test", help="Test grabber with a know good search")
+ parser.add_option('-s', "--search", action="store_true", default=False,
+ dest="search", help="Search for lyrics.")
+ parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
+ dest="artist", help="Artist of track.")
+ parser.add_option('-b', "--album", metavar="ALBUM", default=None,
+ dest="album", help="Album of track.")
+ parser.add_option('-n', "--title", metavar="TITLE", default=None,
+ dest="title", help="Title of track.")
+ parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
+ dest="filename", help="Filename of track.")
+ parser.add_option('-d', '--debug', action="store_true", default=False,
+ dest="debug", help=("Show debug messages"))
+
+ opts, args = parser.parse_args()
+
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+
+ if opts.debug:
+ debug = True
+
+ if opts.version:
+ buildVersion()
+
+ if opts.test:
+ performSelfTest()
+
+ if opts.artist:
+ lyrics.artist = opts.artist
+ if opts.album:
+ lyrics.album = opts.album
+ if opts.title:
+ lyrics.title = opts.title
+ if opts.filename:
+ lyrics.filename = opts.filename
+
+ fetcher = LyricsFetcher()
+ if fetcher.get_lyrics(lyrics):
+ buildLyrics(lyrics)
+ sys.exit(0)
+ else:
+ utilities.log(True, "No lyrics found for this track")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/mythtv/programs/scripts/metadata/Music/lyrics/supermusic.py b/mythtv/programs/scripts/metadata/Music/lyrics/supermusic.py
new file mode 100644
index 00000000000..8391969f2e8
--- /dev/null
+++ b/mythtv/programs/scripts/metadata/Music/lyrics/supermusic.py
@@ -0,0 +1,181 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+"""
+Scraper for https://supermusic.cz
+
+Jose Riha
+"""
+
+import re
+import requests
+import html
+
+import os
+import sys
+from optparse import OptionParser
+from common import utilities
+
+__author__ = "Paul Harrison and Jose Riha"
+__title__ = "SuperMusic"
+__description__ = "Search https://supermusic.cz for lyrics"
+__priority__ = "250"
+__version__ = "0.1"
+__syncronized__ = False
+
+debug = False
+
+class LyricsFetcher:
+ def __init__( self ):
+ return
+
+ def get_lyrics(self, lyrics):
+ utilities.log(debug, "%s: searching lyrics for %s - %s - %s" % (__title__, lyrics.artist, lyrics.album, lyrics.title))
+
+ artist = lyrics.artist.lower()
+ title = lyrics.title.lower()
+
+ try:
+ req = requests.post('https://supermusic.cz/najdi.php', data={'hladane': title, 'typhladania': 'piesen', 'fraza': 'off'})
+ response = req.text
+ except:
+ return False
+ req.close()
+ url = None
+ try:
+ items = re.search(r'Počet nájdených piesní.+
(.*)
', response, re.S).group(1)
+ for match in re.finditer(r'"[^"]+?") target="_parent">(?P.*?) - (?P.+?) \((.*?)', response, re.S).group(1)
+ lyr = re.sub(r'.*?', '', lyr)
+ lyr = re.sub(r'
\s*', '\n', lyr)
+ lyr = re.sub(r'', '', lyr, flags=re.DOTALL)
+ lyr = re.sub(r'<[^>]*?>', '', lyr, flags=re.DOTALL)
+ lyr = lyr.strip('\r\n')
+ lyr = html.unescape(lyr)
+ lyrics.lyrics = lyr
+ return True
+ except:
+ return False
+
+def performSelfTest():
+ found = False
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+ lyrics.artist = 'Karel Gott'
+ lyrics.album = ''
+ lyrics.title = 'Trezor'
+
+ fetcher = LyricsFetcher()
+ found = fetcher.get_lyrics(lyrics)
+
+ if found:
+ utilities.log(True, "Everything appears in order.")
+ buildLyrics(lyrics)
+ sys.exit(0)
+
+ utilities.log(True, "The lyrics for the test search failed!")
+ sys.exit(1)
+
+def buildLyrics(lyrics):
+ from lxml import etree
+ xml = etree.XML(u'')
+ etree.SubElement(xml, "artist").text = lyrics.artist
+ etree.SubElement(xml, "album").text = lyrics.album
+ etree.SubElement(xml, "title").text = lyrics.title
+ etree.SubElement(xml, "syncronized").text = 'True' if __syncronized__ else 'False'
+ etree.SubElement(xml, "grabber").text = lyrics.source
+
+ lines = lyrics.lyrics.splitlines()
+ for line in lines:
+ etree.SubElement(xml, "lyric").text = line
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(xml, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def buildVersion():
+ from lxml import etree
+ version = etree.XML(u'')
+ etree.SubElement(version, "name").text = __title__
+ etree.SubElement(version, "author").text = __author__
+ etree.SubElement(version, "command").text = 'supermusic.py'
+ etree.SubElement(version, "type").text = 'lyrics'
+ etree.SubElement(version, "description").text = __description__
+ etree.SubElement(version, "version").text = __version__
+ etree.SubElement(version, "priority").text = __priority__
+ etree.SubElement(version, "syncronized").text = 'True' if __syncronized__ else 'False'
+
+ utilities.log(True, utilities.convert_etree(etree.tostring(version, encoding='UTF-8',
+ pretty_print=True, xml_declaration=True)))
+ sys.exit(0)
+
+def main():
+ global debug
+
+ parser = OptionParser()
+
+ parser.add_option('-v', "--version", action="store_true", default=False,
+ dest="version", help="Display version and author")
+ parser.add_option('-t', "--test", action="store_true", default=False,
+ dest="test", help="Test grabber with a know good search")
+ parser.add_option('-s', "--search", action="store_true", default=False,
+ dest="search", help="Search for lyrics.")
+ parser.add_option('-a', "--artist", metavar="ARTIST", default=None,
+ dest="artist", help="Artist of track.")
+ parser.add_option('-b', "--album", metavar="ALBUM", default=None,
+ dest="album", help="Album of track.")
+ parser.add_option('-n', "--title", metavar="TITLE", default=None,
+ dest="title", help="Title of track.")
+ parser.add_option('-f', "--filename", metavar="FILENAME", default=None,
+ dest="filename", help="Filename of track.")
+ parser.add_option('-d', '--debug', action="store_true", default=False,
+ dest="debug", help=("Show debug messages"))
+
+ opts, args = parser.parse_args()
+
+ lyrics = utilities.Lyrics()
+ lyrics.source = __title__
+ lyrics.syncronized = __syncronized__
+
+ if opts.debug:
+ debug = True
+
+ if opts.version:
+ buildVersion()
+
+ if opts.test:
+ performSelfTest()
+
+ if opts.artist:
+ lyrics.artist = opts.artist
+ if opts.album:
+ lyrics.album = opts.album
+ if opts.title:
+ lyrics.title = opts.title
+ if opts.filename:
+ lyrics.filename = opts.filename
+
+ fetcher = LyricsFetcher()
+ if fetcher.get_lyrics(lyrics):
+ buildLyrics(lyrics)
+ sys.exit(0)
+ else:
+ utilities.log(True, "No lyrics found for this track")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()