rhythmbox lyrics WIP

raw

0-patch.diff

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
diff --git a/plugins/lyrics/lyrics.py b/plugins/lyrics/lyrics.py
index 13124b3..c0f8de1 100644
--- a/plugins/lyrics/lyrics.py
+++ b/plugins/lyrics/lyrics.py
@@ -32,6 +32,7 @@ import urllib.request
 import rb
 from gi.repository import Gtk, Gio, GObject, Peas
 from gi.repository import RB
+from gi.repository import Gst, GstPbutils
 
 import LyricsParse
 from LyricsConfigureDialog import LyricsConfigureDialog
@@ -132,22 +133,33 @@ def build_cache_path(artist, title):
        return os.path.join(artist_folder, title[:128] + '.lyric')
 
 class LyricGrabber(object):
+       """
+       Fetch lyrics from several sources.
+
+       1. Local cache file
+       2. Lyric tags in file meta data
+       3. Online services
+       """
        def __init__(self, db, entry):
                self.db = db
                self.entry = entry
-               
+
                (self.artist, self.title) = parse_song_data(self.db, self.entry)
 
                self.cache_path = build_cache_path(self.artist, self.title)
 
        def verify_lyric(self):
                return os.path.exists(self.cache_path)
-         
+
        def search_lyrics(self, callback, cache_only=False):
+               """
+               Fetch lyrics from cache.
+
+               If no cache file exist, tag extraction is tried next.
+               """
                self.callback = callback
-               
+
                status = self.verify_lyric()
-               
                if status:
                        f = open(self.cache_path, 'rt')
                        text = f.read()
@@ -155,20 +167,73 @@ class LyricGrabber(object):
                        self.callback(text)
                elif cache_only:
                        self.callback(_("No lyrics found"))
-               elif self.artist == "" and self.title == "":
-                       self.callback(_("No lyrics found"))
                else:
-                       def lyric_callback (text):
-                               if text is not None:
-                                       f = open(self.cache_path, 'wt')
-                                       f.write (text)
-                                       f.close ()
-                                       self.callback(text)
-                               else:
-                                       self.callback(_("No lyrics found"))
+                       self.search_tags()
+
+       def search_tags(self):
+               """
+               Initiate fetching meta tags.
+
+               Result will be handled in search_tags_result
+               """
+               location = self.entry.get_playback_uri()
+               self.discoverer = GstPbutils.Discoverer(timeout=Gst.SECOND*3)
+               self.discoverer.connect('discovered', self.search_tags_result)
+               self.discoverer.start()
+               self.discoverer.discover_uri_async(location)
+
+       def search_tags_result(self, discoverer, info, error):
+               """
+               Extract lyrics from the file meta data (tags).
+
+               If no lyrics tags are found, online services are tried next.
+
+               Supported file formats and lyrics tags:
+               - ogg/vorbis files with "LYRICS" and "SYNCLYRICS" tag
+               """
+               tags = info.get_tags()
+               if tags is None:
+                       self.search_online()
+                       return
 
+               for i in range(tags.get_tag_size("extended-comment")):
+                       (exists, value) = tags.get_string_index("extended-comment", i)
+                       #ogg/vorbis unsynchronized lyrics
+                       if exists and value.startswith("LYRICS"):
+                               text = value.replace("LYRICS=", "")
+                               self.lyrics_found(text)
+                               return
+                       #ogg/vorbis synchronized lyrics
+                       elif exists and value.startswith("SYNCLYRICS"):
+                               text = value.replace("SYNCLYRICS=", "")
+                               self.lyrics_found(text)
+                               return
+
+               self.search_online()
+
+       def search_online(self):
+               """Initiate searching the online lyrics services"""
+               if self.artist == "" and self.title == "":
+                       self.callback(_("No lyrics found"))
+               else:
                        parser = LyricsParse.Parser(self.artist, self.title)
-                       parser.get_lyrics(lyric_callback)
+                       parser.get_lyrics(self.search_online_result)
+
+       def search_online_result(self, text):
+               """Handle the result of searching online lyrics services"""
+               if text is not None:
+                       self.lyrics_found(text)
+               else:
+                       self.callback(_("No lyrics found"))
+
+
+       def lyrics_found(self, text):
+               f = open(self.cache_path, 'wt')
+               f.write(text)
+               f.close()
+
+               self.callback(text)
+
 
 class LyricPane(object):
        def __init__(self, db, song_info):
 
raw

1-command.sh

1
 ./shell/rhythmbox --debug-match lyrics filename.ogg
raw

2-output.txt

(14:13:55) [0x1c7bb20] [LyricGrabber.search_lyrics] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:299: discovering file:///home/cweiske/Musik/neu/Britney%20Spears/Femme%20Fatale/Britney%20Spears%20-%20Femme%20Fatale%20-%2003%20-%20Inside%20Out.ogg
(14:13:55) [0x1c7bb20] [LyricGrabber.search_lyrics] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:303: abc
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: title
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: artist
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: album
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: datetime
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: track-number
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: track-count
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: genre
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: image
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: extended-comment
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: encoder
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: encoder-version
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: audio-codec
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: nominal-bitrate
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: bitrate
(14:13:55) [0x1c7bb20] [fetags] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:317: tag: container-format
(14:13:55) [0x1c7bb20] [LyricGrabber.search_lyrics] /home/cweiske/php/rhythmbox/rhythmbox/data/../plugins/lyrics/lyrics.py:311: found lyrics tag:
LYRICS=Said you're gonna be here in a minute
Sitting in the mirror getting pretty
Gotta look my best if we're gonna break up
Gotta look my best if we're gonna break up
raw

5-copiedcode.py

1
2
3
4
5
6
7
        for i in range(tags.get_tag_size("extended-comment")):
            (exists, read) = tags.get_string_index("extended-comment", i)
            if exists and read.startswith("DISCSUBTITLE"):
                discname = read.replace("DISCSUBTITLE=", "")
                break
        return discname
 
raw

6-printalltags.py

1
2
3
               def fetags (list, tag):
                       print("tag: %s" % tag)
               tags.foreach(fetags)
Anonymous Anonymous
owner

History