@thelord/mcp-arr 1.0.0 → 1.2.0
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/README.md +70 -15
- package/dist/arr-client.d.ts +106 -0
- package/dist/arr-client.d.ts.map +1 -1
- package/dist/arr-client.js +164 -0
- package/dist/arr-client.js.map +1 -1
- package/dist/bazarr-client.d.ts +212 -0
- package/dist/bazarr-client.d.ts.map +1 -0
- package/dist/bazarr-client.js +155 -0
- package/dist/bazarr-client.js.map +1 -0
- package/dist/bazarr-handlers.d.ts +8 -0
- package/dist/bazarr-handlers.d.ts.map +1 -0
- package/dist/bazarr-handlers.js +355 -0
- package/dist/bazarr-handlers.js.map +1 -0
- package/dist/bazarr-tools.d.ts +8 -0
- package/dist/bazarr-tools.d.ts.map +1 -0
- package/dist/bazarr-tools.js +359 -0
- package/dist/bazarr-tools.js.map +1 -0
- package/dist/index.js +662 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -20,13 +20,17 @@ import { trashClient } from "./trash-client.js";
|
|
|
20
20
|
import { LingarrClient } from "./lingarr-client.js";
|
|
21
21
|
import { getLingarrTools } from "./lingarr-tools.js";
|
|
22
22
|
import { handleLingarrTool, getLingarrStatus } from "./lingarr-handlers.js";
|
|
23
|
+
import { BazarrClient } from "./bazarr-client.js";
|
|
24
|
+
import { bazarrTools } from "./bazarr-tools.js";
|
|
25
|
+
import { handleBazarrTool } from "./bazarr-handlers.js";
|
|
23
26
|
const services = [
|
|
24
27
|
{ name: 'sonarr', displayName: 'Sonarr (TV)', url: process.env.SONARR_URL, apiKey: process.env.SONARR_API_KEY },
|
|
25
28
|
{ name: 'radarr', displayName: 'Radarr (Movies)', url: process.env.RADARR_URL, apiKey: process.env.RADARR_API_KEY },
|
|
26
29
|
{ name: 'lidarr', displayName: 'Lidarr (Music)', url: process.env.LIDARR_URL, apiKey: process.env.LIDARR_API_KEY },
|
|
27
30
|
{ name: 'readarr', displayName: 'Readarr (Books)', url: process.env.READARR_URL, apiKey: process.env.READARR_API_KEY },
|
|
28
31
|
{ name: 'prowlarr', displayName: 'Prowlarr (Indexers)', url: process.env.PROWLARR_URL, apiKey: process.env.PROWLARR_API_KEY },
|
|
29
|
-
{ name: 'lingarr', displayName: 'Lingarr (
|
|
32
|
+
{ name: 'lingarr', displayName: 'Lingarr (Subtitle Translation)', url: process.env.LINGARR_URL, apiKey: process.env.LINGARR_API_KEY },
|
|
33
|
+
{ name: 'bazarr', displayName: 'Bazarr (Subtitles)', url: process.env.BAZARR_URL, apiKey: process.env.BAZARR_API_KEY },
|
|
30
34
|
];
|
|
31
35
|
// Check which services are configured
|
|
32
36
|
const configuredServices = services.filter(s => s.url && s.apiKey);
|
|
@@ -58,6 +62,9 @@ for (const service of configuredServices) {
|
|
|
58
62
|
case 'lingarr':
|
|
59
63
|
clients.lingarr = new LingarrClient(config);
|
|
60
64
|
break;
|
|
65
|
+
case 'bazarr':
|
|
66
|
+
clients.bazarr = new BazarrClient(config);
|
|
67
|
+
break;
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
70
|
// Build tools based on configured services
|
|
@@ -232,6 +239,339 @@ if (clients.sonarr) {
|
|
|
232
239
|
},
|
|
233
240
|
required: ["episodeIds"],
|
|
234
241
|
},
|
|
242
|
+
}, {
|
|
243
|
+
name: "sonarr_add_series",
|
|
244
|
+
description: "Add a new TV series to Sonarr",
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: "object",
|
|
247
|
+
properties: {
|
|
248
|
+
tvdbId: {
|
|
249
|
+
type: "number",
|
|
250
|
+
description: "TVDB ID for the series (get from sonarr_search)",
|
|
251
|
+
},
|
|
252
|
+
title: {
|
|
253
|
+
type: "string",
|
|
254
|
+
description: "Series title",
|
|
255
|
+
},
|
|
256
|
+
qualityProfileId: {
|
|
257
|
+
type: "number",
|
|
258
|
+
description: "Quality profile ID (get from sonarr_get_quality_profiles)",
|
|
259
|
+
},
|
|
260
|
+
rootFolderPath: {
|
|
261
|
+
type: "string",
|
|
262
|
+
description: "Root folder path (get from sonarr_get_root_folders)",
|
|
263
|
+
},
|
|
264
|
+
seasonFolder: {
|
|
265
|
+
type: "boolean",
|
|
266
|
+
description: "Use season folders (default: true)",
|
|
267
|
+
},
|
|
268
|
+
monitored: {
|
|
269
|
+
type: "boolean",
|
|
270
|
+
description: "Monitor series (default: true)",
|
|
271
|
+
},
|
|
272
|
+
tags: {
|
|
273
|
+
type: "array",
|
|
274
|
+
items: { type: "number" },
|
|
275
|
+
description: "Tag IDs to apply",
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
required: ["tvdbId", "qualityProfileId", "rootFolderPath"],
|
|
279
|
+
},
|
|
280
|
+
}, {
|
|
281
|
+
name: "sonarr_update_series",
|
|
282
|
+
description: "Update an existing series configuration (monitored, tags, quality profile, path, etc.)",
|
|
283
|
+
inputSchema: {
|
|
284
|
+
type: "object",
|
|
285
|
+
properties: {
|
|
286
|
+
seriesId: {
|
|
287
|
+
type: "number",
|
|
288
|
+
description: "Series ID to update",
|
|
289
|
+
},
|
|
290
|
+
monitored: {
|
|
291
|
+
type: "boolean",
|
|
292
|
+
description: "Monitor series",
|
|
293
|
+
},
|
|
294
|
+
qualityProfileId: {
|
|
295
|
+
type: "number",
|
|
296
|
+
description: "Quality profile ID",
|
|
297
|
+
},
|
|
298
|
+
tags: {
|
|
299
|
+
type: "array",
|
|
300
|
+
items: { type: "number" },
|
|
301
|
+
description: "Tag IDs to apply",
|
|
302
|
+
},
|
|
303
|
+
seasonFolder: {
|
|
304
|
+
type: "boolean",
|
|
305
|
+
description: "Use season folders",
|
|
306
|
+
},
|
|
307
|
+
seriesType: {
|
|
308
|
+
type: "string",
|
|
309
|
+
description: "Series type (standard, daily, anime)",
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
required: ["seriesId"],
|
|
313
|
+
},
|
|
314
|
+
}, {
|
|
315
|
+
name: "sonarr_delete_series",
|
|
316
|
+
description: "Delete a series from Sonarr library. Optionally delete files and add to exclusion list.",
|
|
317
|
+
inputSchema: {
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: {
|
|
320
|
+
seriesId: {
|
|
321
|
+
type: "number",
|
|
322
|
+
description: "Series ID to delete",
|
|
323
|
+
},
|
|
324
|
+
deleteFiles: {
|
|
325
|
+
type: "boolean",
|
|
326
|
+
description: "Delete all files for this series (default: false)",
|
|
327
|
+
},
|
|
328
|
+
addImportListExclusion: {
|
|
329
|
+
type: "boolean",
|
|
330
|
+
description: "Add series to import list exclusion (default: false)",
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
required: ["seriesId"],
|
|
334
|
+
},
|
|
335
|
+
}, {
|
|
336
|
+
name: "sonarr_update_episode",
|
|
337
|
+
description: "Update episode settings (monitored status)",
|
|
338
|
+
inputSchema: {
|
|
339
|
+
type: "object",
|
|
340
|
+
properties: {
|
|
341
|
+
episodeId: {
|
|
342
|
+
type: "number",
|
|
343
|
+
description: "Episode ID to update",
|
|
344
|
+
},
|
|
345
|
+
monitored: {
|
|
346
|
+
type: "boolean",
|
|
347
|
+
description: "Monitor episode",
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
required: ["episodeId"],
|
|
351
|
+
},
|
|
352
|
+
}, {
|
|
353
|
+
name: "sonarr_delete_episode_file",
|
|
354
|
+
description: "Delete an episode file. Useful for removing bad quality files to force re-download.",
|
|
355
|
+
inputSchema: {
|
|
356
|
+
type: "object",
|
|
357
|
+
properties: {
|
|
358
|
+
episodeFileId: {
|
|
359
|
+
type: "number",
|
|
360
|
+
description: "Episode file ID to delete",
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
required: ["episodeFileId"],
|
|
364
|
+
},
|
|
365
|
+
}, {
|
|
366
|
+
name: "sonarr_refresh_series",
|
|
367
|
+
description: "Refresh series metadata from TheTVDB",
|
|
368
|
+
inputSchema: {
|
|
369
|
+
type: "object",
|
|
370
|
+
properties: {
|
|
371
|
+
seriesId: {
|
|
372
|
+
type: "number",
|
|
373
|
+
description: "Series ID to refresh",
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
required: ["seriesId"],
|
|
377
|
+
},
|
|
378
|
+
}, {
|
|
379
|
+
name: "sonarr_rescan_series",
|
|
380
|
+
description: "Rescan series disk for new/changed files",
|
|
381
|
+
inputSchema: {
|
|
382
|
+
type: "object",
|
|
383
|
+
properties: {
|
|
384
|
+
seriesId: {
|
|
385
|
+
type: "number",
|
|
386
|
+
description: "Series ID to rescan",
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
required: ["seriesId"],
|
|
390
|
+
},
|
|
391
|
+
}, {
|
|
392
|
+
name: "sonarr_rename_series",
|
|
393
|
+
description: "Rename series files according to Sonarr naming conventions",
|
|
394
|
+
inputSchema: {
|
|
395
|
+
type: "object",
|
|
396
|
+
properties: {
|
|
397
|
+
seriesId: {
|
|
398
|
+
type: "number",
|
|
399
|
+
description: "Series ID to rename",
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
required: ["seriesId"],
|
|
403
|
+
},
|
|
404
|
+
}, {
|
|
405
|
+
name: "sonarr_rename_episodes",
|
|
406
|
+
description: "Rename all episode files for a series",
|
|
407
|
+
inputSchema: {
|
|
408
|
+
type: "object",
|
|
409
|
+
properties: {
|
|
410
|
+
seriesId: {
|
|
411
|
+
type: "number",
|
|
412
|
+
description: "Series ID",
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
required: ["seriesId"],
|
|
416
|
+
},
|
|
417
|
+
}, {
|
|
418
|
+
name: "sonarr_get_releases",
|
|
419
|
+
description: "Get grabbed releases (pushes). Shows recent downloads and their status.",
|
|
420
|
+
inputSchema: {
|
|
421
|
+
type: "object",
|
|
422
|
+
properties: {
|
|
423
|
+
seriesId: {
|
|
424
|
+
type: "number",
|
|
425
|
+
description: "Optional: filter to specific series ID",
|
|
426
|
+
},
|
|
427
|
+
page: {
|
|
428
|
+
type: "number",
|
|
429
|
+
description: "Page number (default: 1)",
|
|
430
|
+
},
|
|
431
|
+
pageSize: {
|
|
432
|
+
type: "number",
|
|
433
|
+
description: "Items per page (default: 10)",
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
required: [],
|
|
437
|
+
},
|
|
438
|
+
}, {
|
|
439
|
+
name: "sonarr_delete_release",
|
|
440
|
+
description: "Delete a release from history. Use sonarr_get_releases to find the GUID.",
|
|
441
|
+
inputSchema: {
|
|
442
|
+
type: "object",
|
|
443
|
+
properties: {
|
|
444
|
+
guid: {
|
|
445
|
+
type: "string",
|
|
446
|
+
description: "Release GUID (from sonarr_get_releases)",
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
required: ["guid"],
|
|
450
|
+
},
|
|
451
|
+
}, {
|
|
452
|
+
name: "sonarr_create_release_profile",
|
|
453
|
+
description: "Create a release profile to control which releases are grabbed or rejected",
|
|
454
|
+
inputSchema: {
|
|
455
|
+
type: "object",
|
|
456
|
+
properties: {
|
|
457
|
+
name: {
|
|
458
|
+
type: "string",
|
|
459
|
+
description: "Profile name",
|
|
460
|
+
},
|
|
461
|
+
enabled: {
|
|
462
|
+
type: "boolean",
|
|
463
|
+
description: "Enable profile (default: true)",
|
|
464
|
+
},
|
|
465
|
+
required: {
|
|
466
|
+
type: "array",
|
|
467
|
+
items: { type: "string" },
|
|
468
|
+
description: "Required terms (releases must contain)",
|
|
469
|
+
},
|
|
470
|
+
ignored: {
|
|
471
|
+
type: "array",
|
|
472
|
+
items: { type: "string" },
|
|
473
|
+
description: "Ignored terms (releases will be rejected if they contain these)",
|
|
474
|
+
},
|
|
475
|
+
tags: {
|
|
476
|
+
type: "array",
|
|
477
|
+
items: { type: "number" },
|
|
478
|
+
description: "Tag IDs to apply this profile to",
|
|
479
|
+
},
|
|
480
|
+
indexerId: {
|
|
481
|
+
type: "number",
|
|
482
|
+
description: "Specific indexer ID to apply profile to",
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
required: ["name"],
|
|
486
|
+
},
|
|
487
|
+
}, {
|
|
488
|
+
name: "sonarr_update_release_profile",
|
|
489
|
+
description: "Update an existing release profile",
|
|
490
|
+
inputSchema: {
|
|
491
|
+
type: "object",
|
|
492
|
+
properties: {
|
|
493
|
+
profileId: {
|
|
494
|
+
type: "number",
|
|
495
|
+
description: "Release profile ID to update",
|
|
496
|
+
},
|
|
497
|
+
enabled: {
|
|
498
|
+
type: "boolean",
|
|
499
|
+
description: "Enable/disable profile",
|
|
500
|
+
},
|
|
501
|
+
required: {
|
|
502
|
+
type: "array",
|
|
503
|
+
items: { type: "string" },
|
|
504
|
+
description: "Required terms",
|
|
505
|
+
},
|
|
506
|
+
ignored: {
|
|
507
|
+
type: "array",
|
|
508
|
+
items: { type: "string" },
|
|
509
|
+
description: "Ignored terms",
|
|
510
|
+
},
|
|
511
|
+
tags: {
|
|
512
|
+
type: "array",
|
|
513
|
+
items: { type: "number" },
|
|
514
|
+
description: "Tag IDs",
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
required: ["profileId"],
|
|
518
|
+
},
|
|
519
|
+
}, {
|
|
520
|
+
name: "sonarr_delete_release_profile",
|
|
521
|
+
description: "Delete a release profile",
|
|
522
|
+
inputSchema: {
|
|
523
|
+
type: "object",
|
|
524
|
+
properties: {
|
|
525
|
+
profileId: {
|
|
526
|
+
type: "number",
|
|
527
|
+
description: "Release profile ID to delete",
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
required: ["profileId"],
|
|
531
|
+
},
|
|
532
|
+
}, {
|
|
533
|
+
name: "sonarr_create_tag",
|
|
534
|
+
description: "Create a new tag",
|
|
535
|
+
inputSchema: {
|
|
536
|
+
type: "object",
|
|
537
|
+
properties: {
|
|
538
|
+
label: {
|
|
539
|
+
type: "string",
|
|
540
|
+
description: "Tag label/name",
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
required: ["label"],
|
|
544
|
+
},
|
|
545
|
+
}, {
|
|
546
|
+
name: "sonarr_update_tag",
|
|
547
|
+
description: "Update a tag label",
|
|
548
|
+
inputSchema: {
|
|
549
|
+
type: "object",
|
|
550
|
+
properties: {
|
|
551
|
+
tagId: {
|
|
552
|
+
type: "number",
|
|
553
|
+
description: "Tag ID to update",
|
|
554
|
+
},
|
|
555
|
+
label: {
|
|
556
|
+
type: "string",
|
|
557
|
+
description: "New tag label",
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
required: ["tagId", "label"],
|
|
561
|
+
},
|
|
562
|
+
}, {
|
|
563
|
+
name: "sonarr_delete_tag",
|
|
564
|
+
description: "Delete a tag",
|
|
565
|
+
inputSchema: {
|
|
566
|
+
type: "object",
|
|
567
|
+
properties: {
|
|
568
|
+
tagId: {
|
|
569
|
+
type: "number",
|
|
570
|
+
description: "Tag ID to delete",
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
required: ["tagId"],
|
|
574
|
+
},
|
|
235
575
|
});
|
|
236
576
|
}
|
|
237
577
|
// Radarr tools
|
|
@@ -509,6 +849,10 @@ if (clients.prowlarr) {
|
|
|
509
849
|
if (clients.lingarr) {
|
|
510
850
|
TOOLS.push(...getLingarrTools());
|
|
511
851
|
}
|
|
852
|
+
// Bazarr tools - imported from separate module
|
|
853
|
+
if (clients.bazarr) {
|
|
854
|
+
TOOLS.push(...bazarrTools);
|
|
855
|
+
}
|
|
512
856
|
// Cross-service search tool
|
|
513
857
|
TOOLS.push({
|
|
514
858
|
name: "arr_search_all",
|
|
@@ -676,6 +1020,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
676
1020
|
if (lingarrResult) {
|
|
677
1021
|
return lingarrResult;
|
|
678
1022
|
}
|
|
1023
|
+
// Delegate Bazarr tools to separate handler module
|
|
1024
|
+
if (name.startsWith('bazarr_') && clients.bazarr) {
|
|
1025
|
+
const result = await handleBazarrTool(clients.bazarr, name, (args || {}));
|
|
1026
|
+
return {
|
|
1027
|
+
content: [{ type: "text", text: result }],
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
679
1030
|
switch (name) {
|
|
680
1031
|
case "arr_status": {
|
|
681
1032
|
const statuses = {};
|
|
@@ -685,6 +1036,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
685
1036
|
// Lingarr status handled by separate module
|
|
686
1037
|
statuses[service.name] = await getLingarrStatus(clients.lingarr);
|
|
687
1038
|
}
|
|
1039
|
+
else if (service.name === 'bazarr' && clients.bazarr) {
|
|
1040
|
+
// Bazarr has different status format
|
|
1041
|
+
const status = await clients.bazarr.getStatus();
|
|
1042
|
+
statuses[service.name] = {
|
|
1043
|
+
configured: true,
|
|
1044
|
+
connected: true,
|
|
1045
|
+
version: status.bazarr_version,
|
|
1046
|
+
appName: 'Bazarr',
|
|
1047
|
+
sonarrVersion: status.sonarr_version || 'Not connected',
|
|
1048
|
+
radarrVersion: status.radarr_version || 'Not connected',
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
688
1051
|
else {
|
|
689
1052
|
const client = clients[service.name];
|
|
690
1053
|
if (client) {
|
|
@@ -1107,6 +1470,304 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1107
1470
|
}],
|
|
1108
1471
|
};
|
|
1109
1472
|
}
|
|
1473
|
+
case "sonarr_add_series": {
|
|
1474
|
+
if (!clients.sonarr)
|
|
1475
|
+
throw new Error("Sonarr not configured");
|
|
1476
|
+
const { tvdbId, title, qualityProfileId, rootFolderPath, seasonFolder, monitored, tags } = args;
|
|
1477
|
+
const series = await clients.sonarr.addSeries({
|
|
1478
|
+
tvdbId,
|
|
1479
|
+
title,
|
|
1480
|
+
qualityProfileId,
|
|
1481
|
+
rootFolderPath,
|
|
1482
|
+
seasonFolder,
|
|
1483
|
+
monitored,
|
|
1484
|
+
tags,
|
|
1485
|
+
});
|
|
1486
|
+
return {
|
|
1487
|
+
content: [{
|
|
1488
|
+
type: "text",
|
|
1489
|
+
text: JSON.stringify({
|
|
1490
|
+
success: true,
|
|
1491
|
+
message: `Series '${title}' added to Sonarr`,
|
|
1492
|
+
seriesId: series.id,
|
|
1493
|
+
series: {
|
|
1494
|
+
id: series.id,
|
|
1495
|
+
title: series.title,
|
|
1496
|
+
path: series.path,
|
|
1497
|
+
monitored: series.monitored,
|
|
1498
|
+
},
|
|
1499
|
+
}, null, 2),
|
|
1500
|
+
}],
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
case "sonarr_update_series": {
|
|
1504
|
+
if (!clients.sonarr)
|
|
1505
|
+
throw new Error("Sonarr not configured");
|
|
1506
|
+
const { seriesId, ...updates } = args;
|
|
1507
|
+
const series = await clients.sonarr.updateSeries(seriesId, updates);
|
|
1508
|
+
return {
|
|
1509
|
+
content: [{
|
|
1510
|
+
type: "text",
|
|
1511
|
+
text: JSON.stringify({
|
|
1512
|
+
success: true,
|
|
1513
|
+
message: `Series '${series.title}' updated`,
|
|
1514
|
+
seriesId: series.id,
|
|
1515
|
+
updates: Object.keys(updates),
|
|
1516
|
+
}, null, 2),
|
|
1517
|
+
}],
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
case "sonarr_delete_series": {
|
|
1521
|
+
if (!clients.sonarr)
|
|
1522
|
+
throw new Error("Sonarr not configured");
|
|
1523
|
+
const { seriesId, deleteFiles = false, addImportListExclusion = false } = args;
|
|
1524
|
+
await clients.sonarr.deleteSeries(seriesId, deleteFiles, addImportListExclusion);
|
|
1525
|
+
return {
|
|
1526
|
+
content: [{
|
|
1527
|
+
type: "text",
|
|
1528
|
+
text: JSON.stringify({
|
|
1529
|
+
success: true,
|
|
1530
|
+
message: `Series deleted (files: ${deleteFiles}, exclude: ${addImportListExclusion})`,
|
|
1531
|
+
}, null, 2),
|
|
1532
|
+
}],
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
case "sonarr_update_episode": {
|
|
1536
|
+
if (!clients.sonarr)
|
|
1537
|
+
throw new Error("Sonarr not configured");
|
|
1538
|
+
const { episodeId, monitored } = args;
|
|
1539
|
+
const episode = await clients.sonarr.updateEpisode(episodeId, { monitored });
|
|
1540
|
+
return {
|
|
1541
|
+
content: [{
|
|
1542
|
+
type: "text",
|
|
1543
|
+
text: JSON.stringify({
|
|
1544
|
+
success: true,
|
|
1545
|
+
message: `Episode ${episodeId} ${monitored ? 'monitored' : 'unmonitored'}`,
|
|
1546
|
+
episodeId: episode.id,
|
|
1547
|
+
monitored: episode.monitored,
|
|
1548
|
+
}, null, 2),
|
|
1549
|
+
}],
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
case "sonarr_delete_episode_file": {
|
|
1553
|
+
if (!clients.sonarr)
|
|
1554
|
+
throw new Error("Sonarr not configured");
|
|
1555
|
+
const { episodeFileId } = args;
|
|
1556
|
+
await clients.sonarr.deleteEpisodeFile(episodeFileId);
|
|
1557
|
+
return {
|
|
1558
|
+
content: [{
|
|
1559
|
+
type: "text",
|
|
1560
|
+
text: JSON.stringify({
|
|
1561
|
+
success: true,
|
|
1562
|
+
message: `Episode file ${episodeFileId} deleted`,
|
|
1563
|
+
}, null, 2),
|
|
1564
|
+
}],
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
case "sonarr_refresh_series": {
|
|
1568
|
+
if (!clients.sonarr)
|
|
1569
|
+
throw new Error("Sonarr not configured");
|
|
1570
|
+
const { seriesId } = args;
|
|
1571
|
+
const result = await clients.sonarr.refreshSeries(seriesId);
|
|
1572
|
+
return {
|
|
1573
|
+
content: [{
|
|
1574
|
+
type: "text",
|
|
1575
|
+
text: JSON.stringify({
|
|
1576
|
+
success: true,
|
|
1577
|
+
message: `Series ${seriesId} metadata refresh triggered`,
|
|
1578
|
+
commandId: result.id,
|
|
1579
|
+
}, null, 2),
|
|
1580
|
+
}],
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
case "sonarr_rescan_series": {
|
|
1584
|
+
if (!clients.sonarr)
|
|
1585
|
+
throw new Error("Sonarr not configured");
|
|
1586
|
+
const { seriesId } = args;
|
|
1587
|
+
const result = await clients.sonarr.rescanSeries(seriesId);
|
|
1588
|
+
return {
|
|
1589
|
+
content: [{
|
|
1590
|
+
type: "text",
|
|
1591
|
+
text: JSON.stringify({
|
|
1592
|
+
success: true,
|
|
1593
|
+
message: `Series ${seriesId} disk rescan triggered`,
|
|
1594
|
+
commandId: result.id,
|
|
1595
|
+
}, null, 2),
|
|
1596
|
+
}],
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
case "sonarr_rename_series": {
|
|
1600
|
+
if (!clients.sonarr)
|
|
1601
|
+
throw new Error("Sonarr not configured");
|
|
1602
|
+
const { seriesId } = args;
|
|
1603
|
+
const result = await clients.sonarr.renameSeries(seriesId);
|
|
1604
|
+
return {
|
|
1605
|
+
content: [{
|
|
1606
|
+
type: "text",
|
|
1607
|
+
text: JSON.stringify({
|
|
1608
|
+
success: true,
|
|
1609
|
+
message: `Series ${seriesId} files will be renamed`,
|
|
1610
|
+
commandId: result.id,
|
|
1611
|
+
}, null, 2),
|
|
1612
|
+
}],
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
case "sonarr_rename_episodes": {
|
|
1616
|
+
if (!clients.sonarr)
|
|
1617
|
+
throw new Error("Sonarr not configured");
|
|
1618
|
+
const { seriesId } = args;
|
|
1619
|
+
const result = await clients.sonarr.renameEpisodes(seriesId);
|
|
1620
|
+
return {
|
|
1621
|
+
content: [{
|
|
1622
|
+
type: "text",
|
|
1623
|
+
text: JSON.stringify({
|
|
1624
|
+
success: true,
|
|
1625
|
+
message: `All episodes for series ${seriesId} will be renamed`,
|
|
1626
|
+
commandId: result.id,
|
|
1627
|
+
}, null, 2),
|
|
1628
|
+
}],
|
|
1629
|
+
};
|
|
1630
|
+
}
|
|
1631
|
+
case "sonarr_get_releases": {
|
|
1632
|
+
if (!clients.sonarr)
|
|
1633
|
+
throw new Error("Sonarr not configured");
|
|
1634
|
+
const { seriesId, page = 1, pageSize = 10 } = args;
|
|
1635
|
+
const releases = await clients.sonarr.getReleases(seriesId, page, pageSize);
|
|
1636
|
+
return {
|
|
1637
|
+
content: [{
|
|
1638
|
+
type: "text",
|
|
1639
|
+
text: JSON.stringify({
|
|
1640
|
+
totalRecords: releases.totalRecords,
|
|
1641
|
+
page,
|
|
1642
|
+
pageSize,
|
|
1643
|
+
releases: releases.records.map((r) => ({
|
|
1644
|
+
guid: r.guid,
|
|
1645
|
+
title: r.title,
|
|
1646
|
+
indexer: r.indexer,
|
|
1647
|
+
quality: r.quality,
|
|
1648
|
+
size: formatBytes(r.size),
|
|
1649
|
+
age: r.age,
|
|
1650
|
+
ageHours: r.ageHours,
|
|
1651
|
+
rejected: r.rejected,
|
|
1652
|
+
rejectionReason: r.rejectionReason,
|
|
1653
|
+
publishedDate: r.publishedDate,
|
|
1654
|
+
})),
|
|
1655
|
+
}, null, 2),
|
|
1656
|
+
}],
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
case "sonarr_delete_release": {
|
|
1660
|
+
if (!clients.sonarr)
|
|
1661
|
+
throw new Error("Sonarr not configured");
|
|
1662
|
+
const { guid } = args;
|
|
1663
|
+
await clients.sonarr.deleteRelease(guid);
|
|
1664
|
+
return {
|
|
1665
|
+
content: [{
|
|
1666
|
+
type: "text",
|
|
1667
|
+
text: JSON.stringify({
|
|
1668
|
+
success: true,
|
|
1669
|
+
message: `Release ${guid} deleted from history`,
|
|
1670
|
+
}, null, 2),
|
|
1671
|
+
}],
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
case "sonarr_create_release_profile": {
|
|
1675
|
+
if (!clients.sonarr)
|
|
1676
|
+
throw new Error("Sonarr not configured");
|
|
1677
|
+
const profile = await clients.sonarr.createReleaseProfile(args);
|
|
1678
|
+
return {
|
|
1679
|
+
content: [{
|
|
1680
|
+
type: "text",
|
|
1681
|
+
text: JSON.stringify({
|
|
1682
|
+
success: true,
|
|
1683
|
+
message: `Release profile '${profile.name}' created`,
|
|
1684
|
+
profileId: profile.id,
|
|
1685
|
+
profile,
|
|
1686
|
+
}, null, 2),
|
|
1687
|
+
}],
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
case "sonarr_update_release_profile": {
|
|
1691
|
+
if (!clients.sonarr)
|
|
1692
|
+
throw new Error("Sonarr not configured");
|
|
1693
|
+
const { profileId, ...updates } = args;
|
|
1694
|
+
const profile = await clients.sonarr.updateReleaseProfile(profileId, updates);
|
|
1695
|
+
return {
|
|
1696
|
+
content: [{
|
|
1697
|
+
type: "text",
|
|
1698
|
+
text: JSON.stringify({
|
|
1699
|
+
success: true,
|
|
1700
|
+
message: `Release profile ${profileId} updated`,
|
|
1701
|
+
profileId: profile.id,
|
|
1702
|
+
profile,
|
|
1703
|
+
}, null, 2),
|
|
1704
|
+
}],
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
case "sonarr_delete_release_profile": {
|
|
1708
|
+
if (!clients.sonarr)
|
|
1709
|
+
throw new Error("Sonarr not configured");
|
|
1710
|
+
const { profileId } = args;
|
|
1711
|
+
await clients.sonarr.deleteReleaseProfile(profileId);
|
|
1712
|
+
return {
|
|
1713
|
+
content: [{
|
|
1714
|
+
type: "text",
|
|
1715
|
+
text: JSON.stringify({
|
|
1716
|
+
success: true,
|
|
1717
|
+
message: `Release profile ${profileId} deleted`,
|
|
1718
|
+
}, null, 2),
|
|
1719
|
+
}],
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
case "sonarr_create_tag": {
|
|
1723
|
+
if (!clients.sonarr)
|
|
1724
|
+
throw new Error("Sonarr not configured");
|
|
1725
|
+
const { label } = args;
|
|
1726
|
+
const tag = await clients.sonarr.createTag(label);
|
|
1727
|
+
return {
|
|
1728
|
+
content: [{
|
|
1729
|
+
type: "text",
|
|
1730
|
+
text: JSON.stringify({
|
|
1731
|
+
success: true,
|
|
1732
|
+
message: `Tag '${label}' created`,
|
|
1733
|
+
tagId: tag.id,
|
|
1734
|
+
tag,
|
|
1735
|
+
}, null, 2),
|
|
1736
|
+
}],
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
case "sonarr_update_tag": {
|
|
1740
|
+
if (!clients.sonarr)
|
|
1741
|
+
throw new Error("Sonarr not configured");
|
|
1742
|
+
const { tagId, label } = args;
|
|
1743
|
+
const tag = await clients.sonarr.updateTag(tagId, label);
|
|
1744
|
+
return {
|
|
1745
|
+
content: [{
|
|
1746
|
+
type: "text",
|
|
1747
|
+
text: JSON.stringify({
|
|
1748
|
+
success: true,
|
|
1749
|
+
message: `Tag ${tagId} updated to '${label}'`,
|
|
1750
|
+
tagId: tag.id,
|
|
1751
|
+
tag,
|
|
1752
|
+
}, null, 2),
|
|
1753
|
+
}],
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
case "sonarr_delete_tag": {
|
|
1757
|
+
if (!clients.sonarr)
|
|
1758
|
+
throw new Error("Sonarr not configured");
|
|
1759
|
+
const { tagId } = args;
|
|
1760
|
+
await clients.sonarr.deleteTag(tagId);
|
|
1761
|
+
return {
|
|
1762
|
+
content: [{
|
|
1763
|
+
type: "text",
|
|
1764
|
+
text: JSON.stringify({
|
|
1765
|
+
success: true,
|
|
1766
|
+
message: `Tag ${tagId} deleted`,
|
|
1767
|
+
}, null, 2),
|
|
1768
|
+
}],
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1110
1771
|
// Radarr handlers
|
|
1111
1772
|
case "radarr_get_movies": {
|
|
1112
1773
|
if (!clients.radarr)
|