Use Cover Art Archive for albums with MusicBrainz IDs #12
Reference in New Issue
Block a user
Delete Branch "cover-art-archive"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #10
Summary of Changes
Hello @sproutsberry, I'm Gemini Code Assist1 ! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request significantly enhances the plugin's ability to retrieve album artwork by integrating with the Cover Art Archive (CAA). It introduces a new configuration option to enable this feature, allowing users to leverage a richer source for album covers. The artwork fetching logic has been updated to intelligently prioritize CAA when enabled, falling back to existing methods, and centralizes caching for improved performance.
Highlights
caaenabledconfiguration option, allowing users to prioritize CAA artwork retrieval over existing methods (uguu.se and direct Navidrome).getImageURLfunction, ensuring consistent caching across all artwork sources.coverartarchive.orgin themanifest.jsonto allow API calls to the new artwork source.🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.
Changelog
cacheKeyFormatfor standardized caching keys.uguu.serelated constants and functions into a dedicated section.getImageViaUguu.getAlbumIDFromTrackID,getMusicBrainzIDFromAlbumID,fetchImageFromCAA, andgetImageViaCAAto interact with MusicBrainz and Cover Art Archive.subsonicGetSongResponse,subsonicGetAlbumResponse,caaResponse) for parsing JSON responses from Navidrome and CAA APIs.getImageURLto act as the primary artwork retrieval function, incorporating a cache check, conditional CAA fetching, conditional uguu.se fetching, and a fallback to direct Navidrome artwork.caaEnabledKeyconstant for the new configuration.requiredHoststo includecoverartarchive.orgfor network access to the Cover Art Archive.caaenabledto the plugin configuration schema, allowing users to enable or disable CAA integration.caaenabledproperty into the UI schema for user configuration.Activity
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point by creating a comment using either
/gemini <command>or@gemini-code-assist <command>. Below is a summary of the supported commands on the current page./gemini review/gemini summary/gemini helpCustomization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a
.gemini/folder in the base of the repository. Detailed instructions can be found here.Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩︎
Code Review
This pull request introduces a new feature to fetch cover art from the
Cover Art ArchiveusingMusicBrainzIDs. While the functionality is a great addition, there are critical security concerns related to URL construction. The current implementation lacks proper URL encoding for both internalSubsonicAPI calls and externalCover Art Archiverequests, which could lead toHTTP Parameter Injection,SSRF, orpath traversalvulnerabilities. It is strongly recommended to use thenet/urlpackage for proper URL escaping. Additionally, the feature has high-severity caching issues that could impact fallback image providers, and there are medium-severity suggestions to improve code clarity and maintainability, including renaming a confusing struct field and addressingTODOcomments.@@ -54,3 +51,4 @@pdk.Log(pdk.LogDebug, fmt.Sprintf("Cache hit for uguu.se artwork: %s", trackID))return cachedURL}Caching an empty URL string prevents the fallback logic in
getImageURLfrom working on subsequent requests for the same track. The cache should only be set if a valid URL is obtained.Caching an empty URL string will prevent the fallback logic in
getImageURL(touguu.seor direct) from triggering on subsequent requests for the same track. The cache should only be set when a valid image URL is found.The
usernameandtrackIDvariables are concatenated directly into the query string of the Subsonic API call without URL encoding. This makes the application vulnerable to HTTP Parameter Injection (HPP). An attacker could provide a malicioustrackID(e.g.,id1&u=admin) to inject additional parameters into the internal API request, potentially bypassing access controls or manipulating the API's behavior.Note: You will need to import the
net/urlpackage to useurl.QueryEscape.Similar to the
getSongcall, thegetAlbumAPI call is vulnerable to HTTP Parameter Injection because theusernameandalbumIDare not URL-encoded before being concatenated into the query string.Note: You will need to import the
net/urlpackage to useurl.QueryEscape.The
musicBrainzIDis concatenated directly into the URL path for an external request tocoverartarchive.orgwithout any validation or escaping. If a malicious MusicBrainz ID containing path traversal sequences (e.g.,../../) is retrieved from the database (which can be populated from untrusted file tags), it could lead to a request being sent to an unintended endpoint on the target host, a form of Server-Side Request Forgery (SSRF).Note: You will need to import the
net/urlpackage to useurl.PathEscape.The field name
Songis confusing here, as the JSON key is"album". For better code clarity and maintainability, it's recommended to rename this field toAlbum. You will also need to update the usage of this field ingetMusicBrainzIDFromAlbumID. I've added a separate comment for that.To correspond with the suggested change of renaming the
Songfield toAlbuminsubsonicGetAlbumResponse, this line should be updated to access the field viaresponse.Data.Album.The
TODOcomment here is misleading. In Go, a string variable cannot benil, so checking for an empty string""is the correct and sufficient way to see if it has a value. The TODO can be removed to avoid confusion.There's a
TODOhere to name this section. It's good practice to resolve these before merging. A descriptive name would improve code readability.@@ -54,3 +51,4 @@pdk.Log(pdk.LogDebug, fmt.Sprintf("Cache hit for uguu.se artwork: %s", trackID))return cachedURL}Not relevant
Intentional
Ready to go!
Download the plugin for this PR: discord-rich-presence.zip
Built from
371dff6352on 2026-02-09T19:59:26Z@@ -64,6 +65,11 @@"title": "Upload artwork to uguu.se (enable if Navidrome is not publicly accessible)",The default should be false.
Thanks! Looks good. I'll test it out in my server before merging.
Trying this PR, it seems that it does not guard against (fairly frequent) CAA errors:
This causes the activity to not be updated. We may have to capture this error somehow and fallback gracefully to the other methods.
I'm already handling HTTP >=400 gracefully, but I had no idea the API timeouts so readily (and Go doesn't handle it well, I guess)
I'll look into implementing this in a few. I haven't had any problems with the API personally, so I may not be able to debug.
Ah, MusicBrainz has a maintenance window right around now where a lot of requests fail. Forgot about that.
I've implemented a 5 second timeout which should guard for failed and timed out requests. Give it another try and let me know how it goes.
This now requires the scheduler, which means it isn't compiling with the recommended tinygo command (which I didn't notice since I've been using standard Go). Since the pdk's HTTP request API is so limited, there aren't really any other options I can use to handle network failures gracefully. This might be bigger in scope than expected haha
Well, I may add a Navidrome host function for HTTP calls, with more options than the native Extism one. Once I get that in place I'll ping you here.
Pull request closed