I have pondered doing this a yearish ago when I noticed that jellyfin did not yet support external lyrics via API (I use jellyfin as provider for Symfonium).
After some further research I found out that this feature is coming (it will be in 10.9.0 which should release within the next weeks) so I did not go through with it (because I prefer using external lyrics) but I can tell you what I would have done.
Note from future me: Writing this down took me way more time than I expected but I’m glad it is done and hope it helps you. 
Since I have a massive collection and cannot open all songs in mp3tag (the tagging software I use, I’ve never used OneTagger) at the same time, I first decided to specifically only edit files that do have external lyrics. If you have a reasonable collection size where opening it in full is an option, you can skip the filtering steps I’m about to describe (I still recommend them for other reasons tho).
Additionally I wanted to be sure that all .lrc files have a matching .flac or .mp3 song (otherwise the automatic embedding would fail at least partially).
Checking if all .lrc files have matching music files (flac/mp3):
- I’ve used Voidtools Everything to search for
.lrc Z:\
Z:\ being the root of the mounted music SMB share where all my music is
- Then I selected all results via
CTRL+A
(after checking that it only found .lrc files) and used right click → Copy Full Path
on the selection
Depending on how many .lrc files you have (I have 13.034) this step takes a while.
- Then you paste these paths into a new text file named
lyrics.txt
and save the changes to disk
This is where a python script I wrote for this purpose comes into play (it’s rough but has worked when I tried it). For it to work you obviously have to have python installed on your system.
import os
import re
lyrics = open('lyrics.txt', 'r', encoding="utf8")
lines = lyrics.readlines()
def main():
for line in lines:
if os.path.isfile(replext("flac", line)):
writelog("flac", line)
elif os.path.isfile(replext("mp3", line)):
writelog("mp3", line)
else:
with open("Fehler.txt", "a", encoding="utf8") as log:
log.write(line)
def writelog(ext, line):
with open(f"{ext}.txt", "a", encoding="utf8") as log:
log.write(re.sub('\.lrc$', f'.{ext}', line))
def replext(ext, line):
return re.sub('\.lrc$', f'.{ext}', line).strip()
if __name__ == "__main__":
main()
You can save this as lyricstest.py
for example and it has to be put in the same folder where you have created lyrics.txt
.
This replaces the .lrc extension from the lyrics.txt paths with .flac or .mp3, checks if the resulting new filepaths exist on your system and logs the results (with .flac/.mp3 extensions) into 3 new .txt files.
flac.txt when it found a matching flac file, mp3.txt when it found a matching .mp3 file and Fehler.txt (I’m German, “Fehler” means error) when it found neither, meaning there isn’t a .flac or .mp3 file linked to the .lrc file.
Broken links can happen when you for example change your file naming scheme for songs without adjusting the .lrc file names at the same time.
To run the script, open the folder where lyrics.txt and lyricstest.py reside in windows explorer, press CTRL+L
, type lyricstest.py
and press ENTER
. That executes the script and should result in 1-3 new text files in the same folder as described above.
If it is created you should check Fehler.txt and investigate why there are no songs linked to the .lrc files (when I first ran this test I found over 200 .lrc files with broken links and fixed them).
To re-run the script after making changes to your files to fix broken links, you should recreate the lyrics.txt file and delete mp3.txt, flac.txt and Fehler.txt before re-running the script to avoid confusion as new results are appended.
Once you are satisfied that all your .lrc files have linked music files it’s mp3tag time
As we now have files containing the paths of all .flac and .mp3 songs which have linked .lrc files, how do we open these (and only these) in mp3tag?
You might have guessed it, I wrote another python script for that.
import subprocess
import os
def main(extensions = ["flac", "mp3"]):
for ext in extensions:
if os.path.isfile(f"{ext}.txt"):
song_list = open(f'{ext}.txt', 'r', encoding="utf8")
track_path = song_list.readlines()
for track in track_path:
try:
subprocess.run(["mp3tag", "/add", "/fn:" + track.strip()])
except subprocess.CalledProcessError:
print(f"Error while adding {track} to mp3tag.")
else:
print(f"{ext}.txt not found.")
if __name__ == "__main__":
main()
I called this one mp3tag.py
but you can call it whatever you want. This also needs to be in the same folder where flac.txt and mp3.txt are stored to work.
The script checks if flac.txt and mp3.txt exist and if so, opens the songs they contain in mp3tag. As this happens song by song it might take quite a while and lag/flicker but I think I remember testing it with around 9.000 files a year ago and it finished loading eventually.
The neat part is that once all this is done, we have only those songs loaded into mp3tag that do have a matching .lrc file.
WARNING
If you execute the next step, it could lead to data loss as we’re discarding/replacing already embedded lyrics with the contents of the .lrc files.
If you want to reserve the option to later on retrieve the embedded lyrics, you can create an action in mp3tag that saves the current content of the LYRICS
tag (I think that’s the tag for synced lyrics) to a custom field like LYRICS_BU
.
That way when LYRICS
is replaced with the content of the .lrc files you still have a copy of the old embedded tags in LYRICS_BU
.
You can save this as backup lyrics.mta
in this path (replacing USER with your user name).
C:\Users\USER\AppData\Roaming\Mp3tag\data\actions\
[#0]
T=5
1=%LYRICS%
F=LYRICS_BU
This backs up the current content of LYRICS to LYRICS_BU.
FINAL STEP
Now to the last and possibly destructive action that I stole (and adjusted) from this thread:
You can save this as Import LRC From Filename.mta
in this path (replacing USER with your user name).
C:\Users\USER\AppData\Roaming\Mp3tag\data\actions\
[#0]
T=14
F=LYRICS
1=%_filename%.lrc
You can run either of these actions on all selected songs from within mp3tag via
right click → Actions → select from dropdown
If you have followed this rather lengthy and complex guide correctly, you should now have the content of all .lrc files embedded in your songs.
Be aware that there are different tags used for synced / unsynced lyrics in mp3/flac, which is one of the reasons why I prefer having lyrics externally, different conventions for different file types give me a headache.
If you have backed up previous lyrics to LYRICS_BU
and wish to delete them you can open all tracks in mp3-tag, press ALT+T
, look for the LYRICS_BU field, select it, click the red remove Field
X on the right and then click OK
to permanently remove the old tags.
Disclaimer:
I’ve written these scripts and actions for my personal use with minimal error handling, so be careful, try to understand everything before executing anything and whatever you do, you do at your own risk.