How can I modify this plugin to use mp3 tags instead of lrc files?

414 views Asked by At

The music player "QuodLibet" contains a plugin that synchronizes lyrics to the currently playing song. It achieves this by parsing the data from a .lrc file. This data however could also be stored in the file metadata, and I want to modify it to account for that.

I did some digging through the pc and I found the .py file containing the plugin. I looked through the code in hopes it would be easy, but I never programmed with python and there's some code that I don't understand. It doesn't look too complicated tho, so I was thinking if you guys could decypher it.

def _build_data(self):
    self.text_buffer.set_text("")
    if app.player.song is not None:
        # check in same location as track
        track_name = app.player.song.get("~filename")
        new_lrc = os.path.splitext(track_name)[0] + ".lrc"
        print_d("Checking for lyrics file %s" % new_lrc)
        if self._current_lrc != new_lrc:
            self._lines = []
            if os.path.exists(new_lrc):
                print_d("Found lyrics file: %s" % new_lrc)
                self._parse_lrc_file(new_lrc)
        self._current_lrc = new_lrc

It seems like it stores the data in new_lrc, so I tried to change that line in an attempt to have it fetched from the tags:

        new_lrc = os.path.splitext(track_name)[0] + ".lrc"

Changed to this:

        new_lrc = app.player.song.get("~lyrics")

I verified that ~lyrics is indeed the correct way to reference the tag since that's the way it's used in other parts of the code.

This worked to some extent. It was an improvement from previous test in the way that it didn't told me there was something undefined, here is what the program tells me when I boot it up:

TypeError: stat: path should be string, bytes, os.PathLike or integer  not NoneType
------
Traceback (most recent call last):

  File "/usr/lib/python3/dist-packages/quodlibet/plugins/events.py", line 141, in __invoke
    handler(*args)

  File "/usr/lib/python3/dist-packages/quodlibet/ext/events/synchronizedlyrics.py", line 282, in plugin_on_song_started
self._build_data()

  File "/usr/lib/python3/dist-packages/quodlibet/ext/events/synchronizedlyrics.py", line 195, in _build_data
    if os.path.exists(new_lrc):

  File "/usr/lib/python3.6/genericpath.py", line 19, in exists
    os.stat(path)

TypeError: stat: path should be string, bytes, os.PathLike or integer, not NoneType

I'll let you decide what you make out of that. Here's another snippet of code that is the other part of the puzzle:

def _parse_lrc_file(self, filename):
    with open(filename, 'r', encoding="utf-8") as f:
        raw_file = f.read()
    raw_file = raw_file.replace("\n", "")
    begin = 0
    keep_reading = len(raw_file) != 0
    tmp_dict = {}
    compressed = []
    while keep_reading:
        next_find = raw_file.find("[", begin + 1)
        if next_find == -1:
            keep_reading = False
            line = raw_file[begin:]
        else:
            line = raw_file[begin:next_find]
        begin = next_find

        # parse lyricsLine
        if len(line) < 2 or not line[1].isdigit():
            continue
        close_bracket = line.find("]")
        t = datetime.strptime(line[1:close_bracket], '%M:%S.%f')
        timestamp = (t.minute * 60000 + t.second * 1000 +
                     t.microsecond / 1000)
        words = line[close_bracket + 1:]
        if not words:
            compressed.append(timestamp)
        else:
            tmp_dict[timestamp] = words
            for t in compressed:
                tmp_dict[t] = words
            compressed = []

    keys = list(tmp_dict.keys())
    keys.sort()
    for key in keys:
        self._lines.append((key, tmp_dict[key]))
    del keys
    del tmp_dict

That's the part that made things get difficult, and that's where I'm stuck now. The way I see it, the code is expecting to deal with a file, not a tag, so when it makes it's calls they are not working. Any clues to what I can try?

1

There are 1 answers

0
tralph3 On

Doesn't matter guys, I already modified it myself and it works as I want now. Here's a link so you can download it and check it out if you want: GitHub issue