Skip to content

feat(Melon): Implement Melon provider - #234

Open
Maxr1998 wants to merge 4 commits into
kellnerd:mainfrom
Maxr1998:melon-provider
Open

feat(Melon): Implement Melon provider#234
Maxr1998 wants to merge 4 commits into
kellnerd:mainfrom
Maxr1998:melon-provider

Conversation

@Maxr1998

Copy link
Copy Markdown
Contributor

Melon is a South Korean digital streaming service. This provider uses the mobile app's API, which is publicly accessible without authentication. The API returns album metadata and track listings via separate endpoints. The brand color and icon have been taken from the website.

To implement this change, I used the help of an AI tool (namely Claude Code using the Sonnet 4.6 model), which created the Melon API types from HTTP responses caputured with curl using the endpoints described in #203, and wrote the initial provider implementation (using the previous Bugs! provider as a reference). Regardless, all code changes have been personally reviewed by me, and I take full responsibility for them. I also selected and wrote the test cases myself.

Assisted-by: Claude:claude-sonnet-4.6

Closes #203.

Melon is a South Korean digital streaming service.
This provider uses the mobile app's API, which is publicly accessible without authentication. The API returns album metadata and track listings via separate endpoints.
The brand color and icon have been taken from the website.

To implement this change, I used the help of an AI tool (namely Claude Code using the Sonnet 4.6 model), which created the Melon API types from HTTP responses caputured with curl using the endpoints described in kellnerd#203, and wrote the initial provider implementation (using the previous Bugs! provider as a reference). Regardless, all code changes have been personally reviewed by me, and I take full responsibility for them. I also selected and wrote the test cases myself.

Assisted-by: Claude:claude-sonnet-4.6
@Maxr1998

Copy link
Copy Markdown
Contributor Author

Still plan to do some fine-tuning on the provider implementation and fix the ToDo I left there, but I think it is mostly ready to review already.

@kellnerd kellnerd added feature New feature or request provider Metadata provider labels Jul 20, 2026

@kellnerd kellnerd left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thank you for giving this a shot. Sorry that it took quite a while till I came back to reviewing it, I needed a short break after your last PR and then it got too hot and my motivation was melting like ice in the sun.

Without having tried the provider so far, here is some initial feedback.

Comment thread providers/Melon/mod.ts Outdated
Comment thread providers/Melon/mod.ts

readonly supportedUrls = new URLPattern({
hostname: 'www.melon.com',
pathname: String.raw`/:type(album|song|artist)/detail.htm\?\1Id=:id(\d+)`,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Interesting, I wouldn't have thought that this works, the search query is a separate property of the URLPattern usually. I have an unpublished provider which extracts the ID from the query that way, but this surely wouldn't allow us to have a regex back reference.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I admit that part was clanker output, but I didn't question it since it worked perfectly. The idea is that Melon always uses the same keyword for the path and the query parameter, and they should never mismatch. We don't really have to enforce that, though, so I can rewrite that part to eliminate the backreference if you like.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

No, I actually like the fact that the back-reference makes the pattern stricter.
I would have to have a deeper look into the specs or docs for URLPattern to see if the behavior of "query as part of the path" is expected or depending on the implementation, but I guess it is fine.

Comment thread providers/Melon/mod.ts Outdated
Comment thread providers/Melon/mod.ts Outdated
Comment thread providers/Melon/mod.ts Outdated
Comment on lines +51 to +54
ISSERVICE: boolean;
/** Whether the song is marked as a title song, which is usually actively promoted, has an MV, and/or was previously released as a single. */
ISTITLESONG: boolean;
ISHOLDBACK: boolean;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Have you seen examples where ISSERVICE and ISHOLDBACK indicate unavailable tracks/releases or something else which is of interest?
There is also an ISFREE attribute in the testdata which you haven't used here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've been looking into this for a bit after submitting the PR, but couldn't find any release with the ISHOLDBACK set to false. I also couldn't find any release that wasn't available both for streaming and purchase. I updated the code for now to simply mark every release as streamable and downloadable, since that's also the default used with URL normalization in MusicBrainz right now.

Comment thread providers/Melon/mod.ts

private extractLabels(planCnpy?: string): Label[] {
if (!planCnpy) return [];
return planCnpy.split(', ').map((name) => ({ name: name.trim() }));

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

We already have a utility function splitLabels which should be reused. Feel free to enhance its test cases with a Korean script example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It appears that splitLabels only works for splitting labels separated by slashes, not commas like Melon uses. Should I make the split character customizable in splitLabels then?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Oh, sorry, I had overlooked that. If Melon specifically uses the comma as a separator, then it makes sense to keep this separate.

P.S. We could probably split on slash and comma in the general utility function even, given that this seems to be fine in case of my copyright parser script and release label fields are even simpler than copyright texts (where the comma is also used to structure the sentence).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure! Should I make the required changes within this PR, or should that be a separate change later on? I can also do this on a separate branch/PR and then rebase this PR.

Comment thread providers/Melon/mod.ts
cdList: MelonDisc[];
}

function parseISSUEDATE(date: string): PartialDate {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

We should revisit this helper as part of #169 one day 😇

Comment thread providers/Melon/mod.ts
if (albumType === 'EP') return ['EP'];
if (albumType === '옴니버스') return ['Compilation'];
if (albumType === 'OST') return ['Soundtrack'];
if (albumType === '리믹스') return ['Single', 'Remix'];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Could you confirm the theory that this is only used for remix singles and not remix albums?

>
<path
fillRule='evenodd'
d='M3.89 15.25A8.0 7.95 0 1 0 19.89 15.25A8.0 7.95 0 1 0 3.89 15.25ZM8.34 15.25A3.55 3.84 0 1 0 15.44 15.25A3.55 3.84 0 1 0 8.34 15.25ZM15.3 3.7A2.95 2.95 0 1 0 21.2 3.7A2.95 2.95 0 1 0 15.3 3.7Z'

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nice and simple logo.
I do roughly understand SVG path syntax and wonder whether it is intentional that the logo does not consist of perfect circles (A8.0 7.95 is x and y radius for example) or a conversion/rounding error at some point.
Not worth changing that right now, one day I may make Tabler-style icons for some of the providers and submit them upstream like I did for the MusicBrainz icon.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I retraced the icon from their full name logo, which doesn't use perfect circles. However, I'll look if I can find an actual icon SVG in their Android app. That one seems more circle-ish to me when checking again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request provider Metadata provider

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Melon

2 participants