Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added music support through Lidarr #1238

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from

Conversation

0-Pierre
Copy link

@0-Pierre 0-Pierre commented Jan 8, 2025

Description

Jellyseerr fully support music trough Lidarr 🚀 🎵

Metadata
We’re using the Lidarr API (api.lidarr.audio) to improve speed, as MusicBrainz (MB) often suffers from high latency and server overload issues.
Trade-off: The results are slightly less precise compared to the MB Search API.

Overview
The overview section pulls data from Wikipedia, ensuring multilingual support for more flexible and accurate descriptions.

Image Handling
Images are initially loaded from the provided links and cached locally.
The Lidarr API offers additional artist images that aren’t available in the Cover Art Archive (CAA).
If an image is missing, the system automatically falls back to CAA.

Caching
By default, images from CAA and Fanart are cached locally, as these sources tend to load slowly.
Cached images are stored in the /cache folder and can be cleared via the settings page using a dedicated job.
Other images are only cached if specific options are enabled.

Similar Artists / Trending
We use the ListenBrainz API to provide recommendations for similar artists and trending music. Missing images are completed through the Lidarr API, with a fallback to CAA.

Artist Groups
The /group endpoint is dedicated to artist groups (bands). For individual artists (persons), we merge data with the /person endpoint from TMDb to avoid duplicate entries.

Informations Points
There are cases where images aren’t available on either the CAA or the Lidarr API. In such instances, we need to upload them to the MusicBrainz database to ensure they are displayed. This aligns with the core purpose of MusicBrainz, a community-driven platform for sharing metadata.
For this reason, I’ve removed Deezer as a fallback. Encouraging community contributions is more in line with MusicBrainz’s philosophy. Additionally, using Deezer’s fallback wasn’t always reliable, as the matching wasn’t based on precise IDs, though it worked correctly about 95% of the time.

Screenshot (if UI-related)

localhost_5055_ (1)
localhost_5055_ (2)
localhost_5055_ (4)
localhost_5055_ (3)

To-Dos

  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Issues Fixed or Closed

server/api/musicbrainz/index.ts Fixed Show fixed Hide fixed
server/api/musicbrainz/index.ts Fixed Show fixed Hide fixed
server/api/musicbrainz/index.ts Fixed Show fixed Hide fixed
server/api/musicbrainz/index.ts Fixed Show fixed Hide fixed
server/api/musicbrainz/index.ts Fixed Show fixed Hide fixed
server/api/musicbrainz/index.ts Fixed Show fixed Hide fixed
@github-actions github-actions bot added the merge conflict Cannot merge due to merge conflicts label Jan 9, 2025
Copy link

github-actions bot commented Jan 9, 2025

This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.

@github-actions github-actions bot removed the merge conflict Cannot merge due to merge conflicts label Jan 10, 2025
@@ -26,7 +27,7 @@
return null;
}

if (!data && !error) {
if (data === undefined && !error) {

Check warning

Code scanning / CodeQL

Useless conditional Warning

This negation always evaluates to true.
src/components/Setup/index.tsx Fixed Show fixed Hide fixed
src/components/Setup/index.tsx Fixed Show fixed Hide fixed
@gauthier-th
Copy link
Collaborator

@0-Pierre do not merge develop into this PR. Rebase on develop instead.
Merging develop is a very bad practice, it makes things harder for us to review and later to track changes.
It is written as well in our contribution guide.

Could you please rebase and remove all theses merge commits?

@0-Pierre
Copy link
Author

@0-Pierre do not merge develop into this PR. Rebase on develop instead. Merging develop is a very bad practice, it makes things harder for us to review and later to track changes. It is written as well in our contribution guide.

Could you please rebase and remove all theses merge commits?

Apologies, I'm not very familiar with the GitHub processes and features. I'll do my best, but the first commit was automatically created during a rebase when I used the "Rebase current branch..." option.

@0-Pierre 0-Pierre reopened this Jan 10, 2025
@fallenbagel
Copy link
Owner

A preview for this feature is available as preview-music-support tag.

(In about 15-20 mins)

@fallenbagel fallenbagel added the preview PRs deployed for testing with tag `:preview-prxx` label Jan 14, 2025
@AlessandroTischer
Copy link

Hi! I briefly tested the PR. Great work!
Some feedbacks (Warning! I'm quite a noob, so they might be wrong or due to inexperience):

  1. If Jellyseerr is already configured and you try to request any kind of music related content, you get a generic error. The fact that Lidarr was not configured should have been highlighted in the popup error, in my opinion.
  2. If an album doesn't have a cover, it is shown as it is hovered with the mouse, I think it's a bit odd.
  3. When an album is displayed, it contains a certain number of tracks (I suppose disk 1). When it is then selected in Lidarr, it might contain a whole lot of other tracks. You can test with Green Day - American Idiot: in Jellyseerr 13 tracks are listed, in Lidarr they are 57.
  4. There is no filter in the music panel. Is this just because it wasn't implemented yet or are there problems?
  5. If an artist is not monitored in Lidarr and I request an album from said artist, the artist gets added, and it takes a lot (>1 min, nothing unbearable, but I noticed it) before the album is monitored and then searched. Is this wanted for any reason?

Thank you again for the work!

@0-Pierre
Copy link
Author

Hi! I briefly tested the PR. Great work! Some feedbacks (Warning! I'm quite a noob, so they might be wrong or due to inexperience):

  1. If Jellyseerr is already configured and you try to request any kind of music related content, you get a generic error. The fact that Lidarr was not configured should have been highlighted in the popup error, in my opinion.
  2. If an album doesn't have a cover, it is shown as it is hovered with the mouse, I think it's a bit odd.
  3. When an album is displayed, it contains a certain number of tracks (I suppose disk 1). When it is then selected in Lidarr, it might contain a whole lot of other tracks. You can test with Green Day - American Idiot: in Jellyseerr 13 tracks are listed, in Lidarr they are 57.
  4. There is no filter in the music panel. Is this just because it wasn't implemented yet or are there problems?
  5. If an artist is not monitored in Lidarr and I request an album from said artist, the artist gets added, and it takes a lot (>1 min, nothing unbearable, but I noticed it) before the album is monitored and then searched. Is this wanted for any reason?

Thank you again for the work!

@AlessandroTischer Hey! Thanks for your notes, here's some points :
1- I'm fairly certain that the exact same behavior applies to Movies and Shows with Sonarr and Radarr
2 - The behavior is consistent across Movies and Show cards without covers, but missing covers are more frequent for Music, making it more noticeable. I can implement a fix to prevent the 'hovered' style from being applied to any card (Movies, TV, or Music) when a cover is missing.
3 - This occurs because I haven't configured the same release selection method used by Lidarr, such as choosing the best metadata or the most recent release. The current implementation is still very basic.
4 - Exactly, I’ve only configured the sorting options and nothing beyond that.
5 - Here's how the system currently works: if the artist doesn't already exist, it's first created in Lidarr without monitoring any of their albums. We then give Lidarr time to fetch all the metadata for the artist's discography. After about 40 seconds, the requested album is set to monitored. If multiple requests come in quickly, the system waits 30 seconds between each new request and checks at the end if all the requested albums are now monitored. It’s a very basic and somewhat crude solution, but it works. Without this process, I was encountering errors and unmonitored albums when users spammed requests. It can definitely be improved in the future, but for now, I prioritized getting a stable and reliable solution in place quickly.

On my end, I’m using it in production with all my users, and so far, I haven’t encountered any major bugs or errors causing server crashes. I’ll continue monitoring the logs. 👀 🔥

@gssariev
Copy link

Hey, @0-Pierre just popping by to say awesome work! It's fun to finally be able to play around with music requests, even though it's just a preview. Sharing some additional feedback as a Plex/Jellyseerr user:

  • No music library(s) syncing - in the initial setup process, only the movie and series type libraries are visible making it impossible to sync what music is currently available with Jellyseerr
  • Requesting an album by a monitored artist throws an error in Lidarr:

LidarrErrorPipeline Invalid request Validation failed: -- ForeignAlbumId: This album has already been added. LidarrErrorPipeline FluentValidation.ValidationException: Validation failed: -- ForeignAlbumId: This album has already been added.: Validation failed: -- ForeignAlbumId: This album has already been added.

Unmonitoring either the artist/album and making the request again results with the same error. The only work around that I've found so far is to remove the artist altogether from Lidarr and request it again. That way, it get added to Lidarr the way you described above; however, the following is observed:

  • Artist is unmonitored
  • Album is monitored
  • Download starts
  • Download is stuck at importing and after roughly 2-3 min requires manual import

Additionally, the artist gets added to Lidarr without a metadata profile; unsure if that is expected behavior. For context, I am running everything on Windows 11 and have tried using both the official Lidarr service as well as with ghcr.io/hotio/lidarr:pr-plugins, both experiencing the same.

Once again, thanks for putting in the work and time to make this!

@0-Pierre
Copy link
Author

Hey, @0-Pierre just popping by to say awesome work! It's fun to finally be able to play around with music requests, even though it's just a preview. Sharing some additional feedback as a Plex/Jellyseerr user:

  • No music library(s) syncing - in the initial setup process, only the movie and series type libraries are visible making it impossible to sync what music is currently available with Jellyseerr
  • Requesting an album by a monitored artist throws an error in Lidarr:

LidarrErrorPipeline Invalid request Validation failed: -- ForeignAlbumId: This album has already been added. LidarrErrorPipeline FluentValidation.ValidationException: Validation failed: -- ForeignAlbumId: This album has already been added.: Validation failed: -- ForeignAlbumId: This album has already been added.

Unmonitoring either the artist/album and making the request again results with the same error. The only work around that I've found so far is to remove the artist altogether from Lidarr and request it again. That way, it get added to Lidarr the way you described above; however, the following is observed:

  • Artist is unmonitored
  • Album is monitored
  • Download starts
  • Download is stuck at importing and after roughly 2-3 min requires manual import

Additionally, the artist gets added to Lidarr without a metadata profile; unsure if that is expected behavior. For context, I am running everything on Windows 11 and have tried using both the official Lidarr service as well as with ghcr.io/hotio/lidarr:pr-plugins, both experiencing the same.

Once again, thanks for putting in the work and time to make this!

@gssariev Thank you for your tests! I actually haven't tested it in a Plex setup situation yet. I'll review all the issues you reported and will get back with a commit to fix them. 😉

@mr2toyou
Copy link

mr2toyou commented Jan 14, 2025

@0-Pierre great work so far, I was hoping one day we would get this added. Some items I am seeing so far;

Running in docker with emby
Settings -> Emby -> Emby libraries, music library missing
Manage music /Manage Album showing [object Object] instead of Artist
clearing data from lidarr only available after going to requests.
2025-01-14T23:01:34.912Z [error][Media]: [Lidarr] Failed to remove album: 404 Not Found

"play on" not showing for available media player

@github-actions github-actions bot added the merge conflict Cannot merge due to merge conflicts label Jan 15, 2025
Copy link

This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.

Copy link
Owner

@fallenbagel fallenbagel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should only have the en.json using the pnpm i18n:extract for the i18n locales. The rest of the translation strings should be updated through weblate.

Could you remove those translation files and rebase?

@0-Pierre
Copy link
Author

You should only have the en.json using the pnpm i18n:extract for the i18n locales. The rest of the translation strings should be updated through weblate.

Could you remove those translation files and rebase?

Sure, i'm fixing all the requested issues and I'll take care of that 👍

@0-Pierre
Copy link
Author

@gssariev Hello! I'll be pushing a new commit that resolves all the Plex-related issues. However, I'm having trouble reproducing your other issues, as everything seems to be working perfectly on my end — requests are sent and downloaded properly. I even set up a Plex test server, and everything is running smoothly with the same Docker version you're using.

I've also implemented proper mapping for the Plex music library since Plex doesn't handle it like other libraries. They rely solely on release IDs, so we’re now mapping those to release-group IDs to ensure the database gets the correct entries. Gotta love Jellyfin for handling things more consistently in this area, lol.

Let me know if you encounter any additional issues!

image
image
image

Copy link
Collaborator

@gauthier-th gauthier-th left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just started a review, not done yet, here is what i noticed for now.

I stopped it because i'm pretty sure you messed up a lot of things during merge.
For instance: you removed a bunch of updates related to OverrideRules in MediaRequest.request() and changed the behavior to the old one.

I don't want to be rude, but please be careful when you rebase on the develop branch. This is already huge and hard to review, so please help us by making it easier.

Can you please fix these rebase/merge issues? Thanks for your work!

@@ -50,51 +51,57 @@ export const defaultSliders: Partial<DiscoverSlider>[] = [
order: 3,
},
{
type: DiscoverSliderType.POPULAR_MOVIES,
type: DiscoverSliderType.POPULAR_ALBUMS,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious why would you put music above all movies/series? IMO it makes more sense to add it at the end of the list

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding an album slider to the default sliders will not make albums appear in everyone's instances.
You should probably add a migration that adds an album slider to the list of the existing sliders, so people with an already configured instance can see albums.

Comment on lines +5 to +6
LYRICS = 4,
OTHER = 5,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the value of OTHER from 4 to 5 will break existing rows that have an OTHER value.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your migrations are not properly generated. I see missing things (an example: the change of tmdbId from non-nullable to nullable).
And the PostgreSQL migrations are missing too.
See how to generate migrations in our contribution guide: https://github.com/Fallenbagel/jellyseerr/blob/develop/CONTRIBUTING.md#migrations

Comment on lines -40 to -44
if (!Array.isArray(tmdbIds)) {
finalIds = [tmdbIds];
} else {
finalIds = tmdbIds;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happened to this part? If the ids argument is not a list but a primitive?

Comment on lines +82 to +85
const whereClause =
typeof id === 'string'
? { mbId: id, mediaType }
: { tmdbId: id, mediaType };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it better to assign the where clause by checking the mediaType instead of the type of the arg?

Comment on lines 232 to 240
if (
requestBody.mediaType === MediaType.MOVIE &&
requestBody.mediaType === MediaType.MUSIC &&
existing[0].status !== MediaRequestStatus.DECLINED
) {
logger.warn('Duplicate request for media blocked', {
tmdbId: tmdbMedia.id,
mbId: requestBody.mediaId,
mediaType: requestBody.mediaType,
is4k: requestBody.is4k,
label: 'Media Request',
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happened to the check for MediaType.MOVIE?

@@ -208,131 +257,116 @@ export class MediaRequest {
}
}

// Apply overrides if the user is not an admin or has the "advanced request" permission
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this deleted?

Comment on lines -216 to -302
const overrideRules = await overrideRuleRepository.find({
where:
requestBody.mediaType === MediaType.MOVIE
? { radarrServiceId: defaultRadarrId }
: { sonarrServiceId: defaultSonarrId },
});

const appliedOverrideRules = overrideRules.filter((rule) => {
const hasAnimeKeyword =
'results' in tmdbMedia.keywords &&
tmdbMedia.keywords.results.some(
(keyword: TmdbKeyword) => keyword.id === ANIME_KEYWORD_ID
);

// Skip override rules if the media is an anime TV show as anime TV
// is handled by default and override rules do not explicitly include
// the anime keyword
if (
requestBody.mediaType === MediaType.TV &&
hasAnimeKeyword &&
(!rule.keywords ||
!rule.keywords.split(',').map(Number).includes(ANIME_KEYWORD_ID))
) {
return false;
}
if (requestBody.mediaType !== MediaType.MUSIC) {
const defaultRadarrId = requestBody.is4k
? settings.radarr.findIndex((r) => r.is4k && r.isDefault)
: settings.radarr.findIndex((r) => !r.is4k && r.isDefault);
const defaultSonarrId = requestBody.is4k
? settings.sonarr.findIndex((s) => s.is4k && s.isDefault)
: settings.sonarr.findIndex((s) => !s.is4k && s.isDefault);

const overrideRuleRepository = getRepository(OverrideRule);
const overrideRules = await overrideRuleRepository.find({
where:
requestBody.mediaType === MediaType.MOVIE
? { radarrServiceId: defaultRadarrId }
: { sonarrServiceId: defaultSonarrId },
});

if (
rule.users &&
!rule.users
.split(',')
.some((userId) => Number(userId) === requestUser.id)
) {
return false;
}
if (
rule.genre &&
!rule.genre
.split(',')
.some((genreId) =>
tmdbMedia.genres
.map((genre) => genre.id)
.includes(Number(genreId))
)
) {
return false;
}
if (
rule.language &&
!rule.language
.split('|')
.some((languageId) => languageId === tmdbMedia.original_language)
) {
return false;
}
if (
rule.keywords &&
!rule.keywords.split(',').some((keywordId) => {
let keywordList: TmdbKeyword[] = [];

if ('keywords' in tmdbMedia.keywords) {
keywordList = tmdbMedia.keywords.keywords;
} else if ('results' in tmdbMedia.keywords) {
keywordList = tmdbMedia.keywords.results;
const appliedOverrideRules = overrideRules.filter((rule) => {
if (isTmdbMedia(tmdbMedia)) {
if (
rule.language &&
!rule.language
.split('|')
.some(
(languageId) => languageId === tmdbMedia.original_language
)
) {
return false;
}

return keywordList
.map((keyword: TmdbKeyword) => keyword.id)
.includes(Number(keywordId));
})
) {
return false;
}
return true;
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, why is all this deleted?

@gauthier-th
Copy link
Collaborator

@0-Pierre any update with the fix of the merge issues?
It seems quite critical to us, and we'd like it to be fixed as soon as possible before things become even more complicated.

If you're unsure on how to proceed, it's ok we can help and do it ourselves, but please let us know

@0-Pierre
Copy link
Author

0-Pierre commented Jan 19, 2025

@gauthier-th

I'd be glad if you could take care of this, as you're much more comfortable with the rebase process than I am. 🤝

Should I stop commiting for the moment ?

@gauthier-th
Copy link
Collaborator

@gauthier-th

I'd be glad if you could take care of this, as you're much more comfortable with the rebase process than I am. 🤝

Should I stop commiting for the moment ?

Yes please. I'll try to do it by 1/2 days.

@RobinDadswell
Copy link

Hi All, one of the Servarr devs here (we maintain Lidarr and it's backend). The Lidarr API exists solely for Lidarr's use and is aimed at it. We run a MusicBrainz mirror and ultimately do not test any changes with any products that are not Lidarr nor do we expect to support alternative products.

As much as I appreciate what you are trying to do, there has been no conversation with ourselves surroudning the user of our API and our general expectation would be that Jellyseerr would run a similar service themselves to support this instead of using our API as then changes to it can be tested/managed for compatibility with Jellyseerr.

@gauthier-th
Copy link
Collaborator

Hi All, one of the Servarr devs here (we maintain Lidarr and it's backend). The Lidarr API exists solely for Lidarr's use and is aimed at it. We run a MusicBrainz mirror and ultimately do not test any changes with any products that are not Lidarr nor do we expect to support alternative products.

As much as I appreciate what you are trying to do, there has been no conversation with ourselves surroudning the user of our API and our general expectation would be that Jellyseerr would run a similar service themselves to support this instead of using our API as then changes to it can be tested/managed for compatibility with Jellyseerr.

Hi, sorry for the use of your API. We (maintainers) didn't catch that from this PR as we have not yet reviewed this and it wasn't our intention to use something we shouldn't have (we thank you for the confirmation).

However, would you be willing to discuss with us about the use of this API? If not, that's fine, thanks for your awesome work on Servarr and Lidarr 🙂

@0-Pierre
Copy link
Author

Hi All, one of the Servarr devs here (we maintain Lidarr and it's backend). The Lidarr API exists solely for Lidarr's use and is aimed at it. We run a MusicBrainz mirror and ultimately do not test any changes with any products that are not Lidarr nor do we expect to support alternative products.
As much as I appreciate what you are trying to do, there has been no conversation with ourselves surroudning the user of our API and our general expectation would be that Jellyseerr would run a similar service themselves to support this instead of using our API as then changes to it can be tested/managed for compatibility with Jellyseerr.

Hi, sorry for the use of your API. We (maintainers) didn't catch that from this PR as we have not yet reviewed this and it wasn't our intention to use something we shouldn't have (we thank you for the confirmation).

However, would you be willing to discuss with us about the use of this API? If not, that's fine, thanks for your awesome work on Servarr and Lidarr 🙂

Our options are quite limited. The MusicBrainz API is too slow and has a low rate limit, which prevents it from delivering a smooth experience like TMDB. A possible solution would be to set up a Jellyseerr mirror API, similar to Lidarr's approach. This mirror API could prefetch data, populate the database with CCA images, and retrieve artist images from Fanart.tv using a global API key and maybe the missing one with LastFM with a global key for the API. This approach could offer additional benefits, such as more detailed data, preloaded charts, and an overall enhanced experience.

@Shadowghost
Copy link

It's always possible to self-host a music brainz instance, they provide a full setup guide and everything. Issue is that it requires comparably potent hardware.

@gauthier-th
Copy link
Collaborator

It's always possible to self-host a music brainz instance, they provide a full setup guide and everything. Issue is that it requires comparably potent hardware.

Yep, I started to setup a mirror on a VPS I don't really use, but the issue is the hardware requirements that are quite high, especially if we want something fast enough for thousands of users.
The costs of running such an instance would be quite high.

@jvoisin
Copy link

jvoisin commented Jan 20, 2025

Using MusicBrainz would work but take more time to gather all the data, which I think is an alright compromise, isn't it? Even it it takes a couple of days to scan a complete library, it shouldn't be a big deal.

@DunklerPhoenix
Copy link

I think there are multiple aproaches to attack this problem (some can be combined)

  1. Only us musicbrain
  2. Let the user choose an MB-instance of their own
  3. Create a donation based api
  4. Create a premium service
  5. Create a complete new project for cloning the mb database with the possibility of crowdpower (every user can donate compute power, ram and storage [e.g. torrent or blockchain storage])

Other ideas?

@Druidblack
Copy link

Why not use arl deezer? and other alternative sources for information. The MB database does not have the same content as streaming services. And their moderation in 7 days is depressing.

@0-Pierre
Copy link
Author

0-Pierre commented Jan 20, 2025

I can easily adjust the API to use MusicBrainz instead, but this would require all images to be fetched from CCA for covers. Since CCA servers are quite slow, these images will need to be cached. Additionally, we would need to use a global API key from Fanart (similar to the TMDB key) to retrieve artist covers and images. I will create a new branch lidarr-mb to make tests.

@0-Pierre
Copy link
Author

0-Pierre commented Jan 20, 2025

Why not use arl deezer? and other alternative sources for information. The MB database does not have the same content as streaming services. And their moderation in 7 days is depressing.

I've already considered this, but the main issue is that Lidarr relies on unique mbIds, while Deezer does not, which would inevitably cause mismatches over time. To ensure accuracy in media requests without manually mapping each item to the correct ID and to avoid running into rate limits we must adhere to the source app's identification system. The only viable solution would be a version of Lidarr built entirely around using Deezer as its indexer.

@adrianipopescu
Copy link

hm, maybe a unified metadata service with a standard api set that we can work into all the arrs and jellyfin and jellyseerr, that we can self-host, with a pull-through mode, would be an interesting side-project in case I ever get time.

@gauthier-th
Copy link
Collaborator

After discussions with @fallenbagel and the Lidarr dev, we're going to host our own MusicBrainz mirror.

Since the costs of hosting the mirror are going to be high, we're going to launch a fundraising initiative to finance these servers. We'll be discussing with Fallen how we're going to proceed.

@0-Pierre, could you please list all the MusicBrainz endpoints used in this PR (as well as the parameters used)? So that I can think about how to make the api for the MusicBrainz mirror (i'm not sure we will use the api in Perl they provide)

@0-Pierre
Copy link
Author

After discussions with @fallenbagel and the Lidarr dev, we're going to host our own MusicBrainz mirror.

Since the costs of hosting the mirror are going to be high, we're going to launch a fundraising initiative to finance these servers. We'll be discussing with Fallen how we're going to proceed.

@0-Pierre, could you please list all the MusicBrainz endpoints used in this PR (as well as the parameters used)? So that I can think about how to make the api for the MusicBrainz mirror (i'm not sure we will use the api in Perl they provide)

@gauthier-th I've started reworking the API in a new branch called lidarr-mb, based on the latest version of the current branch. So far, I've achieved response times comparable to Lidarr's mirror by applying some optimizations. Would it make sense to give me a few days to fully align the lidarr branch with lidarr-mb? After that, We could run some preview tests to evaluate its reliability for end use.

@gauthier-th
Copy link
Collaborator

After discussions with @fallenbagel and the Lidarr dev, we're going to host our own MusicBrainz mirror.
Since the costs of hosting the mirror are going to be high, we're going to launch a fundraising initiative to finance these servers. We'll be discussing with Fallen how we're going to proceed.
@0-Pierre, could you please list all the MusicBrainz endpoints used in this PR (as well as the parameters used)? So that I can think about how to make the api for the MusicBrainz mirror (i'm not sure we will use the api in Perl they provide)

@gauthier-th I've started reworking the API in a new branch called lidarr-mb, based on the latest version of the current branch. So far, I've achieved response times comparable to Lidarr's mirror by applying some optimizations. Would it make sense to give me a few days to fully align the lidarr branch with lidarr-mb? After that, We could run some preview tests to evaluate its reliability for end use.

Sure. Let us know how it goes.

@0-Pierre
Copy link
Author

0-Pierre commented Jan 22, 2025

Great news! I'm pretty sure we will not need a mirror server, the main issue with using the MusicBrainz API was:

Occasional 503 Errors: During searches or API calls, 503 errors would sometimes occur (server busy not a rate limit issue). I've added an immediate, invisible retry mechanism that resolves this problem effectively.

Slow Loading Times Due to CAA Image Fetching: Unlike the Lidarr API, which always has properly mapped URLs for album covers in its database, MusicBrainz doesnt provide it. The solution I implemented is simple and works seamlessly. After analyzing how mirrors like ListenBrainz operate, I discovered they store pre-mapped CAA URLs for each album ID.

To address this:
When searching or viewing trends, the initial results load quickly without blocking the front end.
Meanwhile, in the background, all CAA URLs are fetched asynchronously and stored in \config\cache\metadatas\caa\mapping.json in the following format:
"08aa7a6c-3e43-4459-87b2-e47faf3a088a": "/mbid-8e7746ca-7832-41b2-a731-de5b0010f37d/mbid-e7746ca-7832-41b2-a731-de5b0010f37d-13249592911_thumb250.jpg"

I will now review the process of fetching artist images from fanart.tv or other sources. If it introduces significant delays to requests, I will work on implementing a similar caching system to optimize performance.

This approach ensures instant loading as the smallest image versions are cached and ready for display.

Actually, 500 prefetched album covers and their mappings take up less than 10 MB in the cache folder.

Using the MB API resolves the mismatches present in the Lidarr API, such as incorrect artist-to-album associations. Additionally, it provides more album images and significantly improves the accuracy of the results, so I will be able to add country flags on album details, tags etc....

@Gauvino
Copy link
Contributor

Gauvino commented Jan 22, 2025

Great news! I'm pretty sure we will not need a mirror server, the main issue with using the MusicBrainz API was:

Occasional 503 Errors: During searches or API calls, 503 errors would sometimes occur (server busy not a rate limit issue). I've added an immediate, invisible retry mechanism that resolves this problem effectively.

Slow Loading Times Due to CAA Image Fetching: Unlike the Lidarr API, which always has properly mapped URLs for album covers in its database, MusicBrainz doesnt provide it. The solution I implemented is simple and works seamlessly. After analyzing how mirrors like ListenBrainz operate, I discovered they store pre-mapped CAA URLs for each album ID.

To address this:

When searching or viewing trends, the initial results load quickly without blocking the front end.

Meanwhile, in the background, all CAA URLs are fetched asynchronously and stored in \config\cache\metadatas\caa\mapping.json in the following format:

"08aa7a6c-3e43-4459-87b2-e47faf3a088a": "/mbid-8e7746ca-7832-41b2-a731-de5b0010f37d/mbid-8e7746ca-7832-41b2-a731-de5b0010f37d-13249592911_thumb250.jpg"

I will now review the process of fetching artist images from fanart.tv or other sources. If it introduces significant delays to requests, I will work on implementing a similar caching system to optimize performance.

This approach ensures instant loading as the smallest image versions are cached and ready for display.

You are an absolute god

@adrianipopescu
Copy link

adrianipopescu commented Jan 22, 2025

any chance to get a config option to point to our own selfhosted mb instance? for inconsistent internet situations and / or data capped (think rural 4g)

I have a relative that likes to queue up their requests on their server and just lets it do its thing behind the scenes but hates how slow things load sometimes

@0-Pierre
Copy link
Author

any chance to get a config option to point to our own selfhosted mb instance? for inconsistent internet situations and / or data capped (think rural 4g)

I have a relative that likes to queue up their requests on their server and just lets it do its thing behind the scenes but hates how slow things load sometimes

No worries, it's actually very fast, maybe even faster than using the Lidarr API. It will be pushed soon.

@JTCorrin
Copy link

any chance to get a config option to point to our own selfhosted mb instance? for inconsistent internet situations and / or data capped (think rural 4g)
I have a relative that likes to queue up their requests on their server and just lets it do its thing behind the scenes but hates how slow things load sometimes

No worries, it's actually very fast, maybe even faster than using the Lidarr API. It will be pushed soon.

Just wanted to chime in and say great work to @0-Pierre and all involved. Looking forward to this

@0-Pierre
Copy link
Author

0-Pierre commented Jan 25, 2025

Hello everyone, I have some updates to share:

  • The rework of the API is almost complete. We are now fully integrated with ListenBrainz and MusicBrainz as our primary APIs.
  • We are using fanart and cca as our image providers, with fully asynchronous loading and caching implemented.
  • I've upgraded the mapping to personId from tmdb for better content merging.
  • Thanks to ListenBrainz, I've improved the data by including track feat artist details, with links to their profiles:
    image
  • The artist's country and a link to their profile are now available
    image
  • I have added tags, which aren't used yet but are available for future filtering systems or other features that I won't be implementing myself:
    image
  • The new caching system stores mappings for tmdb personId to artist mbId, cca images, and fanart images:
    image
    image
    image
  • The cache size is quite small for 636 items, with fanart images being the largest files. In the future, we could consider implementing an automatic compression process for these images.
    image

The more you use the system, the faster the loading times become, as it caches all the heavy processes efficiently.

There are still some tweaks needed to ensure compatibility with the latest development commits and translations. I’ll keep you updated! 🔥

@formeo14
Copy link

formeo14 commented Jan 25, 2025

Out of curiosity, how is the caching system designed to handle large data sets efficiently?
Is each artist stored in a separate JSON file, or are multiple artists and their track data combined into a single file? Loading data from a large JSON file could also potentially slow down the loading.
Additionally, how does the caching system determine when to invalidate the cache? For instance, if an artist releases a new album, does the system use timestamps within the JSON files to manage updates, or is there another method in place?

@0-Pierre
Copy link
Author

@formeo14 We're not replicating the entire MB database in the cache only storing the image paths and TMDb IDs associated with an Artist MBID. On each call, we check the cache to see if any previously missing data is now available. For instance, if a person in TMDb has been created or a new image has been added to CAA, it will be added to the cache for the corresponding artist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge conflict Cannot merge due to merge conflicts preview PRs deployed for testing with tag `:preview-prxx`
Projects
None yet
Development

Successfully merging this pull request may close these issues.