Self-hosted service
HisAtri/LrcApi avatar
HisAtri/LrcApi

LrcApi: a small Flask service that fetches lyrics and writes them into your music files

A Flask API For StreamMusic.

1,263 stars121 forksPythonGPL-3.0

At a glance

What is it?
LrcApi is a Flask API built for StreamMusic that fetches LRC lyrics and cover art for a self hosted music server, and can write title, artist, album and lyrics tags back into FLAC, MP3 and OGG files. The authentication is off by default, and the licence is GPL-3.0 with a carve-out for the logo and the hosted service.
Who is it for?
LrcApi fits someone running a self hosted music server, Navidrome or StreamMusic, who wants lyrics and cover art without a paid metadata service and who accepts that the lyrics come from a third party upstream. It does not fit a public deployment without a reverse proxy in front of it, because the default is no authentication at all, and it does not fit a closed source product, since the licence requires source distribution and free access.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 66 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A lyrics service for a self hosted music server

LrcApi is a Flask API for StreamMusic, and the problem it solves is narrow and real. When you run your own music server, the clients that connect to it have no lyrics to show and no cover art, because the server never had them and the client cannot reach the streaming service the music came from.

The project's README, in its Chinese original, states the feature set plainly: it supports fetching LRC lyrics through Kugou and aggregate APIs, it offers both a text and a JSON API, and it can return cover images for a song, an album or an artist. The documentation site is docs.lrc.cx, and the package name on Docker Hub is hisatri/lrcapi.

The three endpoints are the core of it. The lyrics endpoint is at /lyrics on port 28883 by default, the newer JSON API is at /jsonapi, and the cover endpoint is at /cover. The port is configurable, and the default is 28883.

What the service does not do is worth stating as well. It is not a music server. It does not index your library or serve audio. It sits next to your music directory, asks an upstream provider for metadata about a track, and hands the result to whatever asks. The directory of mounted music is only relevant for the tag writing feature, which is a different capability with different risks.

The licence is GPL-3.0, and the README notes that JetBrains provides free open source licenses for the project.

Authentication is off unless you turn it on

The startup parameters are minimal, and one of them has a default that deserves attention.

There are two. The --port parameter is an int with the default value 28883. The --auth parameter is a string with no default, and the README explains that it is used for header authentication, that leaving it empty skips authentication, and that it validates the Authorization or Authentication field in the header. If the authentication does not match, the response is a 403.

So the default deployment of this service has no authentication at all, on a port that the README tells you to expose on 0.0.0.0. That is a reasonable default for a service running on a home network next to Navidrome, and a poor default for anything reachable from the internet.

The environment variable API_AUTH exists as an alternative to the command line flag. Its priority is lower than the --auth parameter, but the README says it is easier to deploy in Docker, and the example is the -e API_AUTH=... form in the run command and in the compose file. That is the form to use if you run the container, and it is the reason the parameter is documented in both places.

There is also a public instance at api.lrc.cx, with a lyrics endpoint at https://api.lrc.cx/lyrics and a cover endpoint at https://api.lrc.cx/cover. The README recommends trying it if you cannot deploy privately, and immediately qualifies it: the public API fetches lyrics through interfaces such as Kugou, so it may respond slowly and is not completely accurate. That is an honest warning, and it is the reason to run your own if the lyrics matter.

The tag endpoint writes back into the audio files

The most consequential feature in the project is the one that is not about fetching anything.

The metadata modification interface is a POST to /tag with an application/json body. The body carries a path and the fields to set, and the fields are title, artist, album and lyrics. The formats listed as tested are FLAC, ID3v2 for mp3, and VorbisComment for ogg.

The status codes are documented, which is the kind of detail that tells you the interface has been used against real files. 200 for success, 404 when the file is not found, 421 when there is no permission, 422 for a parse error, and 5xx for an execution failure.

The simple test environment in the README is a short Python snippet using requests to post a JSON object with path, title, artist, album and lyrics to the tag endpoint and print the status code and the response text. It is the shortest complete description of the contract in the project.

This is the feature to think hardest about, because it is the one that writes to your music library. The service needs write access to the files, it needs a path that resolves to a real file, and a mistake in the path or in the tag handling is a corrupted library rather than a failed request. The 404 and 421 codes exist precisely because that happens, and because the author expected the endpoint to be reachable by things other than the intended client.

The requirements list gives a hint at what the implementation uses: mutagen for the tags, tinytag for reading media metadata, and Pillow for image work.

Three ways to run it, and the mount path is the one to get right

The README documents three deployment routes, and they are worth reading together because the same configuration question appears in all of them.

The binary route is the simplest. Upload the binary to the directory you will run it in and start it with the port and auth options, for example./lrcapi --port 8080. The Python source route is to clone the project, or download and unpack the tar.gz into the run directory, then install the dependencies with pip install -r requirements.txt and start the service with python3 app.py --port 8080.

The Docker route publishes the image. The port mapping, the volume mapping and the environment variable are the three things you set:

bash
docker run -d \
    -p 28883:28883 \
    -v /home/user/music:/music \
    -e API_AUTH=自定义一个鉴权key \
    hisatri/lrcapi:latest

The README then recommends specifying a tag rather than using latest, and shows a compose file for the same thing:

yaml
services:
  lrcapi:
    image: hisatri/lrcapi:latest
    container_name: lrcapi
    ports:
      - "28883:28883"
    volumes:
      - /home/user/music:/music
    restart: always

The instruction that matters is the Navidrome one. If you are running Navidrome in Docker, change the host path in the volume mapping to the one you mapped for Navidrome, so that the -v parameter is consistent with Navidrome. In other words, map the same music directory in both containers, and if your music is stored at /www/path/music in Navidrome, use /www/path/music:/www/path/music here rather than a different host path.

That is the whole integration: LrcApi and Navidrome have to see the same files, and a mismatch produces a service that finds no music and a tag writer that writes to a directory the server never reads.

The cover endpoint is a proxy, and that is a cost

The cover API deserves its own note because of how it is implemented. The README says that images are currently returned using a reverse proxy strategy, and that this may involve a certain amount of upstream and downstream traffic consumption and latency.

In practice that means a request for cover art does not hit your music library at all. It goes out to the upstream provider, and the response comes back through the service. For a client that requests a cover for every track in a list, that is a stream of outbound requests from your server to a third party, and it is why the README suggests putting Nginx or Apache in front to handle the reverse proxy and SSL.

That arrangement also has a privacy shape. The upstream provider sees the requests your client makes, and the artwork is fetched from a service you do not control, which is the same upstream the lyrics come from.

The Dockerfile is worth a look for the deployment side. It is a two-stage build on python:3.12.1-slim, where the builder installs gcc, libjpeg-dev, zlib1g-dev, curl and build-essential before installing the requirements into a prefix directory, and the final stage copies that prefix in, copies the application code, removes the __pycache__ directories and the compiled Python files, and starts the container with python /app/app.py.

The Python version support is 3.10, 3.11 and 3.12, and the requirements pin Flask 3.0.0, Flask-Caching, waitress as the WSGI server, requests, aiohttp, mutagen, tinytag, Pillow, pyaes, Werkzeug, mygo, PyYAML and openai.

GPL-3.0 with an explicit carve-out for everything that is not source code

The README sets out its own second development rules, and they are more specific than the usual one-line licence statement, so they are worth reading in full before you build on this.

The project is open source under GPL-3.0, and the requirements listed are: keep the original copyright and licence notice in derivative works; clearly state the changes you make; when distributing, provide the complete source code and distribute your derivative work under GPL-3.0; and any commercial use based on this program must comply with GPL-3.0 and keep free open access. The README then adds a fifth point, which is the unusual one: apart from the source code, the other parts of the project, including the logo, the service and the slogan, are not open sourced under the GPL 3.0 protocol.

That carve-out is the part to think about. The code is copyleft and the branding is not, so a fork has to keep the code open and free but may not reuse the identity. It also means the distinction between the code and the running service is deliberate: the hosted instance at api.lrc.cx is a service, and the logo and the name belong to the project rather than to the licence.

The release history is modest. The tags are 1.5.6 from 2024-10-16, 1.5.7 from 2024-11-18 and 1.6.0 from 2025-01-21, and the last push to the repository was on 2026-08-18. The repository itself carries a Dockerfile, a build.sh, a buildup.py, a make_release.py, an api directory, a mod directory, a src directory, a tests directory and a devtools directory, so the packaging and the test suite are part of the tree rather than hidden outside it.

Editorial conclusion

LrcApi fits someone running a self hosted music server, Navidrome or StreamMusic, who wants lyrics and cover art without a paid metadata service and who accepts that the lyrics come from a third party upstream. It does not fit a public deployment without a reverse proxy in front of it, because the default is no authentication at all, and it does not fit a closed source product, since the licence requires source distribution and free access. Before you deploy it, set --auth or API_AUTH, mount the same music directory that Navidrome already uses, and pin a Docker tag rather than running latest.

Frequently asked questions

How do I install and run LrcApi?

It is distributed as a Docker image, as a binary, and as Python source. For Docker, run hisatri/lrcapi with a port mapping and a volume for your music directory; for source, install the requirements with pip install -r requirements.txt and start it with python3 app.py --port 8080.

Does LrcApi require authentication?

Not by default. The --auth parameter is used for header authentication, validating the Authorization or Authentication field and returning 403 when it does not match, but leaving it empty skips authentication. The API_AUTH environment variable offers the same setting with lower priority than the command line flag and is easier to use in Docker.

Which audio formats can LrcApi write tags to?

The formats listed as tested are FLAC, ID3v2 for mp3, and VorbisComment for ogg. The supported tags are title, artist, album and lyrics, written through the POST /tag endpoint with an application/json body.

How do I use LrcApi with Navidrome running in Docker?

Map the same music directory in both containers, so that the -v parameter matches the host path you already use for Navidrome. The README notes that if your music is stored at /www/path/music, the mapping should be /www/path/music:/www/path/music.

Can I use LrcApi without deploying it myself?

There is a public instance with a lyrics endpoint at https://api.lrc.cx/lyrics and a cover endpoint at https://api.lrc.cx/cover. The README warns that the public API fetches lyrics through upstream interfaces such as Kugou, so it may respond slowly and is not completely accurate.

Official sources

  1. HisAtri/LrcApi on GitHub
  2. License: GPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hisatri-lrcapi.svg)](https://hysenlabs.com/projects/hisatri-lrcapi)

Was this useful?