fix: properly pass qualityProfile and metadataProfile in music requests based on Lidarr configuration selection

This commit is contained in:
Pierre
2025-03-24 19:41:47 +01:00
committed by HiItsStolas
parent 8eb4633501
commit 73fd051886
8 changed files with 120 additions and 29 deletions

View File

@@ -727,6 +727,12 @@ components:
activeProfileName:
type: string
example: 128kps
activeMetadataProfileId:
type: number
example: 1
activeMetadataProfileName:
type: string
example: Standard
activeDirectory:
type: string
example: '/music/'

View File

@@ -278,6 +278,11 @@ export interface SearchCommand extends Record<string, unknown> {
albumIds: number[];
}
export interface MetadataProfile {
id: number;
name: string;
}
class LidarrAPI extends ServarrBase<{ albumId: number }> {
protected apiKey: string;
constructor({ url, apiKey }: { url: string; apiKey: string }) {
@@ -368,25 +373,13 @@ class LidarrAPI extends ServarrBase<{ albumId: number }> {
}
}
public async searchOnAdd(albumId: number): Promise<void> {
logger.info('Executing album search command', {
label: 'Lidarr API',
albumId,
});
public async getMetadataProfiles(): Promise<MetadataProfile[]> {
try {
await this.post('/command', {
name: 'AlbumSearch',
albumIds: [albumId],
});
const data = await this.get<MetadataProfile[]>('/metadataProfile');
return data;
} catch (e) {
logger.error(
'Something went wrong while executing Lidarr album search.',
{
label: 'Lidarr API',
errorMessage: e.message,
albumId,
}
throw new Error(
`[Lidarr] Failed to retrieve metadata profiles: ${e.message}`
);
}
}

View File

@@ -83,8 +83,6 @@ export interface RadarrSettings extends DVRSettings {
minimumAvailability: string;
}
export type LidarrSettings = DVRSettings;
export interface SonarrSettings extends DVRSettings {
seriesType: 'standard' | 'daily' | 'anime';
animeSeriesType: 'standard' | 'daily' | 'anime';
@@ -97,6 +95,11 @@ export interface SonarrSettings extends DVRSettings {
enableSeasonFolders: boolean;
}
export interface LidarrSettings extends DVRSettings {
activeMetadataProfileId?: number;
activeMetadataProfileName?: string;
}
interface Quota {
quotaLimit?: number;
quotaDays?: number;

View File

@@ -251,8 +251,9 @@ serviceRoutes.get<{ id: string }>('/lidarr/:id', async (req, res, next) => {
});
try {
const [profiles, rootFolders, tags] = await Promise.all([
const [profiles, metadataProfiles, rootFolders, tags] = await Promise.all([
lidarr.getProfiles(),
lidarr.getMetadataProfiles(),
lidarr.getRootFolders(),
lidarr.getTags(),
]);
@@ -264,8 +265,11 @@ serviceRoutes.get<{ id: string }>('/lidarr/:id', async (req, res, next) => {
isDefault: lidarrSettings.isDefault,
activeDirectory: lidarrSettings.activeDirectory,
activeProfileId: lidarrSettings.activeProfileId,
activeMetadataProfileId: lidarrSettings.activeMetadataProfileId,
activeTags: lidarrSettings.tags ?? [],
},
profiles,
metadataProfiles,
rootFolders: rootFolders.map((folder) => ({
id: folder.id,
path: folder.path,

View File

@@ -42,11 +42,13 @@ lidarrRoutes.post<
.then((value) => value.urlBase)
.catch(() => req.body.baseUrl);
const profiles = await lidarr.getProfiles();
const metadataProfiles = await lidarr.getMetadataProfiles();
const folders = await lidarr.getRootFolders();
const tags = await lidarr.getTags();
return res.status(200).json({
profiles,
metadataProfiles,
rootFolders: folders.map((folder) => ({
id: folder.id,
path: folder.path,
@@ -59,7 +61,6 @@ lidarrRoutes.post<
label: 'Lidarr',
message: e.message,
});
next({ status: 500, message: 'Failed to connect to Lidarr' });
}
});

View File

@@ -1,6 +1,7 @@
import CoverArtArchive from '@server/api/coverartarchive';
import ListenBrainzAPI from '@server/api/listenbrainz';
import MusicBrainz from '@server/api/musicbrainz';
import type { LidarrAlbumOptions } from '@server/api/servarr/lidarr';
import LidarrAPI from '@server/api/servarr/lidarr';
import type { RadarrMovieOptions } from '@server/api/servarr/radarr';
import RadarrAPI from '@server/api/servarr/radarr';
@@ -915,6 +916,21 @@ export class MediaRequestSubscriber
});
}
let qualityProfile = lidarrSettings.activeProfileId;
const metadataProfile = lidarrSettings.activeMetadataProfileId ?? 1;
if (entity.profileId && entity.profileId !== qualityProfile) {
qualityProfile = entity.profileId;
logger.info(
`Request has an override quality profile ID: ${qualityProfile}`,
{
label: 'Media Request',
requestId: entity.id,
mediaId: entity.media.id,
}
);
}
const artistPath = `${rootFolder}/${albumInfo.artist.artistName}`;
const addAlbumPayload: LidarrAlbumOptions = {
@@ -925,7 +941,7 @@ export class MediaRequestSubscriber
foreignAlbumId: albumInfo.foreignAlbumId,
monitored: true,
anyReleaseOk: true,
profileId: 1,
profileId: qualityProfile,
duration: albumInfo.duration || 0,
albumType: albumInfo.albumType,
secondaryTypes: [],
@@ -948,8 +964,8 @@ export class MediaRequestSubscriber
links: albumInfo.artist.links || [],
images: albumInfo.artist.images || [],
path: artistPath,
qualityProfileId: 1,
metadataProfileId: 2,
qualityProfileId: qualityProfile,
metadataProfileId: metadataProfile,
monitored: true,
monitorNewItems: 'none',
rootFolderPath: rootFolder,
@@ -978,15 +994,10 @@ export class MediaRequestSubscriber
};
await mediaRepository.update({ id: entity.media.id }, updateFields);
if (addAlbumPayload.addOptions.searchForNewAlbum) {
await lidarr.searchOnAdd(result.id);
}
})
.catch(async (error) => {
const requestRepository = getRepository(MediaRequest);
entity.status = MediaRequestStatus.FAILED;
await requestRepository.update(
{ id: entity.id },
{ status: MediaRequestStatus.FAILED }

View File

@@ -59,6 +59,11 @@ const messages = defineMessages('components.Settings.LidarrModal', {
tags: 'Tags',
notagoptions: 'No tags.',
selecttags: 'Select tags',
metadataprofile: 'Metadata Profile',
selectMetadataProfile: 'Select metadata profile',
loadingmetadataprofiles: 'Loading metadata profiles…',
testFirstMetadataProfiles: 'Test connection to load metadata profiles',
validationMetadataProfileRequired: 'You must select a metadata profile',
});
interface TestResponse {
@@ -66,6 +71,10 @@ interface TestResponse {
id: number;
name: string;
}[];
metadataProfiles: {
id: number;
name: string;
}[];
rootFolders: {
id: number;
path: string;
@@ -91,6 +100,7 @@ const LidarrModal = ({ onClose, lidarr, onSave }: LidarrModalProps) => {
const [isTesting, setIsTesting] = useState(false);
const [testResponse, setTestResponse] = useState<TestResponse>({
profiles: [],
metadataProfiles: [],
rootFolders: [],
tags: [],
});
@@ -134,6 +144,9 @@ const LidarrModal = ({ onClose, lidarr, onSave }: LidarrModalProps) => {
intl.formatMessage(messages.validationBaseUrlTrailingSlash),
(value) => !value || !value.endsWith('/')
),
activeMetadataProfileId: Yup.string().required(
intl.formatMessage(messages.validationMetadataProfileRequired)
),
});
const testConnection = useCallback(
@@ -236,6 +249,7 @@ const LidarrModal = ({ onClose, lidarr, onSave }: LidarrModalProps) => {
syncEnabled: lidarr?.syncEnabled ?? false,
enableSearch: !lidarr?.preventSearch,
tagRequests: lidarr?.tagRequests ?? false,
activeMetadataProfileId: lidarr?.activeMetadataProfileId ?? 1,
}}
validationSchema={LidarrSettingsSchema}
onSubmit={async (values) => {
@@ -260,6 +274,11 @@ const LidarrModal = ({ onClose, lidarr, onSave }: LidarrModalProps) => {
syncEnabled: values.syncEnabled,
preventSearch: !values.enableSearch,
tagRequests: values.tagRequests,
activeMetadataProfileId: Number(values.activeMetadataProfileId),
activeMetadataProfileName: testResponse.metadataProfiles.find(
(profile) =>
profile.id === Number(values.activeMetadataProfileId)
)?.name,
};
const response = await fetch(
@@ -569,6 +588,55 @@ const LidarrModal = ({ onClose, lidarr, onSave }: LidarrModalProps) => {
)}
</div>
</div>
<div className="form-row">
<label
htmlFor="activeMetadataProfileId"
className="text-label"
>
{intl.formatMessage(messages.metadataprofile)}
<span className="label-required">*</span>
</label>
<div className="form-input-area">
<div className="form-input-field">
<Field
as="select"
id="activeMetadataProfileId"
name="activeMetadataProfileId"
disabled={!isValidated || isTesting}
>
<option value="">
{isTesting
? intl.formatMessage(
messages.loadingmetadataprofiles
)
: !isValidated
? intl.formatMessage(
messages.testFirstMetadataProfiles
)
: intl.formatMessage(
messages.selectMetadataProfile
)}
</option>
{testResponse.metadataProfiles.length > 0 &&
testResponse.metadataProfiles.map((profile) => (
<option
key={`loaded-metadataprofile-${profile.id}`}
value={profile.id}
>
{profile.name}
</option>
))}
</Field>
</div>
{errors.activeMetadataProfileId &&
touched.activeMetadataProfileId &&
typeof errors.activeMetadataProfileId === 'string' && (
<div className="error">
{errors.activeMetadataProfileId}
</div>
)}
</div>
</div>
<div className="form-row">
<label htmlFor="tags" className="text-label">
{intl.formatMessage(messages.tags)}

View File

@@ -719,12 +719,15 @@
"components.Settings.LidarrModal.externalUrl": "External URL",
"components.Settings.LidarrModal.hostname": "Hostname or IP Address",
"components.Settings.LidarrModal.loadingTags": "Loading tags…",
"components.Settings.LidarrModal.loadingmetadataprofiles": "Loading metadata profiles…",
"components.Settings.LidarrModal.loadingprofiles": "Loading quality profiles…",
"components.Settings.LidarrModal.loadingrootfolders": "Loading root folders…",
"components.Settings.LidarrModal.metadataprofile": "Metadata Profile",
"components.Settings.LidarrModal.notagoptions": "No tags.",
"components.Settings.LidarrModal.port": "Port",
"components.Settings.LidarrModal.qualityprofile": "Quality Profile",
"components.Settings.LidarrModal.rootfolder": "Root Folder",
"components.Settings.LidarrModal.selectMetadataProfile": "Select metadata profile",
"components.Settings.LidarrModal.selectQualityProfile": "Select quality profile",
"components.Settings.LidarrModal.selectRootFolder": "Select root folder",
"components.Settings.LidarrModal.selecttags": "Select tags",
@@ -734,6 +737,7 @@
"components.Settings.LidarrModal.tagRequests": "Tag Requests",
"components.Settings.LidarrModal.tagRequestsInfo": "Automatically add an additional tag with the requester's user ID & display name",
"components.Settings.LidarrModal.tags": "Tags",
"components.Settings.LidarrModal.testFirstMetadataProfiles": "Test connection to load metadata profiles",
"components.Settings.LidarrModal.testFirstQualityProfiles": "Test connection to load quality profiles",
"components.Settings.LidarrModal.testFirstRootFolders": "Test connection to load root folders",
"components.Settings.LidarrModal.testFirstTags": "Test connection to load tags",
@@ -745,6 +749,7 @@
"components.Settings.LidarrModal.validationBaseUrlLeadingSlash": "Base URL must have a leading slash",
"components.Settings.LidarrModal.validationBaseUrlTrailingSlash": "Base URL must not end in a trailing slash",
"components.Settings.LidarrModal.validationHostnameRequired": "You must provide a valid hostname or IP address",
"components.Settings.LidarrModal.validationMetadataProfileRequired": "You must select a metadata profile",
"components.Settings.LidarrModal.validationNameRequired": "You must provide a server name",
"components.Settings.LidarrModal.validationPortRequired": "You must provide a valid port number",
"components.Settings.LidarrModal.validationProfileRequired": "You must select a quality profile",