Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,515 changes: 3,515 additions & 0 deletions providers/Genie/__snapshots__/mod.test.ts.snap

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions providers/Genie/api_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
export interface GenieResult {
/** Response status code, `"0"` indicates success. */
ret_code: string;
/** Response status message, e.g. "성공" (success). */
ret_msg: string;
}

export interface GenieAlbumResponse {
result: GenieResult;
album_info?: GenieAlbumInfo;
album_song_list?: GenieSong[];
/** Music videos, magazine articles, user comments and album recommendations (unused). */
album_mv_list?: unknown[];
album_magazine_list?: unknown[];
reply_list?: unknown[];
album_popular_list?: unknown[];
}

export interface GenieAlbumInfo {
album_id: string;
album_name: string;
artist_id: string;
artist_name: string;
/** Percent-encoded 140x140 artist image URL. */
artist_img_path: string;
/** The album's release date, formatted as YYYYMMDD. */
album_release_dt: string;
/** Album description with HTML markup, often contains detailed credits. */
album_desc: string;
/** The distributor (유통사), e.g. "카카오엔터테인먼트". Can contain multiple comma-separated values. */
album_producer: string;
/** The planning agency/label (기획사), e.g. "WM Entertainment". Can contain multiple comma-separated values. */
album_planner: string;
/** Whether the album is age-restricted, "Y" or "N". */
album_adlt_yn: string;
/** Percent-encoded 140x140 cover image URL. */
album_img_path: string;
/** Percent-encoded 600x600 cover image URL (the largest available size). */
album_img_path600: string;
/** The type of the album: "싱글/EP", "정규앨범", "베스트/컬렉션", "컴필레이션" or "기타앨범". */
album_type: string;
/** Whether the album is available for service, "Y" or "N". */
album_service_yn: string;
/** Subgenre, e.g. "댄스" or "애니메이션/게임". */
lowcode_name: string;
/** Main genre category, e.g. "가요", "OST" or "J-POP". */
middlecode_name: string;
total_reply_cnt: string;
like_cnt: string;
like_yn: string;
dolby_yn: string;
}

export interface GenieSong {
song_id: string;
dlm_song_lid: string;
song_name: string;
artist_id: string;
artist_name: string;
album_id: string;
album_name: string;
/** Percent-encoded 140x140 cover image URL. */
album_img_path: string;
/** Percent-encoded 100x100 cover image URL. */
thumbnail_img_path: string;
/** Whether the song is age-restricted, "Y" or "N". */
song_adlt_yn: string;
mv_adlt_yn: string;
/** Whether the song is a title (promoted) song, "Y" or "N". */
rep_yn: string;
lyrics_yn: string;
/** Content provider code. */
cp_code: string;
/** Whether the song is available for streaming, "Y" or "N". */
stream_service_yn: string;
/** Whether the song is available for (FLAC) download, "Y" or "N". */
down_service_yn: string;
/** Whether the song is available for MP3 download, "Y" or "N". */
down_mp3_service_yn: string;
album_adlt_yn: string;
/** Disc number as a string, e.g. "1". */
album_cd_no: string;
/** Track number as a string, e.g. "1". */
album_track_no: string;
/** The album's release date, formatted as YYYYMMDD. */
album_release_dt: string;
/** The distributor (유통사) of the album. */
album_producer: string;
/** Whether the song is temporarily excluded from streaming, "Y" or "N". */
hold_back: string;
/** Duration in seconds as a string. */
duration: string;
/** Bitfield of available FLAC qualities as a string. */
flac_file_bit: string;
song_tts: string;
artist_tts: string;
mv_service_cnt: number;
dolby_yn: string;
}
142 changes: 142 additions & 0 deletions providers/Genie/mod.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import type { ReleaseOptions } from '@/harmonizer/types.ts';
import { describeProvider, makeProviderOptions } from '@/providers/test_spec.ts';
import { stubProviderLookups } from '@/providers/test_stubs.ts';
import { afterAll, describe } from '@std/testing/bdd';
import { assertSnapshot } from '@std/testing/snapshot';
import { assertArrayIncludes, assertEquals, assertExists } from 'std/assert/mod.ts';

import GenieProvider from './mod.ts';

describe('Genie provider', () => {
const genie = new GenieProvider(makeProviderOptions());
const stubs = [stubProviderLookups(genie)];

const releaseOptions: ReleaseOptions = {
withISRC: false,
withAllTrackArtists: true,
};

describeProvider(genie, {
urls: [{
description: 'album page',
url: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=85875094'),
id: { type: 'album', id: '85875094' },
isCanonical: true,
}, {
description: 'album page without www subdomain',
url: new URL('https://genie.co.kr/detail/albumInfo?axnm=85875094'),
id: { type: 'album', id: '85875094' },
}, {
description: 'album page with additional query parameters',
url: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=85875094&ref=share'),
id: { type: 'album', id: '85875094' },
}, {
description: 'mobile album page',
url: new URL('https://mw.genie.co.kr/detail/albumInfo?axnm=85875094'),
id: { type: 'album', id: '85875094' },
}, {
description: 'song page',
url: new URL('https://www.genie.co.kr/detail/songInfo?xgnm=108694326'),
id: { type: 'song', id: '108694326' },
isCanonical: true,
}, {
description: 'artist page',
url: new URL('https://www.genie.co.kr/detail/artistInfo?xxnm=80740728'),
id: { type: 'artist', id: '80740728' },
isCanonical: true,
}, {
description: 'album page with mismatched ID parameter (unsupported)',
url: new URL('https://www.genie.co.kr/detail/albumInfo?xgnm=85875094'),
id: undefined,
}, {
description: 'search page (unsupported)',
url: new URL('https://www.genie.co.kr/search/searchMain?query=test'),
id: undefined,
}],
invalidIds: ['abc', 'not-a-number', '123abc'],
releaseLookup: [{
description: 'Single/EP (싱글/EP) album',
release: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=81020130'),
options: releaseOptions,
assert: async (release, ctx) => {
await assertSnapshot(ctx, release);
assertEquals(release.media.length, 1, 'Should have one disc');
assertEquals(release.media[0].tracklist.length, 5, 'Should have 5 tracks');
assertExists(release.types, 'Should have release types');
assertEquals(release.types, ['Single', 'EP'], 'Should be classified as ambiguous Single/EP');
assertExists(release.labels, 'Should have labels');
assertEquals(release.labels.map((l) => l.name), ['WM Entertainment'], 'Should use the album planner as label');
},
}, {
description: 'Regular (정규앨범) album',
release: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=87060653'),
options: releaseOptions,
assert: async (release, ctx) => {
await assertSnapshot(ctx, release);
assertEquals(release.media.length, 1, 'Should have one disc');
assertEquals(release.media[0].tracklist.length, 9, 'Should have 9 tracks');
assertExists(release.types, 'Should have release types');
assertArrayIncludes(release.types, ['Album'], 'Should be classified as Album');
},
}, {
description: 'OST (기타앨범)',
release: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=81318832'),
options: releaseOptions,
assert: async (release, ctx) => {
await assertSnapshot(ctx, release);
assertEquals(release.media.length, 1, 'Should have one disc');
assertEquals(release.media[0].tracklist.length, 2, 'Should have 2 tracks');
assertEquals(release.types, undefined, 'Other albums (기타앨범) should not be typed');
assertExists(release.labels, 'Should have labels');
assertEquals(release.labels.map((l) => l.name), ['스튜디오S'], 'Label should be trimmed');
},
}, {
description: 'Compilation (베스트/컬렉션) album',
release: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=82590151'),
options: releaseOptions,
assert: async (release, ctx) => {
await assertSnapshot(ctx, release);
assertEquals(release.media.length, 1, 'Should have one disc');
assertEquals(release.media[0].tracklist.length, 19, 'Should have 19 tracks');
assertExists(release.types, 'Should have release types');
assertArrayIncludes(release.types, ['Compilation'], 'Should be classified as Compilation');
},
}, {
description: 'Compilation (컴필레이션) with an unavailable CD-only track',
release: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=87003645'),
options: releaseOptions,
assert: async (release, ctx) => {
await assertSnapshot(ctx, release);
assertEquals(release.media.length, 1, 'Should have one disc');
assertEquals(release.media[0].tracklist.length, 23, 'Should have 23 tracks (without the CD-only track)');
assertExists(release.types, 'Should have release types');
assertArrayIncludes(release.types, ['Compilation'], 'Should be classified as Compilation');
},
}, {
description: 'Album with two discs of different lengths',
release: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=80874223'),
options: releaseOptions,
assert: async (release, ctx) => {
await assertSnapshot(ctx, release);
assertEquals(release.media.length, 2, 'Should have two discs');
assertEquals(release.media[0].tracklist.length, 10, 'Disc 1 should have 10 tracks');
assertEquals(release.media[1].tracklist.length, 7, 'Disc 2 should have 7 tracks');
},
}, {
description: 'Album with three discs',
release: new URL('https://www.genie.co.kr/detail/albumInfo?axnm=81103448'),
options: releaseOptions,
assert: async (release, ctx) => {
await assertSnapshot(ctx, release);
assertEquals(release.media.length, 3, 'Should have three discs');
for (const [index, medium] of release.media.entries()) {
assertEquals(medium.tracklist.length, 11, `Disc ${index + 1} should have 11 tracks`);
}
},
}],
});

afterAll(() => {
stubs.forEach((s) => s.restore());
});
});
Loading
Loading