Skip to content

Commit 6699805

Browse files
Merge pull request #202 from ltctceplrm/musictracks
Add extra metadata for musicbrainz API
2 parents 810beaa + 6170e07 commit 6699805

File tree

5 files changed

+78
-9
lines changed

5 files changed

+78
-9
lines changed

bun.lockb

355 Bytes
Binary file not shown.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "obsidian-media-db-plugin",
33
"version": "0.8.0",
4-
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
4+
"description": "A plugin that can query multiple APIs for movies, series, anime, manga, books, comics, games, music and wiki articles, and import them into your vault.",
55
"main": "main.js",
66
"scripts": {
77
"dev": "bun run automation/build/esbuild.dev.config.ts",
@@ -34,6 +34,7 @@
3434
"eslint": "^9.32.0",
3535
"eslint-plugin-import": "^2.32.0",
3636
"eslint-plugin-only-warn": "^1.1.0",
37+
"iso-639-2": "^3.0.2",
3738
"obsidian": "latest",
3839
"openapi-fetch": "^0.14.0",
3940
"openapi-typescript": "^7.8.0",

src/api/apis/MusicBrainzAPI.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import type MediaDbPlugin from '../../main';
33
import type { MediaTypeModel } from '../../models/MediaTypeModel';
44
import { MusicReleaseModel } from '../../models/MusicReleaseModel';
55
import { MediaType } from '../../utils/MediaType';
6-
import { contactEmail, mediaDbVersion, pluginName } from '../../utils/Utils';
6+
import { contactEmail, extractTracksFromMedia, getLanguageName, mediaDbVersion, pluginName } from '../../utils/Utils';
77
import { APIModel } from '../APIModel';
8+
import { iso6392 } from 'iso-639-2';
89

910
// sadly no open api schema available
1011

@@ -136,19 +137,44 @@ export class MusicBrainzAPI extends APIModel {
136137
async getById(id: string): Promise<MediaTypeModel> {
137138
console.log(`MDB | api "${this.apiName}" queried by ID`);
138139

139-
const searchUrl = `https://musicbrainz.org/ws/2/release-group/${encodeURIComponent(id)}?inc=releases+artists+tags+ratings+genres&fmt=json`;
140-
const fetchData = await requestUrl({
141-
url: searchUrl,
140+
// Fetch release group
141+
const groupUrl = `https://musicbrainz.org/ws/2/release-group/${encodeURIComponent(id)}?inc=releases+artists+tags+ratings+genres&fmt=json`;
142+
const groupResponse = await requestUrl({
143+
url: groupUrl,
142144
headers: {
143145
'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`,
144146
},
145147
});
146148

147-
if (fetchData.status !== 200) {
148-
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
149+
if (groupResponse.status !== 200) {
150+
throw Error(`MDB | Received status code ${groupResponse.status} from ${this.apiName}.`);
151+
}
152+
153+
const result = (await groupResponse.json) as IdResponse;
154+
155+
// Get ID of the first release
156+
const firstRelease = result.releases?.[0];
157+
if (!firstRelease) {
158+
throw Error('MDB | No releases found in release group.');
159+
}
160+
161+
// Fetch recordings for the first release
162+
const releaseUrl = `https://musicbrainz.org/ws/2/release/${firstRelease.id}?inc=recordings+artists&fmt=json`;
163+
console.log(`MDB | Fetching release recordings from: ${releaseUrl}`);
164+
165+
const releaseResponse = await requestUrl({
166+
url: releaseUrl,
167+
headers: {
168+
'User-Agent': `${pluginName}/${mediaDbVersion} (${contactEmail})`,
169+
},
170+
});
171+
172+
if (releaseResponse.status !== 200) {
173+
throw Error(`MDB | Received status code ${releaseResponse.status} from ${this.apiName}.`);
149174
}
150175

151-
const result = (await fetchData.json) as IdResponse;
176+
const releaseData = await releaseResponse.json;
177+
const tracks = extractTracksFromMedia(releaseData.media);
152178

153179
return new MusicReleaseModel({
154180
type: 'musicRelease',
@@ -162,8 +188,11 @@ export class MusicBrainzAPI extends APIModel {
162188
image: 'https://coverartarchive.org/release-group/' + result.id + '/front-500.jpg',
163189

164190
artists: result['artist-credit'].map(a => a.name),
191+
language: releaseData['text-representation'].language ? getLanguageName(releaseData['text-representation'].language) : 'Unknown',
165192
genres: result.genres.map(g => g.name),
166193
subType: result['primary-type'],
194+
trackCount: releaseData.media[0]['track-count'] ?? 0,
195+
tracks: tracks,
167196
rating: result.rating.value * 2,
168197

169198
userData: {

src/models/MusicReleaseModel.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
import { MediaType } from '../utils/MediaType';
22
import type { ModelToData } from '../utils/Utils';
3-
import { mediaDbTag, migrateObject } from '../utils/Utils';
3+
import { mediaDbTag, migrateObject, getLanguageName } from '../utils/Utils';
44
import { MediaTypeModel } from './MediaTypeModel';
55

66
export type MusicReleaseData = ModelToData<MusicReleaseModel>;
77

88
export class MusicReleaseModel extends MediaTypeModel {
99
genres: string[];
1010
artists: string[];
11+
language: string;
1112
image: string;
1213
rating: number;
1314
releaseDate: string;
15+
trackCount: number;
16+
tracks: {
17+
number: number;
18+
title: string;
19+
duration: string;
20+
featuredArtists: string[];
21+
}[];
1422

1523
userData: {
1624
personalRating: number;
@@ -36,6 +44,9 @@ export class MusicReleaseModel extends MediaTypeModel {
3644
}
3745

3846
this.type = this.getMediaType();
47+
this.trackCount = obj.trackCount ?? 0;
48+
this.tracks = obj.tracks ?? [];
49+
this.language = obj.language ?? '';
3950
}
4051

4152
getTags(): string[] {

src/utils/Utils.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { TFile, TFolder, App } from 'obsidian';
22
import { requestUrl } from 'obsidian';
33
import type { MediaTypeModel } from '../models/MediaTypeModel';
4+
import { iso6392 } from 'iso-639-2';
45

56
export const pluginName: string = 'obsidian-media-db-plugin';
67
export const contactEmail: string = '[email protected]';
@@ -296,3 +297,30 @@ export async function obsidianFetch(input: Request): Promise<Response> {
296297
text: async () => res.text,
297298
} as Response;
298299
}
300+
export function extractTracksFromMedia(media: any[]): {
301+
number: number;
302+
title: string;
303+
duration: string;
304+
featuredArtists: string[];
305+
}[] {
306+
if (!media || media.length === 0 || !media[0].tracks) return [];
307+
308+
return media[0].tracks.map((track: any, index: number) => {
309+
const title = track.title || track.recording?.title || 'Unknown Title';
310+
const rawLength = track.length || track.recording?.length;
311+
const duration = rawLength ? new Date(rawLength).toISOString().substr(14, 5) : 'unknown';
312+
const featuredArtists = track['artist-credit']?.map((ac: { name: string }) => ac.name) ?? [];
313+
314+
return {
315+
number: index + 1,
316+
title,
317+
duration,
318+
featuredArtists,
319+
};
320+
});
321+
}
322+
export function getLanguageName(code: string): string | null {
323+
const language = iso6392.find(lang => lang.iso6392B === code || lang.iso6392T === code);
324+
325+
return language?.name ?? null;
326+
}

0 commit comments

Comments
 (0)