navidrome-mcp 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +397 -0
- package/dist/capabilities.d.ts +20 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +25 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/client/auth-manager.d.ts +27 -0
- package/dist/client/auth-manager.d.ts.map +1 -0
- package/dist/client/auth-manager.js +54 -0
- package/dist/client/auth-manager.js.map +1 -0
- package/dist/client/navidrome-client.d.ts +28 -0
- package/dist/client/navidrome-client.d.ts.map +1 -0
- package/dist/client/navidrome-client.js +84 -0
- package/dist/client/navidrome-client.js.map +1 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +132 -0
- package/dist/config.js.map +1 -0
- package/dist/constants/defaults.d.ts +29 -0
- package/dist/constants/defaults.d.ts.map +1 -0
- package/dist/constants/defaults.js +37 -0
- package/dist/constants/defaults.js.map +1 -0
- package/dist/constants/timeouts.d.ts +66 -0
- package/dist/constants/timeouts.d.ts.map +1 -0
- package/dist/constants/timeouts.js +66 -0
- package/dist/constants/timeouts.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/index.d.ts +21 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +83 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/schemas/common.d.ts +68 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +69 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/index.d.ts +21 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +24 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/pagination.d.ts +153 -0
- package/dist/schemas/pagination.d.ts.map +1 -0
- package/dist/schemas/pagination.js +93 -0
- package/dist/schemas/pagination.js.map +1 -0
- package/dist/schemas/validation.d.ts +147 -0
- package/dist/schemas/validation.d.ts.map +1 -0
- package/dist/schemas/validation.js +143 -0
- package/dist/schemas/validation.js.map +1 -0
- package/dist/tools/handlers/lastfm-handlers.d.ts +5 -0
- package/dist/tools/handlers/lastfm-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/lastfm-handlers.js +143 -0
- package/dist/tools/handlers/lastfm-handlers.js.map +1 -0
- package/dist/tools/handlers/lyrics-handlers.d.ts +5 -0
- package/dist/tools/handlers/lyrics-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/lyrics-handlers.js +51 -0
- package/dist/tools/handlers/lyrics-handlers.js.map +1 -0
- package/dist/tools/handlers/playlist-handlers.d.ts +5 -0
- package/dist/tools/handlers/playlist-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/playlist-handlers.js +316 -0
- package/dist/tools/handlers/playlist-handlers.js.map +1 -0
- package/dist/tools/handlers/queue-handlers.d.ts +5 -0
- package/dist/tools/handlers/queue-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/queue-handlers.js +135 -0
- package/dist/tools/handlers/queue-handlers.js.map +1 -0
- package/dist/tools/handlers/radio-handlers.d.ts +5 -0
- package/dist/tools/handlers/radio-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/radio-handlers.js +323 -0
- package/dist/tools/handlers/radio-handlers.js.map +1 -0
- package/dist/tools/handlers/registry.d.ts +26 -0
- package/dist/tools/handlers/registry.d.ts.map +1 -0
- package/dist/tools/handlers/registry.js +77 -0
- package/dist/tools/handlers/registry.js.map +1 -0
- package/dist/tools/handlers/search-handlers.d.ts +5 -0
- package/dist/tools/handlers/search-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/search-handlers.js +125 -0
- package/dist/tools/handlers/search-handlers.js.map +1 -0
- package/dist/tools/handlers/tag-handlers.d.ts +5 -0
- package/dist/tools/handlers/tag-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/tag-handlers.js +155 -0
- package/dist/tools/handlers/tag-handlers.js.map +1 -0
- package/dist/tools/handlers/user-preferences-handlers.d.ts +5 -0
- package/dist/tools/handlers/user-preferences-handlers.d.ts.map +1 -0
- package/dist/tools/handlers/user-preferences-handlers.js +155 -0
- package/dist/tools/handlers/user-preferences-handlers.js.map +1 -0
- package/dist/tools/index.d.ts +19 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +20 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/lastfm-discovery.d.ts +107 -0
- package/dist/tools/lastfm-discovery.d.ts.map +1 -0
- package/dist/tools/lastfm-discovery.js +238 -0
- package/dist/tools/lastfm-discovery.js.map +1 -0
- package/dist/tools/library.d.ts +31 -0
- package/dist/tools/library.d.ts.map +1 -0
- package/dist/tools/library.js +272 -0
- package/dist/tools/library.js.map +1 -0
- package/dist/tools/listening-history.d.ts +51 -0
- package/dist/tools/listening-history.d.ts.map +1 -0
- package/dist/tools/listening-history.js +126 -0
- package/dist/tools/listening-history.js.map +1 -0
- package/dist/tools/lyrics.d.ts +35 -0
- package/dist/tools/lyrics.d.ts.map +1 -0
- package/dist/tools/lyrics.js +222 -0
- package/dist/tools/lyrics.js.map +1 -0
- package/dist/tools/media-library.d.ts +46 -0
- package/dist/tools/media-library.d.ts.map +1 -0
- package/dist/tools/media-library.js +179 -0
- package/dist/tools/media-library.js.map +1 -0
- package/dist/tools/playlist-management.d.ts +80 -0
- package/dist/tools/playlist-management.d.ts.map +1 -0
- package/dist/tools/playlist-management.js +384 -0
- package/dist/tools/playlist-management.js.map +1 -0
- package/dist/tools/queue-management.d.ts +48 -0
- package/dist/tools/queue-management.d.ts.map +1 -0
- package/dist/tools/queue-management.js +83 -0
- package/dist/tools/queue-management.js.map +1 -0
- package/dist/tools/radio-discovery.d.ts +95 -0
- package/dist/tools/radio-discovery.d.ts.map +1 -0
- package/dist/tools/radio-discovery.js +352 -0
- package/dist/tools/radio-discovery.js.map +1 -0
- package/dist/tools/radio-validation.d.ts +49 -0
- package/dist/tools/radio-validation.d.ts.map +1 -0
- package/dist/tools/radio-validation.js +463 -0
- package/dist/tools/radio-validation.js.map +1 -0
- package/dist/tools/radio.d.ts +38 -0
- package/dist/tools/radio.d.ts.map +1 -0
- package/dist/tools/radio.js +315 -0
- package/dist/tools/radio.js.map +1 -0
- package/dist/tools/search.d.ts +54 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +165 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/tags.d.ts +63 -0
- package/dist/tools/tags.d.ts.map +1 -0
- package/dist/tools/tags.js +260 -0
- package/dist/tools/tags.js.map +1 -0
- package/dist/tools/test.d.ts +50 -0
- package/dist/tools/test.d.ts.map +1 -0
- package/dist/tools/test.js +121 -0
- package/dist/tools/test.js.map +1 -0
- package/dist/tools/user-preferences.d.ts +73 -0
- package/dist/tools/user-preferences.d.ts.map +1 -0
- package/dist/tools/user-preferences.js +231 -0
- package/dist/tools/user-preferences.js.map +1 -0
- package/dist/transformers/song-transformer.d.ts +122 -0
- package/dist/transformers/song-transformer.d.ts.map +1 -0
- package/dist/transformers/song-transformer.js +232 -0
- package/dist/transformers/song-transformer.js.map +1 -0
- package/dist/types/core.d.ts +147 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/core.js +19 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/index.d.ts +23 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lyrics.d.ts +58 -0
- package/dist/types/lyrics.d.ts.map +1 -0
- package/dist/types/lyrics.js +19 -0
- package/dist/types/lyrics.js.map +1 -0
- package/dist/types/playlists.d.ts +150 -0
- package/dist/types/playlists.d.ts.map +1 -0
- package/dist/types/playlists.js +19 -0
- package/dist/types/playlists.js.map +1 -0
- package/dist/types/radio.d.ts +192 -0
- package/dist/types/radio.d.ts.map +1 -0
- package/dist/types/radio.js +19 -0
- package/dist/types/radio.js.map +1 -0
- package/dist/types/tags.d.ts +72 -0
- package/dist/types/tags.d.ts.map +1 -0
- package/dist/types/tags.js +19 -0
- package/dist/types/tags.js.map +1 -0
- package/dist/utils/cache.d.ts +32 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +84 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/error-formatter.d.ts +108 -0
- package/dist/utils/error-formatter.d.ts.map +1 -0
- package/dist/utils/error-formatter.js +161 -0
- package/dist/utils/error-formatter.js.map +1 -0
- package/dist/utils/logger.d.ts +33 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +52 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/message-manager.d.ts +74 -0
- package/dist/utils/message-manager.d.ts.map +1 -0
- package/dist/utils/message-manager.js +134 -0
- package/dist/utils/message-manager.js.map +1 -0
- package/dist/utils/version.d.ts +5 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +19 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navidrome MCP Server - Listening History Tools
|
|
3
|
+
* Copyright (C) 2025
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { logger } from '../utils/logger.js';
|
|
20
|
+
import { transformSongsToDTO, transformAlbumsToDTO, transformArtistsToDTO } from '../transformers/song-transformer.js';
|
|
21
|
+
import { DEFAULT_VALUES } from '../constants/defaults.js';
|
|
22
|
+
// Helper function to parse duration from MM:SS format to seconds
|
|
23
|
+
function parseDuration(durationFormatted) {
|
|
24
|
+
const parts = durationFormatted.split(':');
|
|
25
|
+
if (parts.length === 2) {
|
|
26
|
+
const minutes = parseInt(parts[0] || '0', 10);
|
|
27
|
+
const seconds = parseInt(parts[1] || '0', 10);
|
|
28
|
+
return minutes * 60 + seconds;
|
|
29
|
+
}
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
const RecentlyPlayedSchema = z.object({
|
|
33
|
+
limit: z.number().min(1).max(500).optional().default(DEFAULT_VALUES.RECENTLY_PLAYED_LIMIT),
|
|
34
|
+
offset: z.number().min(0).optional().default(0),
|
|
35
|
+
timeRange: z.enum(['today', 'week', 'month', 'all']).optional().default('all'),
|
|
36
|
+
});
|
|
37
|
+
export async function listRecentlyPlayed(client, args) {
|
|
38
|
+
const { limit = 20, offset = 0, timeRange = 'all' } = RecentlyPlayedSchema.parse(args);
|
|
39
|
+
logger.info(`Getting recently played songs (${timeRange})`);
|
|
40
|
+
// For now, we'll get songs sorted by addedDate (when added to library) as a proxy for recently played
|
|
41
|
+
// The API doesn't appear to support playDate filtering reliably
|
|
42
|
+
const response = await client.request(`/song?_sort=addedDate&_order=DESC&_start=${offset}&_end=${offset + limit}`);
|
|
43
|
+
// Transform using proper transformer
|
|
44
|
+
const songs = transformSongsToDTO(response);
|
|
45
|
+
// Filter songs that have been played at least once, if available
|
|
46
|
+
const playedSongs = songs.filter(song => !song.playCount || song.playCount > 0);
|
|
47
|
+
const tracks = playedSongs.slice(0, limit).map((song) => {
|
|
48
|
+
const track = {
|
|
49
|
+
id: song.id,
|
|
50
|
+
title: song.title,
|
|
51
|
+
artist: song.artist,
|
|
52
|
+
album: song.album,
|
|
53
|
+
playCount: song.playCount || 0,
|
|
54
|
+
duration: parseDuration(song.durationFormatted),
|
|
55
|
+
// Note: Real lastPlayed timestamps are not available from the API
|
|
56
|
+
// Only including lastPlayed if we actually have play date data (which we don't currently)
|
|
57
|
+
};
|
|
58
|
+
// Only add lastPlayed if we have actual play date data
|
|
59
|
+
// Currently, the API doesn't provide reliable play date information
|
|
60
|
+
// so we omit this field rather than provide misleading data
|
|
61
|
+
return track;
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
timeRange,
|
|
65
|
+
count: tracks.length,
|
|
66
|
+
tracks,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const MostPlayedSchema = z.object({
|
|
70
|
+
type: z.enum(['songs', 'albums', 'artists']).optional().default('songs'),
|
|
71
|
+
limit: z.number().min(1).max(500).optional().default(DEFAULT_VALUES.MOST_PLAYED_LIMIT),
|
|
72
|
+
offset: z.number().min(0).optional().default(0),
|
|
73
|
+
minPlayCount: z.number().min(1).optional().default(1),
|
|
74
|
+
});
|
|
75
|
+
export async function listMostPlayed(client, args) {
|
|
76
|
+
const { type = 'songs', limit = 20, offset = 0, minPlayCount = 1 } = MostPlayedSchema.parse(args);
|
|
77
|
+
logger.info(`Getting most played ${type} with minPlayCount: ${minPlayCount}`);
|
|
78
|
+
const endpoint = type === 'songs' ? '/song' : type === 'albums' ? '/album' : '/artist';
|
|
79
|
+
// Fetch more items to account for filtering by minPlayCount
|
|
80
|
+
// We'll fetch 3x the requested amount to ensure we have enough after filtering
|
|
81
|
+
const fetchLimit = limit * 3;
|
|
82
|
+
const response = await client.request(`${endpoint}?_sort=playCount&_order=DESC&_start=${offset}&_end=${offset + fetchLimit}`);
|
|
83
|
+
// Transform using the appropriate transformer
|
|
84
|
+
let transformedItems;
|
|
85
|
+
if (type === 'songs') {
|
|
86
|
+
const songs = transformSongsToDTO(response);
|
|
87
|
+
transformedItems = songs.map(song => ({
|
|
88
|
+
id: song.id,
|
|
89
|
+
title: song.title,
|
|
90
|
+
artist: song.artist,
|
|
91
|
+
album: song.album,
|
|
92
|
+
playCount: song.playCount || 0
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
else if (type === 'albums') {
|
|
96
|
+
const albums = transformAlbumsToDTO(response);
|
|
97
|
+
transformedItems = albums.map(album => ({
|
|
98
|
+
id: album.id,
|
|
99
|
+
name: album.name,
|
|
100
|
+
artist: album.artist,
|
|
101
|
+
songCount: album.songCount,
|
|
102
|
+
playCount: album.playCount || 0
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const artists = transformArtistsToDTO(response);
|
|
107
|
+
transformedItems = artists.map(artist => ({
|
|
108
|
+
id: artist.id,
|
|
109
|
+
name: artist.name,
|
|
110
|
+
albumCount: artist.albumCount,
|
|
111
|
+
songCount: artist.songCount,
|
|
112
|
+
playCount: artist.playCount || 0
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
// Filter by minPlayCount and limit results
|
|
116
|
+
const filteredItems = transformedItems
|
|
117
|
+
.filter(item => (item.playCount || 0) >= minPlayCount)
|
|
118
|
+
.slice(0, limit);
|
|
119
|
+
return {
|
|
120
|
+
type,
|
|
121
|
+
minPlayCount,
|
|
122
|
+
count: filteredItems.length,
|
|
123
|
+
items: filteredItems,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=listening-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listening-history.js","sourceRoot":"","sources":["../../src/tools/listening-history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navidrome MCP Server - Lyrics Tools
|
|
3
|
+
* Copyright (C) 2025
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import type { LyricsDTO } from '../types/index.js';
|
|
20
|
+
import type { Config } from '../config.js';
|
|
21
|
+
/**
|
|
22
|
+
* Schema for getting lyrics
|
|
23
|
+
*/
|
|
24
|
+
export declare const GetLyricsArgsSchema: z.ZodObject<{
|
|
25
|
+
title: z.ZodString;
|
|
26
|
+
artist: z.ZodString;
|
|
27
|
+
album: z.ZodOptional<z.ZodString>;
|
|
28
|
+
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
29
|
+
id: z.ZodOptional<z.ZodString>;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
/**
|
|
32
|
+
* Get lyrics for a song
|
|
33
|
+
*/
|
|
34
|
+
export declare function getLyrics(config: Config, args: unknown): Promise<LyricsDTO>;
|
|
35
|
+
//# sourceMappingURL=lyrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lyrics.d.ts","sourceRoot":"","sources":["../../src/tools/lyrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;iBAM9B,CAAC;AA4JH;;GAEG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAyEjF"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navidrome MCP Server - Lyrics Tools
|
|
3
|
+
* Copyright (C) 2025
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { ErrorFormatter } from '../utils/error-formatter.js';
|
|
20
|
+
/**
|
|
21
|
+
* Schema for getting lyrics
|
|
22
|
+
*/
|
|
23
|
+
export const GetLyricsArgsSchema = z.object({
|
|
24
|
+
title: z.string(),
|
|
25
|
+
artist: z.string(),
|
|
26
|
+
album: z.string().optional(),
|
|
27
|
+
durationMs: z.number().min(0).optional(),
|
|
28
|
+
id: z.string().optional()
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Parse LRC format synced lyrics into structured format
|
|
32
|
+
*/
|
|
33
|
+
function parseSyncedLyrics(lrcText) {
|
|
34
|
+
const lines = [];
|
|
35
|
+
const lrcRegex = /\[(\d{2}):(\d{2})\.(\d{2})\](.+)/g;
|
|
36
|
+
let match;
|
|
37
|
+
while ((match = lrcRegex.exec(lrcText)) !== null) {
|
|
38
|
+
const [, minutesStr, secondsStr, centisecondsStr, textStr] = match;
|
|
39
|
+
if (!minutesStr || !secondsStr || !centisecondsStr || !textStr)
|
|
40
|
+
continue;
|
|
41
|
+
const minutes = parseInt(minutesStr, 10);
|
|
42
|
+
const seconds = parseInt(secondsStr, 10);
|
|
43
|
+
const centiseconds = parseInt(centisecondsStr, 10);
|
|
44
|
+
const timeMs = (minutes * 60 + seconds) * 1000 + centiseconds * 10;
|
|
45
|
+
const text = textStr.trim();
|
|
46
|
+
if (text) {
|
|
47
|
+
lines.push({ timeMs, text });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Try to get lyrics using exact match
|
|
54
|
+
*/
|
|
55
|
+
async function tryExactMatch(params, config) {
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL('/api/get', config.lrclibBase);
|
|
58
|
+
if (params.id) {
|
|
59
|
+
url.searchParams.set('id', params.id);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
url.searchParams.set('track_name', params.title);
|
|
63
|
+
url.searchParams.set('artist_name', params.artist);
|
|
64
|
+
if (params.album)
|
|
65
|
+
url.searchParams.set('album_name', params.album);
|
|
66
|
+
if (params.durationMs)
|
|
67
|
+
url.searchParams.set('duration', String(Math.round(params.durationMs / 1000)));
|
|
68
|
+
}
|
|
69
|
+
const response = await fetch(url.toString(), {
|
|
70
|
+
headers: {
|
|
71
|
+
'User-Agent': config.lrclibUserAgent || 'Navidrome-MCP/1.0',
|
|
72
|
+
'Accept': 'application/json'
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
if (response.status === 404) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(ErrorFormatter.httpRequest('LRCLIB API', response));
|
|
80
|
+
}
|
|
81
|
+
return await response.json();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// If exact match fails, return null to try search
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Search for lyrics and find best match
|
|
90
|
+
*/
|
|
91
|
+
async function searchLyrics(params, config) {
|
|
92
|
+
try {
|
|
93
|
+
const url = new URL('/api/search', config.lrclibBase);
|
|
94
|
+
url.searchParams.set('query', `${params.title} ${params.artist}`);
|
|
95
|
+
if (params.durationMs) {
|
|
96
|
+
url.searchParams.set('duration', String(Math.round(params.durationMs / 1000)));
|
|
97
|
+
}
|
|
98
|
+
const response = await fetch(url.toString(), {
|
|
99
|
+
headers: {
|
|
100
|
+
'User-Agent': config.lrclibUserAgent || 'Navidrome-MCP/1.0',
|
|
101
|
+
'Accept': 'application/json'
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
if (!response.ok) {
|
|
105
|
+
throw new Error(ErrorFormatter.httpRequest('LRCLIB search API', response));
|
|
106
|
+
}
|
|
107
|
+
const results = await response.json();
|
|
108
|
+
if (!results || results.length === 0) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
// Find best match
|
|
112
|
+
// 1. Prefer exact artist and title match
|
|
113
|
+
// 2. If duration provided, prefer within 3% tolerance
|
|
114
|
+
const titleLower = params.title.toLowerCase();
|
|
115
|
+
const artistLower = params.artist.toLowerCase();
|
|
116
|
+
const durationSec = params.durationMs ? params.durationMs / 1000 : null;
|
|
117
|
+
let bestMatch = null;
|
|
118
|
+
let bestScore = -1;
|
|
119
|
+
for (const result of results) {
|
|
120
|
+
let score = 0;
|
|
121
|
+
// Check title match
|
|
122
|
+
if (result.trackName?.toLowerCase() === titleLower) {
|
|
123
|
+
score += 10;
|
|
124
|
+
}
|
|
125
|
+
else if (result.trackName?.toLowerCase().includes(titleLower)) {
|
|
126
|
+
score += 5;
|
|
127
|
+
}
|
|
128
|
+
// Check artist match
|
|
129
|
+
if (result.artistName?.toLowerCase() === artistLower) {
|
|
130
|
+
score += 10;
|
|
131
|
+
}
|
|
132
|
+
else if (result.artistName?.toLowerCase().includes(artistLower)) {
|
|
133
|
+
score += 5;
|
|
134
|
+
}
|
|
135
|
+
// Check duration match (within 3% tolerance)
|
|
136
|
+
if (durationSec && result.duration) {
|
|
137
|
+
const tolerance = durationSec * 0.03;
|
|
138
|
+
const diff = Math.abs(result.duration - durationSec);
|
|
139
|
+
if (diff <= tolerance) {
|
|
140
|
+
score += 5;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (score > bestScore) {
|
|
144
|
+
bestScore = score;
|
|
145
|
+
bestMatch = result;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return bestMatch;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get lyrics for a song
|
|
156
|
+
*/
|
|
157
|
+
export async function getLyrics(config, args) {
|
|
158
|
+
const params = GetLyricsArgsSchema.parse(args);
|
|
159
|
+
try {
|
|
160
|
+
// Try exact match first
|
|
161
|
+
let lyricsData = await tryExactMatch(params, config);
|
|
162
|
+
// If no exact match, try searching
|
|
163
|
+
if (!lyricsData) {
|
|
164
|
+
lyricsData = await searchLyrics(params, config);
|
|
165
|
+
}
|
|
166
|
+
// If still no match, return empty lyrics
|
|
167
|
+
if (!lyricsData) {
|
|
168
|
+
const result = {
|
|
169
|
+
track: {
|
|
170
|
+
title: params.title,
|
|
171
|
+
artist: params.artist,
|
|
172
|
+
...(params.album ? { album: params.album } : {}),
|
|
173
|
+
...(params.durationMs ? { durationMs: params.durationMs } : {})
|
|
174
|
+
},
|
|
175
|
+
isInstrumental: false,
|
|
176
|
+
provider: 'lrclib',
|
|
177
|
+
attribution: {
|
|
178
|
+
url: 'https://lrclib.net',
|
|
179
|
+
license: 'community-sourced'
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
// Parse synced lyrics if available
|
|
185
|
+
let syncedLines;
|
|
186
|
+
if (lyricsData.syncedLyrics) {
|
|
187
|
+
syncedLines = parseSyncedLyrics(lyricsData.syncedLyrics);
|
|
188
|
+
}
|
|
189
|
+
const result = {
|
|
190
|
+
track: {
|
|
191
|
+
title: lyricsData.trackName || params.title,
|
|
192
|
+
artist: lyricsData.artistName || params.artist
|
|
193
|
+
},
|
|
194
|
+
isInstrumental: Boolean(lyricsData.instrumental),
|
|
195
|
+
provider: 'lrclib',
|
|
196
|
+
attribution: {
|
|
197
|
+
url: 'https://lrclib.net',
|
|
198
|
+
license: 'community-sourced'
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
// Add optional fields only if they have values
|
|
202
|
+
const album = lyricsData.albumName || params.album;
|
|
203
|
+
if (album) {
|
|
204
|
+
result.track.album = album;
|
|
205
|
+
}
|
|
206
|
+
const durationMs = lyricsData.duration ? lyricsData.duration * 1000 : params.durationMs;
|
|
207
|
+
if (durationMs) {
|
|
208
|
+
result.track.durationMs = durationMs;
|
|
209
|
+
}
|
|
210
|
+
if (syncedLines && syncedLines.length > 0) {
|
|
211
|
+
result.synced = syncedLines;
|
|
212
|
+
}
|
|
213
|
+
if (lyricsData.plainLyrics) {
|
|
214
|
+
result.unsynced = lyricsData.plainLyrics;
|
|
215
|
+
}
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
throw new Error(ErrorFormatter.toolExecution('getLyrics', error));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=lyrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lyrics.js","sourceRoot":"","sources":["../../src/tools/lyrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACxC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC1B,CAAC,CAAC;AAgBH;;GAEG;AACH,SAAS,iBAAiB,CAAC,OAAe;IACxC,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,mCAAmC,CAAC;IAErD,IAAI,KAAK,CAAC;IACV,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACjD,MAAM,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;QACnE,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,CAAC,eAAe,IAAI,CAAC,OAAO;YAAE,SAAS;QAEzE,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,QAAQ,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,CAAC,OAAO,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,YAAY,GAAG,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAE5B,IAAI,IAAI,EAAE,CAAC;YACT,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,aAAa,CAAC,MAA2C,EAAE,MAAc;IACtF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QAEnD,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YACjD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;YACnD,IAAI,MAAM,CAAC,KAAK;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,IAAI,MAAM,CAAC,UAAU;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACxG,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAC3C,OAAO,EAAE;gBACP,YAAY,EAAE,MAAM,CAAC,eAAe,IAAI,mBAAmB;gBAC3D,QAAQ,EAAE,kBAAkB;aAC7B;SACF,CAAC,CAAC;QAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC;QACtE,CAAC;QAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAoB,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,kDAAkD;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,YAAY,CAAC,MAA2C,EAAE,MAAc;IACrF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QACtD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAClE,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACjF,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAC3C,OAAO,EAAE;gBACP,YAAY,EAAE,MAAM,CAAC,eAAe,IAAI,mBAAmB;gBAC3D,QAAQ,EAAE,kBAAkB;aAC7B;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAsB,CAAC;QAE1D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,kBAAkB;QAClB,yCAAyC;QACzC,sDAAsD;QACtD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAChD,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAExE,IAAI,SAAS,GAA0B,IAAI,CAAC;QAC5C,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;QAEnB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,KAAK,GAAG,CAAC,CAAC;YAEd,oBAAoB;YACpB,IAAI,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,CAAC;gBACnD,KAAK,IAAI,EAAE,CAAC;YACd,CAAC;iBAAM,IAAI,MAAM,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAChE,KAAK,IAAI,CAAC,CAAC;YACb,CAAC;YAED,qBAAqB;YACrB,IAAI,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC;gBACrD,KAAK,IAAI,EAAE,CAAC;YACd,CAAC;iBAAM,IAAI,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBAClE,KAAK,IAAI,CAAC,CAAC;YACb,CAAC;YAED,6CAA6C;YAC7C,IAAI,WAAW,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACnC,MAAM,SAAS,GAAG,WAAW,GAAG,IAAI,CAAC;gBACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAC;gBACrD,IAAI,IAAI,IAAI,SAAS,EAAE,CAAC;oBACtB,KAAK,IAAI,CAAC,CAAC;gBACb,CAAC;YACH,CAAC;YAED,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;gBACtB,SAAS,GAAG,KAAK,CAAC;gBAClB,SAAS,GAAG,MAAM,CAAC;YACrB,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,IAAa;IAC3D,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE/C,IAAI,CAAC;QACH,wBAAwB;QACxB,IAAI,UAAU,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAErD,mCAAmC;QACnC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,UAAU,GAAG,MAAM,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC;QAED,yCAAyC;QACzC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,MAAM,GAAc;gBACxB,KAAK,EAAE;oBACL,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAChD,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAChE;gBACD,cAAc,EAAE,KAAK;gBACrB,QAAQ,EAAE,QAAQ;gBAClB,WAAW,EAAE;oBACX,GAAG,EAAE,oBAAoB;oBACzB,OAAO,EAAE,mBAAmB;iBAC7B;aACF,CAAC;YACF,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,mCAAmC;QACnC,IAAI,WAAqC,CAAC;QAC1C,IAAI,UAAU,CAAC,YAAY,EAAE,CAAC;YAC5B,WAAW,GAAG,iBAAiB,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,MAAM,GAAc;YACxB,KAAK,EAAE;gBACL,KAAK,EAAE,UAAU,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK;gBAC3C,MAAM,EAAE,UAAU,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM;aAC/C;YACD,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC;YAChD,QAAQ,EAAE,QAAQ;YAClB,WAAW,EAAE;gBACX,GAAG,EAAE,oBAAoB;gBACzB,OAAO,EAAE,mBAAmB;aAC7B;SACF,CAAC;QAEF,+CAA+C;QAC/C,MAAM,KAAK,GAAG,UAAU,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC;QACnD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QAC7B,CAAC;QAED,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACxF,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC;QACvC,CAAC;QAED,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1C,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC;QAC9B,CAAC;QAED,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;YAC3B,MAAM,CAAC,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC;QAC3C,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;IACpE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navidrome MCP Server - Media Library Tools
|
|
3
|
+
* Copyright (C) 2025
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import type { NavidromeClient } from '../client/navidrome-client.js';
|
|
19
|
+
import type { Config } from '../config.js';
|
|
20
|
+
import type { SongDTO, AlbumDTO, ArtistDTO, GenreDTO, PlaylistDTO } from '../types/index.js';
|
|
21
|
+
export declare function listAlbums(client: NavidromeClient, args: unknown): Promise<{
|
|
22
|
+
albums: AlbumDTO[];
|
|
23
|
+
total: number;
|
|
24
|
+
offset: number;
|
|
25
|
+
limit: number;
|
|
26
|
+
}>;
|
|
27
|
+
export declare function listArtists(client: NavidromeClient, args: unknown): Promise<{
|
|
28
|
+
artists: ArtistDTO[];
|
|
29
|
+
total: number;
|
|
30
|
+
offset: number;
|
|
31
|
+
limit: number;
|
|
32
|
+
}>;
|
|
33
|
+
export declare function listGenres(_client: NavidromeClient, config: Config, args: unknown): Promise<{
|
|
34
|
+
genres: GenreDTO[];
|
|
35
|
+
total: number;
|
|
36
|
+
offset: number;
|
|
37
|
+
limit: number;
|
|
38
|
+
}>;
|
|
39
|
+
export declare function getSong(client: NavidromeClient, args: unknown): Promise<SongDTO>;
|
|
40
|
+
export declare function getAlbum(client: NavidromeClient, args: unknown): Promise<AlbumDTO>;
|
|
41
|
+
export declare function getArtist(client: NavidromeClient, args: unknown): Promise<ArtistDTO>;
|
|
42
|
+
export declare function getSongPlaylists(client: NavidromeClient, args: unknown): Promise<{
|
|
43
|
+
playlists: PlaylistDTO[];
|
|
44
|
+
songId: string;
|
|
45
|
+
}>;
|
|
46
|
+
//# sourceMappingURL=media-library.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-library.d.ts","sourceRoot":"","sources":["../../src/tools/media-library.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAa3C,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAU7F,wBAAsB,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IAChF,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAyBD;AAGD,wBAAsB,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACjF,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAyBD;AAoBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACjG,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAoDD;AAGD,wBAAsB,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAWtF;AAGD,wBAAsB,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAWxF;AAGD,wBAAsB,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAW1F;AAGD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACtF,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CA6BD"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navidrome MCP Server - Media Library Tools
|
|
3
|
+
* Copyright (C) 2025
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import crypto from 'crypto';
|
|
19
|
+
import { logger } from '../utils/logger.js';
|
|
20
|
+
import { transformAlbumsToDTO, transformArtistsToDTO, transformPlaylistsToDTO, transformToSongDTO, transformToAlbumDTO, transformToArtistDTO, } from '../transformers/song-transformer.js';
|
|
21
|
+
import { AlbumPaginationSchema, ArtistPaginationSchema, GenrePaginationSchema, IdSchema, GetSongPlaylistsSchema, } from '../schemas/index.js';
|
|
22
|
+
// List Albums
|
|
23
|
+
export async function listAlbums(client, args) {
|
|
24
|
+
const params = AlbumPaginationSchema.parse(args);
|
|
25
|
+
try {
|
|
26
|
+
const queryParams = new URLSearchParams({
|
|
27
|
+
_start: params.offset.toString(),
|
|
28
|
+
_end: (params.offset + params.limit).toString(),
|
|
29
|
+
_sort: params.sort,
|
|
30
|
+
_order: params.order,
|
|
31
|
+
});
|
|
32
|
+
const rawAlbums = await client.request(`/album?${queryParams.toString()}`);
|
|
33
|
+
const albums = transformAlbumsToDTO(rawAlbums);
|
|
34
|
+
return {
|
|
35
|
+
albums,
|
|
36
|
+
total: albums.length,
|
|
37
|
+
offset: params.offset,
|
|
38
|
+
limit: params.limit,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new Error(`Failed to fetch albums: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// List Artists
|
|
46
|
+
export async function listArtists(client, args) {
|
|
47
|
+
const params = ArtistPaginationSchema.parse(args);
|
|
48
|
+
try {
|
|
49
|
+
const queryParams = new URLSearchParams({
|
|
50
|
+
_start: params.offset.toString(),
|
|
51
|
+
_end: (params.offset + params.limit).toString(),
|
|
52
|
+
_sort: params.sort,
|
|
53
|
+
_order: params.order,
|
|
54
|
+
});
|
|
55
|
+
const rawArtists = await client.request(`/artist?${queryParams.toString()}`);
|
|
56
|
+
const artists = transformArtistsToDTO(rawArtists);
|
|
57
|
+
return {
|
|
58
|
+
artists,
|
|
59
|
+
total: artists.length,
|
|
60
|
+
offset: params.offset,
|
|
61
|
+
limit: params.limit,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
throw new Error(`Failed to fetch artists: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create Subsonic API authentication parameters for genres
|
|
70
|
+
*/
|
|
71
|
+
function createSubsonicAuthForGenres(config) {
|
|
72
|
+
const salt = crypto.randomBytes(16).toString('hex');
|
|
73
|
+
const token = crypto.createHash('md5').update(config.navidromePassword + salt).digest('hex');
|
|
74
|
+
return new URLSearchParams({
|
|
75
|
+
u: config.navidromeUsername,
|
|
76
|
+
t: token,
|
|
77
|
+
s: salt,
|
|
78
|
+
v: '1.16.1',
|
|
79
|
+
c: 'NavidromeMCP',
|
|
80
|
+
f: 'json',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// List Genres
|
|
84
|
+
export async function listGenres(_client, config, args) {
|
|
85
|
+
const params = GenrePaginationSchema.parse(args);
|
|
86
|
+
try {
|
|
87
|
+
// Use direct fetch to Subsonic API (not through our client since it adds /api prefix)
|
|
88
|
+
const authParams = createSubsonicAuthForGenres(config);
|
|
89
|
+
const subsonicUrl = `${config.navidromeUrl}/rest/getGenres?${authParams.toString()}`;
|
|
90
|
+
const subsonicResponse = await fetch(subsonicUrl);
|
|
91
|
+
if (!subsonicResponse.ok) {
|
|
92
|
+
throw new Error(`Subsonic API request failed: ${subsonicResponse.status} ${subsonicResponse.statusText}`);
|
|
93
|
+
}
|
|
94
|
+
const response = await subsonicResponse.json();
|
|
95
|
+
// Extract genres from Subsonic response structure
|
|
96
|
+
const subsonicGenres = response?.['subsonic-response']?.genres?.genre || [];
|
|
97
|
+
// Transform Subsonic genre format to our DTO
|
|
98
|
+
const allGenres = subsonicGenres.map((genre) => ({
|
|
99
|
+
id: genre.value || '', // Subsonic uses 'value' for genre name as ID
|
|
100
|
+
name: genre.value || '',
|
|
101
|
+
songCount: genre.songCount || 0,
|
|
102
|
+
albumCount: genre.albumCount || 0,
|
|
103
|
+
}));
|
|
104
|
+
// Apply pagination manually since Subsonic getGenres doesn't support it
|
|
105
|
+
const startIndex = params.offset;
|
|
106
|
+
const endIndex = Math.min(startIndex + params.limit, allGenres.length);
|
|
107
|
+
const paginatedGenres = allGenres.slice(startIndex, endIndex);
|
|
108
|
+
return {
|
|
109
|
+
genres: paginatedGenres,
|
|
110
|
+
total: allGenres.length,
|
|
111
|
+
offset: params.offset,
|
|
112
|
+
limit: params.limit,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
throw new Error(`Failed to fetch genres: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Get Song by ID
|
|
120
|
+
export async function getSong(client, args) {
|
|
121
|
+
const params = IdSchema.parse(args);
|
|
122
|
+
try {
|
|
123
|
+
const rawSong = await client.request(`/song/${params.id}`);
|
|
124
|
+
return transformToSongDTO(rawSong);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new Error(`Failed to fetch song: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Get Album by ID
|
|
131
|
+
export async function getAlbum(client, args) {
|
|
132
|
+
const params = IdSchema.parse(args);
|
|
133
|
+
try {
|
|
134
|
+
const rawAlbum = await client.request(`/album/${params.id}`);
|
|
135
|
+
return transformToAlbumDTO(rawAlbum);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new Error(`Failed to fetch album: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Get Artist by ID
|
|
142
|
+
export async function getArtist(client, args) {
|
|
143
|
+
const params = IdSchema.parse(args);
|
|
144
|
+
try {
|
|
145
|
+
const rawArtist = await client.request(`/artist/${params.id}`);
|
|
146
|
+
return transformToArtistDTO(rawArtist);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
throw new Error(`Failed to fetch artist: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Get Playlists containing a song
|
|
153
|
+
export async function getSongPlaylists(client, args) {
|
|
154
|
+
const params = GetSongPlaylistsSchema.parse(args);
|
|
155
|
+
try {
|
|
156
|
+
const rawPlaylists = await client.request(`/song/${params.songId}/playlists`);
|
|
157
|
+
// Workaround: This specific endpoint returns JSON data but with text/plain content-type
|
|
158
|
+
// So we need to parse it manually if it's a string
|
|
159
|
+
let playlistData = rawPlaylists;
|
|
160
|
+
if (typeof rawPlaylists === 'string') {
|
|
161
|
+
try {
|
|
162
|
+
playlistData = JSON.parse(rawPlaylists);
|
|
163
|
+
}
|
|
164
|
+
catch (parseError) {
|
|
165
|
+
logger.error('Failed to parse playlist data:', parseError);
|
|
166
|
+
playlistData = [];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const playlists = transformPlaylistsToDTO(playlistData);
|
|
170
|
+
return {
|
|
171
|
+
playlists,
|
|
172
|
+
songId: params.songId,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
throw new Error(`Failed to fetch song playlists: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=media-library.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-library.js","sourceRoot":"","sources":["../../src/tools/media-library.ts"],"names":[],"mappings":"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"}
|