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()