From 75166ec77c28c4bd91ce53cbdb97d8881590acff Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Sun, 31 Aug 2025 11:34:23 +0200 Subject: [PATCH] clean up merged PRs --- src/api/apis/MobyGamesAPI.ts | 2 +- src/api/apis/MusicBrainzAPI.ts | 90 +++++++++++++++++++++++++-------- src/main.ts | 64 +---------------------- src/models/MusicReleaseModel.ts | 2 +- src/utils/Utils.ts | 23 +-------- 5 files changed, 73 insertions(+), 108 deletions(-) diff --git a/src/api/apis/MobyGamesAPI.ts b/src/api/apis/MobyGamesAPI.ts index 1b8b55d..a88eec9 100644 --- a/src/api/apis/MobyGamesAPI.ts +++ b/src/api/apis/MobyGamesAPI.ts @@ -61,7 +61,7 @@ export class MobyGamesAPI extends APIModel { year: new Date(result.platforms[0].first_release_date).getFullYear().toString(), dataSource: this.apiName, id: result.game_id, - } as GameModel), + }), ); } diff --git a/src/api/apis/MusicBrainzAPI.ts b/src/api/apis/MusicBrainzAPI.ts index 5bebd6d..734b3b6 100644 --- a/src/api/apis/MusicBrainzAPI.ts +++ b/src/api/apis/MusicBrainzAPI.ts @@ -3,9 +3,8 @@ import type MediaDbPlugin from '../../main'; import type { MediaTypeModel } from '../../models/MediaTypeModel'; import { MusicReleaseModel } from '../../models/MusicReleaseModel'; import { MediaType } from '../../utils/MediaType'; -import { contactEmail, extractTracksFromMedia, getLanguageName, mediaDbVersion, pluginName } from '../../utils/Utils'; +import { contactEmail, getLanguageName, mediaDbVersion, pluginName } from '../../utils/Utils'; import { APIModel } from '../APIModel'; -import { iso6392 } from 'iso-639-2'; // sadly no open api schema available @@ -26,6 +25,18 @@ interface Release { status: string; } +interface ArtistCredit { + name: string; + artist: { + tags: Tag[]; + type: string; + id: string; + name: string; + 'short-name': string; + country: string; + }; +} + interface SearchResponse { id: string; 'type-id': string; @@ -36,14 +47,7 @@ interface SearchResponse { title: string; 'first-release-date': string; 'primary-type': string; - 'artist-credit': { - name: string; - artist: { - id: string; - name: string; - 'short-name': string; - }; - }[]; + 'artist-credit': ArtistCredit[]; releases: Release[]; tags: Tag[]; } @@ -52,17 +56,7 @@ interface IdResponse { id: string; tags: Tag[]; 'primary-type-id': string; - 'artist-credit': { - name: string; - artist: { - tags: Tag[]; - type: string; - id: string; - name: string; - 'short-name': string; - country: string; - }; - }[]; + 'artist-credit': ArtistCredit[]; title: string; genres: Genre[]; 'first-release-date': string; @@ -74,6 +68,27 @@ interface IdResponse { }; } +interface MediaResponse { + media: { + 'track-count': number; + tracks: { + 'artist-credit': ArtistCredit[]; + length: number | null; + number: string; + position: number; + title: string; + recording: { + length: number; + title: string; + }; + }[]; + }[]; + 'text-representation': { + language: string; + script: string; + }; +} + export class MusicBrainzAPI extends APIModel { plugin: MediaDbPlugin; apiDateFormat: string = 'YYYY-MM-DD'; @@ -173,9 +188,11 @@ export class MusicBrainzAPI extends APIModel { throw Error(`MDB | Received status code ${releaseResponse.status} from ${this.apiName}.`); } - const releaseData = await releaseResponse.json; + const releaseData = (await releaseResponse.json) as MediaResponse; const tracks = extractTracksFromMedia(releaseData.media); + console.log(releaseData); + return new MusicReleaseModel({ type: 'musicRelease', title: result.title, @@ -204,3 +221,32 @@ export class MusicBrainzAPI extends APIModel { return this.plugin.settings.MusicBrainzAPI_disabledMediaTypes; } } + +function extractTracksFromMedia(media: MediaResponse['media']): { + number: number; + title: string; + duration: string; + featuredArtists: string[]; +}[] { + if (!media || media.length === 0 || !media[0].tracks) return []; + + return media[0].tracks.map((track, index) => { + const title = track.title ?? track.recording?.title ?? 'Unknown Title'; + const rawLength = track.length ?? track.recording?.length; + const duration = rawLength ? millisecondsToMinutes(rawLength) : 'unknown'; + const featuredArtists = track['artist-credit']?.map(ac => ac.name) ?? []; + + return { + number: index + 1, + title, + duration, + featuredArtists, + }; + }); +} + +function millisecondsToMinutes(milliseconds: number): string { + const minutes = Math.floor(milliseconds / 60000); + const seconds = Math.floor((milliseconds % 60000) / 1000); + return `${minutes}:${seconds.toString().padStart(2, '0')}`; +} diff --git a/src/main.ts b/src/main.ts index 4d18e59..b4aac01 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,5 @@ import { MarkdownView, Notice, parseYaml, Plugin, stringifyYaml, TFile, TFolder } from 'obsidian'; -import { requestUrl, normalizePath } from 'obsidian'; // Add requestUrl import +import { requestUrl, normalizePath } from 'obsidian'; import type { MediaType } from 'src/utils/MediaType'; import { APIManager } from './api/APIManager'; import { BoardGameGeekAPI } from './api/apis/BoardGameGeekAPI'; @@ -59,7 +59,6 @@ export default class MediaDbPlugin extends Plugin { this.apiManager.registerAPI(new ComicVineAPI(this)); this.apiManager.registerAPI(new MobyGamesAPI(this)); this.apiManager.registerAPI(new GiantBombAPI(this)); - // this.apiManager.registerAPI(new LocGovAPI(this)); // TODO: parse data this.mediaTypeManager = new MediaTypeManager(); this.modelPropertyMapper = new PropertyMapper(this); @@ -157,12 +156,6 @@ export default class MediaDbPlugin extends Plugin { }); } - /** - * first very simple approach - * TODO: - * - replace the detail query - * - maybe custom link syntax - */ async createLinkWithSearchModal(): Promise { const apiSearchResults = await this.modalHelper.openAdvancedSearchModal({}, async advancedSearchModalData => { return await this.apiManager.query(advancedSearchModalData.query, advancedSearchModalData.apis); @@ -381,18 +374,7 @@ export default class MediaDbPlugin extends Plugin { * @param options */ async generateMediaDbNoteContents(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions): Promise { - const template = await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app); - - return this.generateContentWithDefaultFrontMatter(mediaTypeModel, options, template); - - // if (this.settings.useDefaultFrontMatter || !template) { - // return this.generateContentWithDefaultFrontMatter(mediaTypeModel, options, template); - // } else { - // return this.generateContentWithCustomFrontMatter(mediaTypeModel, options, template); - // } - } - - async generateContentWithDefaultFrontMatter(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions, template?: string): Promise { + let template = await this.mediaTypeManager.getTemplate(mediaTypeModel, this.app); let fileMetadata: Record; if (this.settings.useDefaultFrontMatter) { @@ -421,48 +403,6 @@ export default class MediaDbPlugin extends Plugin { return fileContent; } - async generateContentWithCustomFrontMatter(mediaTypeModel: MediaTypeModel, options: CreateNoteOptions, template: string): Promise { - const regExp = new RegExp(this.frontMatterRexExpPattern); - - const frontMatter = this.getMetaDataFromFileContent(template); - let fileContent: string = template.replace(regExp, ''); - - // Updating a previous file - if (options.attachFile) { - const previousMetadata = this.app.metadataCache.getFileCache(options.attachFile)?.frontmatter ?? {}; - - // Use contents (below front matter) from previous file - fileContent = await this.app.vault.read(options.attachFile); - - fileContent = fileContent.replace(regExp, ''); - fileContent = fileContent.startsWith('\n') ? fileContent.substring(1) : fileContent; - - // Update updated front matter with entries from the old front matter, if it isn't defined in the new front matter - Object.keys(previousMetadata).forEach(key => { - const value: unknown = previousMetadata[key]; - - if (!frontMatter[key] && value) { - frontMatter[key] = value; - } - }); - } - - // Ensure that id, type, and dataSource are defined - frontMatter.id ??= mediaTypeModel.id; - frontMatter.type ??= mediaTypeModel.type; - frontMatter.dataSource ??= mediaTypeModel.dataSource; - - if (this.settings.enableTemplaterIntegration && hasTemplaterPlugin(this.app)) { - // Only support stringifyYaml for templater plugin - // Include the media variable in all templater commands by using a top level JavaScript execution command. - fileContent = `---\n<%* const media = ${JSON.stringify(mediaTypeModel)} %>\n${stringifyYaml(frontMatter)}---\n${fileContent}`; - } else { - fileContent = `---\n${stringifyYaml(frontMatter)}---\n${fileContent}`; - } - - return fileContent; - } - async attachFile(fileMetadata: Metadata, fileContent: string, fileToAttach?: TFile): Promise<{ fileMetadata: Metadata; fileContent: string }> { if (!fileToAttach) { return { fileMetadata: fileMetadata, fileContent: fileContent }; diff --git a/src/models/MusicReleaseModel.ts b/src/models/MusicReleaseModel.ts index a358089..971b30a 100644 --- a/src/models/MusicReleaseModel.ts +++ b/src/models/MusicReleaseModel.ts @@ -1,6 +1,6 @@ import { MediaType } from '../utils/MediaType'; import type { ModelToData } from '../utils/Utils'; -import { mediaDbTag, migrateObject, getLanguageName } from '../utils/Utils'; +import { mediaDbTag, migrateObject } from '../utils/Utils'; import { MediaTypeModel } from './MediaTypeModel'; export type MusicReleaseData = ModelToData; diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 10e6746..9b25b34 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -1,7 +1,7 @@ +import { iso6392 } from 'iso-639-2'; import type { TFile, TFolder, App } from 'obsidian'; import { requestUrl } from 'obsidian'; import type { MediaTypeModel } from '../models/MediaTypeModel'; -import { iso6392 } from 'iso-639-2'; export const pluginName: string = 'obsidian-media-db-plugin'; export const contactEmail: string = 'm.projects.code@gmail.com'; @@ -297,28 +297,7 @@ export async function obsidianFetch(input: Request): Promise { text: async () => res.text, } as Response; } -export function extractTracksFromMedia(media: any[]): { - number: number; - title: string; - duration: string; - featuredArtists: string[]; -}[] { - if (!media || media.length === 0 || !media[0].tracks) return []; - return media[0].tracks.map((track: any, index: number) => { - const title = track.title || track.recording?.title || 'Unknown Title'; - const rawLength = track.length || track.recording?.length; - const duration = rawLength ? new Date(rawLength).toISOString().substr(14, 5) : 'unknown'; - const featuredArtists = track['artist-credit']?.map((ac: { name: string }) => ac.name) ?? []; - - return { - number: index + 1, - title, - duration, - featuredArtists, - }; - }); -} export function getLanguageName(code: string): string | null { const language = iso6392.find(lang => lang.iso6392B === code || lang.iso6392T === code);