From 810f3ab76356854762f653d23ea04bdf5f59340c Mon Sep 17 00:00:00 2001 From: FoxxMD Date: Mon, 28 Sep 2026 16:02:46 +0000 Subject: [PATCH] feat(musicbrainz): Disallow audiobook and music videos by default --- .../configuration/transforms/musicbrainz.mdx | 64 ++++++++++++++++++- .../transforms/MusicbrainzTransformer.ts | 21 ++++-- .../musicbrainz/MusicbrainzTransformerUtil.ts | 35 +++++++--- 3 files changed, 105 insertions(+), 15 deletions(-) diff --git a/docsite/docs/configuration/transforms/musicbrainz.mdx b/docsite/docs/configuration/transforms/musicbrainz.mdx index 57fb3319..315517ca 100644 --- a/docsite/docs/configuration/transforms/musicbrainz.mdx +++ b/docsite/docs/configuration/transforms/musicbrainz.mdx @@ -321,6 +321,8 @@ To configure [stage defaults](#stage-configuration) use `MB_PRESETS` with a comm * `aggressive` - Adds **Free Text Search** (`freetext`) to [`searchOrder`](#search-methods) * `fields` - Applies [Field Scoring](#field-scoring) to match sorting so match text is baised towards similarity to original scrobble * `sensible` - Applies the [Sensible Default](#sensible-default) configuration for [sorting releases](#sorting-releases) +* `musicvideo` - *Allows* music videos as a valid search result (not allowed by default) +* `audiobook` - *Allows* audiobooks as a valid search result (not allowed by default) Finally, use ENV `*_TRANSFORMS=musicbrainz` on each Source/Client you wish to apply this stage to. This applies the stage in the [`preTransform` Hook](/configuration/transforms#lifecycle-hooks) with all [Rules](#rules-and-hooks) enabled. @@ -732,7 +734,7 @@ An easy way to think about this: * Should a track that belongs to a Compilation album be allowed to match? * What country do you prefer an album release to be from? -##### Release Attributes +#### Release Attributes These attributes (`attribute_name` in config) are: @@ -757,11 +759,67 @@ EX to exclude compilations "releaseGroupSecondaryTypeDeny": ["compilation"] ``` +:::tip[Allow Overrides Deny] + +When a release attributes has both types of filter (`allow` and `deny`), only `allow` is used: + +``` +"releaseStatusAllow": ["official"], +"releaseStatusDeny": ["bootleg","promotional"] // this is ignored! +``` + +::: + + + +The [Release Group **Secondary Type**](https://wiki.musicbrainz.org/Release_Group/Type#Secondary_types) (`releaseGroupSecondaryType`) can be `audiobook` which is rarely a desired result. + +When `releaseGroupSecondaryTypeDeny` is **not** defined it defaults to: + +``` +"releaseGroupSecondaryTypeDeny": ["audiobook"] +``` + +which filters out audiobook results. + +If you *want* to include audiobook results then explicitly define this and set it to an empty array EX: + +``` +"releaseGroupSecondaryTypeDeny": [] +``` + + + +#### Music Videos + +Search results can also return Recordings that are **Music Videos**. These are rarely a desired result so they are **filtered out by default.** + +If you want to include music videos in results then add this to your config: `"allowMusicVideo": true` + +
+ +Example + +```json5 +// ... +"defaults": { + //"releaseStatusAllow": ["official"], + // ...etc + "allowMusicVideo": true +} +``` + +
+ #### Empty Releases Sometimes a Recording may not have any associated Releases. This may be because there is not enough information about the Recording yet, or it was never included on an actual Release. -You may want to filter the majority of your matches by releases but allow matching a Recording that has no releases to begin with. To allow this set `releaseAllowEmpty` to `true` in configuration: +You may want to filter the majority of your matches by releases but allow matching a Recording that has no releases to begin with. To allow this set `releaseAllowEmpty` to `true` in configuration. + +
+ +Example ```json5 // ... @@ -771,6 +829,8 @@ You may want to filter the majority of your matches by releases but allow matchi } ``` +
+ ### Sorting diff --git a/src/backend/common/transforms/MusicbrainzTransformer.ts b/src/backend/common/transforms/MusicbrainzTransformer.ts index 7e735306..cecafb0c 100644 --- a/src/backend/common/transforms/MusicbrainzTransformer.ts +++ b/src/backend/common/transforms/MusicbrainzTransformer.ts @@ -1,5 +1,5 @@ import { type ArtistCredit, DEFAULT_MISSING_TYPES, type LifecycleInput, type MissingMbidType, type OptionalCacheUsage, type PlayObject, type TrackMetaIsrc } from "../../../core/Atomic.ts"; -import { mBReleaseSecondaryGroupTypesSchema } from "../vendor/musicbrainz/MusicbrainzTypes.ts"; +import { MB_RELEASE_GROUP_SECONDARY_TYPES, mBReleaseSecondaryGroupTypesSchema } from "../vendor/musicbrainz/MusicbrainzTypes.ts"; import { type MBReleaseGroupSecondaryType } from "../vendor/musicbrainz/MusicbrainzTypes.ts"; import { type MBReleaseGroupPrimaryType } from "../vendor/musicbrainz/MusicbrainzTypes.ts"; import { mBReleasePrimaryGroupTypesSchema } from "../vendor/musicbrainz/MusicbrainzTypes.ts"; @@ -113,7 +113,7 @@ export const parseStageConfig = (data: MusicbrainzTransformerData | undefined = releaseGroupPrimaryTypeDeny, releaseGroupPrimaryTypePriority, releaseGroupSecondaryTypeAllow, - releaseGroupSecondaryTypeDeny, + releaseGroupSecondaryTypeDeny = [MB_RELEASE_GROUP_SECONDARY_TYPES.enum.audiobook], releaseGroupSecondaryTypePriority, releaseStatusAllow, releaseStatusDeny, @@ -131,6 +131,7 @@ export const parseStageConfig = (data: MusicbrainzTransformerData | undefined = titleWeight, albumWeight, artistWeight, + allowMusicVideo = false, ...rest } = data; @@ -153,6 +154,7 @@ export const parseStageConfig = (data: MusicbrainzTransformerData | undefined = releaseCountryAllow: maybeStringLowerArrayFromString.parse(releaseCountryAllow), releaseCountryDeny: maybeStringLowerArrayFromString.parse(releaseCountryDeny), releaseCountryPriority: maybeStringLowerArrayFromString.parse(releaseCountryPriority), + allowMusicVideo, ...rest, }; @@ -162,6 +164,10 @@ export const parseStageConfig = (data: MusicbrainzTransformerData | undefined = logger.debug(`Will search if missing: ${config.searchWhenMissing.join(', ')} | Match if (default) score is >= ${config.score}`); + if(allowMusicVideo === false) { + logger.debug('Disallowing music videos in results'); + } + let soSet = searchOrder.length > 0 ? new Set(searchOrder.map(asSearchType)) : new Set(); const depSearch = []; @@ -518,7 +524,8 @@ export default class MusicbrainzTransformer extends AtomicPartsTransformer x.video === true)) { + hadMusicVideo = true; + filteredList = filteredList.filter(x => x.video !== true); + } + const mergedConfig = Object.assign({}, removeUndefinedKeys({...this.defaults}), removeUndefinedKeys({...stageConfig})); filteredList = filterByValidReleaseStatus(filteredList, mergedConfig); filteredList = filterByValidReleaseGroupPrimary(filteredList, mergedConfig); @@ -547,7 +560,7 @@ export default class MusicbrainzTransformer extends AtomicPartsTransformer = { default: DEFAULTS_PRESET, sensible: DEFAULTS_SENSIBLE, native: DEFAULTS_NATIVE, aggressive: DEFAULTS_AGGRESSIVE, fields: DEFAULTS_FIELDS_BIAS, + musicvideo: DEFAULTS_MUSIC_VIDEO, + audiobook: DEFAULTS_AUDIOBOOK, 'id': DEFAULTS_ID }; -- 2.51.2