2022-12-22 23:38:55 +01:00
|
|
|
const Logger = require('../../Logger')
|
2023-10-20 00:20:12 +02:00
|
|
|
const { areEquivalent, copyValue, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
2022-12-22 23:38:55 +01:00
|
|
|
|
|
|
|
class MusicMetadata {
|
|
|
|
constructor(metadata) {
|
|
|
|
this.title = null
|
2023-01-02 23:35:39 +01:00
|
|
|
this.artists = [] // Array of strings
|
2022-12-22 23:38:55 +01:00
|
|
|
this.album = null
|
2023-01-02 23:35:39 +01:00
|
|
|
this.albumArtist = null
|
2022-12-22 23:38:55 +01:00
|
|
|
this.genres = [] // Array of strings
|
2023-01-02 23:35:39 +01:00
|
|
|
this.composer = null
|
|
|
|
this.originalYear = null
|
2022-12-22 23:38:55 +01:00
|
|
|
this.releaseDate = null
|
2023-01-02 23:35:39 +01:00
|
|
|
this.releaseCountry = null
|
|
|
|
this.releaseType = null
|
|
|
|
this.releaseStatus = null
|
|
|
|
this.recordLabel = null
|
2022-12-22 23:38:55 +01:00
|
|
|
this.language = null
|
|
|
|
this.explicit = false
|
|
|
|
|
2023-01-02 23:35:39 +01:00
|
|
|
this.discNumber = null
|
|
|
|
this.discTotal = null
|
|
|
|
this.trackNumber = null
|
|
|
|
this.trackTotal = null
|
|
|
|
|
|
|
|
this.isrc = null
|
|
|
|
this.musicBrainzTrackId = null
|
|
|
|
this.musicBrainzAlbumId = null
|
|
|
|
this.musicBrainzAlbumArtistId = null
|
|
|
|
this.musicBrainzArtistId = null
|
|
|
|
|
2022-12-22 23:38:55 +01:00
|
|
|
if (metadata) {
|
|
|
|
this.construct(metadata)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
construct(metadata) {
|
|
|
|
this.title = metadata.title
|
2023-01-02 23:35:39 +01:00
|
|
|
this.artists = metadata.artists ? [...metadata.artists] : []
|
2022-12-22 23:38:55 +01:00
|
|
|
this.album = metadata.album
|
2023-01-02 23:35:39 +01:00
|
|
|
this.albumArtist = metadata.albumArtist
|
2022-12-22 23:38:55 +01:00
|
|
|
this.genres = metadata.genres ? [...metadata.genres] : []
|
2023-01-02 23:35:39 +01:00
|
|
|
this.composer = metadata.composer || null
|
|
|
|
this.originalYear = metadata.originalYear || null
|
2022-12-22 23:38:55 +01:00
|
|
|
this.releaseDate = metadata.releaseDate || null
|
2023-01-02 23:35:39 +01:00
|
|
|
this.releaseCountry = metadata.releaseCountry || null
|
|
|
|
this.releaseType = metadata.releaseType || null
|
|
|
|
this.releaseStatus = metadata.releaseStatus || null
|
|
|
|
this.recordLabel = metadata.recordLabel || null
|
|
|
|
this.language = metadata.language || null
|
2022-12-22 23:38:55 +01:00
|
|
|
this.explicit = !!metadata.explicit
|
2023-01-02 23:35:39 +01:00
|
|
|
this.discNumber = metadata.discNumber || null
|
|
|
|
this.discTotal = metadata.discTotal || null
|
|
|
|
this.trackNumber = metadata.trackNumber || null
|
|
|
|
this.trackTotal = metadata.trackTotal || null
|
|
|
|
this.isrc = metadata.isrc || null
|
|
|
|
this.musicBrainzTrackId = metadata.musicBrainzTrackId || null
|
|
|
|
this.musicBrainzAlbumId = metadata.musicBrainzAlbumId || null
|
|
|
|
this.musicBrainzAlbumArtistId = metadata.musicBrainzAlbumArtistId || null
|
|
|
|
this.musicBrainzArtistId = metadata.musicBrainzArtistId || null
|
2022-12-22 23:38:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
toJSON() {
|
|
|
|
return {
|
|
|
|
title: this.title,
|
2023-01-02 23:35:39 +01:00
|
|
|
artists: [...this.artists],
|
2022-12-22 23:38:55 +01:00
|
|
|
album: this.album,
|
2023-01-02 23:35:39 +01:00
|
|
|
albumArtist: this.albumArtist,
|
2022-12-22 23:38:55 +01:00
|
|
|
genres: [...this.genres],
|
2023-01-02 23:35:39 +01:00
|
|
|
composer: this.composer,
|
|
|
|
originalYear: this.originalYear,
|
2022-12-22 23:38:55 +01:00
|
|
|
releaseDate: this.releaseDate,
|
2023-01-02 23:35:39 +01:00
|
|
|
releaseCountry: this.releaseCountry,
|
|
|
|
releaseType: this.releaseType,
|
|
|
|
releaseStatus: this.releaseStatus,
|
|
|
|
recordLabel: this.recordLabel,
|
2022-12-22 23:38:55 +01:00
|
|
|
language: this.language,
|
2023-01-02 23:35:39 +01:00
|
|
|
explicit: this.explicit,
|
|
|
|
discNumber: this.discNumber,
|
|
|
|
discTotal: this.discTotal,
|
|
|
|
trackNumber: this.trackNumber,
|
|
|
|
trackTotal: this.trackTotal,
|
|
|
|
isrc: this.isrc,
|
|
|
|
musicBrainzTrackId: this.musicBrainzTrackId,
|
|
|
|
musicBrainzAlbumId: this.musicBrainzAlbumId,
|
|
|
|
musicBrainzAlbumArtistId: this.musicBrainzAlbumArtistId,
|
|
|
|
musicBrainzArtistId: this.musicBrainzArtistId
|
2022-12-22 23:38:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
toJSONMinified() {
|
|
|
|
return {
|
|
|
|
title: this.title,
|
|
|
|
titleIgnorePrefix: this.titlePrefixAtEnd,
|
2023-01-02 23:35:39 +01:00
|
|
|
artists: [...this.artists],
|
2022-12-22 23:38:55 +01:00
|
|
|
album: this.album,
|
2023-01-02 23:35:39 +01:00
|
|
|
albumArtist: this.albumArtist,
|
2022-12-22 23:38:55 +01:00
|
|
|
genres: [...this.genres],
|
2023-01-02 23:35:39 +01:00
|
|
|
composer: this.composer,
|
|
|
|
originalYear: this.originalYear,
|
2022-12-22 23:38:55 +01:00
|
|
|
releaseDate: this.releaseDate,
|
2023-01-02 23:35:39 +01:00
|
|
|
releaseCountry: this.releaseCountry,
|
|
|
|
releaseType: this.releaseType,
|
|
|
|
releaseStatus: this.releaseStatus,
|
|
|
|
recordLabel: this.recordLabel,
|
2022-12-22 23:38:55 +01:00
|
|
|
language: this.language,
|
2023-01-02 23:35:39 +01:00
|
|
|
explicit: this.explicit,
|
|
|
|
discNumber: this.discNumber,
|
|
|
|
discTotal: this.discTotal,
|
|
|
|
trackNumber: this.trackNumber,
|
|
|
|
trackTotal: this.trackTotal,
|
|
|
|
isrc: this.isrc,
|
|
|
|
musicBrainzTrackId: this.musicBrainzTrackId,
|
|
|
|
musicBrainzAlbumId: this.musicBrainzAlbumId,
|
|
|
|
musicBrainzAlbumArtistId: this.musicBrainzAlbumArtistId,
|
|
|
|
musicBrainzArtistId: this.musicBrainzArtistId
|
2022-12-22 23:38:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
toJSONExpanded() {
|
|
|
|
return this.toJSONMinified()
|
|
|
|
}
|
|
|
|
|
|
|
|
clone() {
|
|
|
|
return new MusicMetadata(this.toJSON())
|
|
|
|
}
|
|
|
|
|
|
|
|
get titleIgnorePrefix() {
|
|
|
|
return getTitleIgnorePrefix(this.title)
|
|
|
|
}
|
|
|
|
|
|
|
|
get titlePrefixAtEnd() {
|
|
|
|
return getTitlePrefixAtEnd(this.title)
|
|
|
|
}
|
|
|
|
|
|
|
|
setData(mediaMetadata = {}) {
|
|
|
|
this.title = mediaMetadata.title || null
|
|
|
|
this.artist = mediaMetadata.artist || null
|
|
|
|
this.album = mediaMetadata.album || null
|
|
|
|
}
|
|
|
|
|
|
|
|
update(payload) {
|
|
|
|
const json = this.toJSON()
|
|
|
|
let hasUpdates = false
|
|
|
|
for (const key in json) {
|
|
|
|
if (payload[key] !== undefined) {
|
|
|
|
if (!areEquivalent(payload[key], json[key])) {
|
|
|
|
this[key] = copyValue(payload[key])
|
|
|
|
Logger.debug('[MusicMetadata] Key updated', key, this[key])
|
|
|
|
hasUpdates = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hasUpdates
|
|
|
|
}
|
2023-01-02 23:35:39 +01:00
|
|
|
|
|
|
|
parseArtistsTag(artistsTag) {
|
|
|
|
if (!artistsTag || !artistsTag.length) return []
|
|
|
|
const separators = ['/', '//', ';']
|
|
|
|
for (let i = 0; i < separators.length; i++) {
|
|
|
|
if (artistsTag.includes(separators[i])) {
|
|
|
|
return artistsTag.split(separators[i]).map(artist => artist.trim()).filter(a => !!a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [artistsTag]
|
|
|
|
}
|
|
|
|
|
|
|
|
parseGenresTag(genreTag) {
|
|
|
|
if (!genreTag || !genreTag.length) return []
|
|
|
|
const separators = ['/', '//', ';']
|
|
|
|
for (let i = 0; i < separators.length; i++) {
|
|
|
|
if (genreTag.includes(separators[i])) {
|
|
|
|
return genreTag.split(separators[i]).map(genre => genre.trim()).filter(g => !!g)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [genreTag]
|
|
|
|
}
|
|
|
|
|
|
|
|
setDataFromAudioMetaTags(audioFileMetaTags, overrideExistingDetails = false) {
|
|
|
|
const MetadataMapArray = [
|
|
|
|
{
|
|
|
|
tag: 'tagTitle',
|
|
|
|
key: 'title',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagArtist',
|
|
|
|
key: 'artists'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagAlbumArtist',
|
|
|
|
key: 'albumArtist'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagAlbum',
|
|
|
|
key: 'album',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagPublisher',
|
|
|
|
key: 'recordLabel'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagComposer',
|
|
|
|
key: 'composer'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagDate',
|
|
|
|
key: 'releaseDate'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagReleaseCountry',
|
|
|
|
key: 'releaseCountry'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagReleaseType',
|
|
|
|
key: 'releaseType'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagReleaseStatus',
|
|
|
|
key: 'releaseStatus'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagOriginalYear',
|
|
|
|
key: 'originalYear'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagGenre',
|
|
|
|
key: 'genres'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagLanguage',
|
|
|
|
key: 'language'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagLanguage',
|
|
|
|
key: 'language'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagISRC',
|
|
|
|
key: 'isrc'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagMusicBrainzTrackId',
|
|
|
|
key: 'musicBrainzTrackId'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagMusicBrainzAlbumId',
|
|
|
|
key: 'musicBrainzAlbumId'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagMusicBrainzAlbumArtistId',
|
|
|
|
key: 'musicBrainzAlbumArtistId'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'tagMusicBrainzArtistId',
|
|
|
|
key: 'musicBrainzArtistId'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'trackNumber',
|
|
|
|
key: 'trackNumber'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'trackTotal',
|
|
|
|
key: 'trackTotal'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'discNumber',
|
|
|
|
key: 'discNumber'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
tag: 'discTotal',
|
|
|
|
key: 'discTotal'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
const updatePayload = {}
|
|
|
|
|
|
|
|
// Metadata is only mapped to the music track if it is empty
|
|
|
|
MetadataMapArray.forEach((mapping) => {
|
|
|
|
let value = audioFileMetaTags[mapping.tag]
|
2023-01-07 00:39:15 +01:00
|
|
|
|
2023-01-02 23:35:39 +01:00
|
|
|
// let tagToUse = mapping.tag
|
|
|
|
if (!value && mapping.altTag) {
|
|
|
|
value = audioFileMetaTags[mapping.altTag]
|
|
|
|
// tagToUse = mapping.altTag
|
|
|
|
}
|
|
|
|
|
2023-01-07 00:39:15 +01:00
|
|
|
if (value && (typeof value === 'string' || typeof value === 'number')) {
|
|
|
|
value = value.toString().trim() // Trim whitespace
|
2023-01-02 23:35:39 +01:00
|
|
|
|
|
|
|
if (mapping.key === 'artists' && (!this.artists.length || overrideExistingDetails)) {
|
|
|
|
updatePayload.artists = this.parseArtistsTag(value)
|
|
|
|
} else if (mapping.key === 'genres' && (!this.genres.length || overrideExistingDetails)) {
|
|
|
|
updatePayload.genres = this.parseGenresTag(value)
|
|
|
|
} else if (!this[mapping.key] || overrideExistingDetails) {
|
|
|
|
updatePayload[mapping.key] = value
|
|
|
|
// Logger.debug(`[Book] Mapping metadata to key ${tagToUse} => ${mapping.key}: ${updatePayload[mapping.key]}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if (Object.keys(updatePayload).length) {
|
|
|
|
return this.update(updatePayload)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2022-12-22 23:38:55 +01:00
|
|
|
}
|
|
|
|
module.exports = MusicMetadata
|