mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-16 06:11:38 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
b3f9a40f18
227 changed files with 11338 additions and 3382 deletions
|
|
@ -1,9 +1,12 @@
|
|||
class AudioMetaTags {
|
||||
constructor(metadata) {
|
||||
this.tagAlbum = null
|
||||
this.tagAlbumSort = null
|
||||
this.tagArtist = null
|
||||
this.tagArtistSort = null
|
||||
this.tagGenre = null
|
||||
this.tagTitle = null
|
||||
this.tagTitleSort = null
|
||||
this.tagSeries = null
|
||||
this.tagSeriesPart = null
|
||||
this.tagTrack = null
|
||||
|
|
@ -20,6 +23,9 @@ class AudioMetaTags {
|
|||
this.tagIsbn = null
|
||||
this.tagLanguage = null
|
||||
this.tagASIN = null
|
||||
this.tagItunesId = null
|
||||
this.tagPodcastType = null
|
||||
this.tagEpisodeType = null
|
||||
this.tagOverdriveMediaMarker = null
|
||||
this.tagOriginalYear = null
|
||||
this.tagReleaseCountry = null
|
||||
|
|
@ -94,9 +100,12 @@ class AudioMetaTags {
|
|||
|
||||
construct(metadata) {
|
||||
this.tagAlbum = metadata.tagAlbum || null
|
||||
this.tagAlbumSort = metadata.tagAlbumSort || null
|
||||
this.tagArtist = metadata.tagArtist || null
|
||||
this.tagArtistSort = metadata.tagArtistSort || null
|
||||
this.tagGenre = metadata.tagGenre || null
|
||||
this.tagTitle = metadata.tagTitle || null
|
||||
this.tagTitleSort = metadata.tagTitleSort || null
|
||||
this.tagSeries = metadata.tagSeries || null
|
||||
this.tagSeriesPart = metadata.tagSeriesPart || null
|
||||
this.tagTrack = metadata.tagTrack || null
|
||||
|
|
@ -113,6 +122,9 @@ class AudioMetaTags {
|
|||
this.tagIsbn = metadata.tagIsbn || null
|
||||
this.tagLanguage = metadata.tagLanguage || null
|
||||
this.tagASIN = metadata.tagASIN || null
|
||||
this.tagItunesId = metadata.tagItunesId || null
|
||||
this.tagPodcastType = metadata.tagPodcastType || null
|
||||
this.tagEpisodeType = metadata.tagEpisodeType || null
|
||||
this.tagOverdriveMediaMarker = metadata.tagOverdriveMediaMarker || null
|
||||
this.tagOriginalYear = metadata.tagOriginalYear || null
|
||||
this.tagReleaseCountry = metadata.tagReleaseCountry || null
|
||||
|
|
@ -128,9 +140,12 @@ class AudioMetaTags {
|
|||
// Data parsed in prober.js
|
||||
setData(payload) {
|
||||
this.tagAlbum = payload.file_tag_album || null
|
||||
this.tagAlbumSort = payload.file_tag_albumsort || null
|
||||
this.tagArtist = payload.file_tag_artist || null
|
||||
this.tagArtistSort = payload.file_tag_artistsort || null
|
||||
this.tagGenre = payload.file_tag_genre || null
|
||||
this.tagTitle = payload.file_tag_title || null
|
||||
this.tagTitleSort = payload.file_tag_titlesort || null
|
||||
this.tagSeries = payload.file_tag_series || null
|
||||
this.tagSeriesPart = payload.file_tag_seriespart || null
|
||||
this.tagTrack = payload.file_tag_track || null
|
||||
|
|
@ -147,6 +162,9 @@ class AudioMetaTags {
|
|||
this.tagIsbn = payload.file_tag_isbn || null
|
||||
this.tagLanguage = payload.file_tag_language || null
|
||||
this.tagASIN = payload.file_tag_asin || null
|
||||
this.tagItunesId = payload.file_tag_itunesid || null
|
||||
this.tagPodcastType = payload.file_tag_podcasttype || null
|
||||
this.tagEpisodeType = payload.file_tag_episodetype || null
|
||||
this.tagOverdriveMediaMarker = payload.file_tag_overdrive_media_marker || null
|
||||
this.tagOriginalYear = payload.file_tag_originalyear || null
|
||||
this.tagReleaseCountry = payload.file_tag_releasecountry || null
|
||||
|
|
@ -166,9 +184,12 @@ class AudioMetaTags {
|
|||
updateData(payload) {
|
||||
const dataMap = {
|
||||
tagAlbum: payload.file_tag_album || null,
|
||||
tagAlbumSort: payload.file_tag_albumsort || null,
|
||||
tagArtist: payload.file_tag_artist || null,
|
||||
tagArtistSort: payload.file_tag_artistsort || null,
|
||||
tagGenre: payload.file_tag_genre || null,
|
||||
tagTitle: payload.file_tag_title || null,
|
||||
tagTitleSort: payload.file_tag_titlesort || null,
|
||||
tagSeries: payload.file_tag_series || null,
|
||||
tagSeriesPart: payload.file_tag_seriespart || null,
|
||||
tagTrack: payload.file_tag_track || null,
|
||||
|
|
@ -185,6 +206,9 @@ class AudioMetaTags {
|
|||
tagIsbn: payload.file_tag_isbn || null,
|
||||
tagLanguage: payload.file_tag_language || null,
|
||||
tagASIN: payload.file_tag_asin || null,
|
||||
tagItunesId: payload.file_tag_itunesid || null,
|
||||
tagPodcastType: payload.file_tag_podcasttype || null,
|
||||
tagEpisodeType: payload.file_tag_episodetype || null,
|
||||
tagOverdriveMediaMarker: payload.file_tag_overdrive_media_marker || null,
|
||||
tagOriginalYear: payload.file_tag_originalyear || null,
|
||||
tagReleaseCountry: payload.file_tag_releasecountry || null,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class BookMetadata {
|
|||
this.asin = null
|
||||
this.language = null
|
||||
this.explicit = false
|
||||
this.abridged = false
|
||||
|
||||
if (metadata) {
|
||||
this.construct(metadata)
|
||||
|
|
@ -26,9 +27,9 @@ class BookMetadata {
|
|||
construct(metadata) {
|
||||
this.title = metadata.title
|
||||
this.subtitle = metadata.subtitle
|
||||
this.authors = (metadata.authors && metadata.authors.map) ? metadata.authors.map(a => ({ ...a })) : []
|
||||
this.narrators = metadata.narrators ? [...metadata.narrators] : []
|
||||
this.series = (metadata.series && metadata.series.map) ? metadata.series.map(s => ({ ...s })) : []
|
||||
this.authors = (metadata.authors?.map) ? metadata.authors.map(a => ({ ...a })) : []
|
||||
this.narrators = metadata.narrators ? [...metadata.narrators].filter(n => n) : []
|
||||
this.series = (metadata.series?.map) ? metadata.series.map(s => ({ ...s })) : []
|
||||
this.genres = metadata.genres ? [...metadata.genres] : []
|
||||
this.publishedYear = metadata.publishedYear || null
|
||||
this.publishedDate = metadata.publishedDate || null
|
||||
|
|
@ -38,6 +39,7 @@ class BookMetadata {
|
|||
this.asin = metadata.asin
|
||||
this.language = metadata.language
|
||||
this.explicit = !!metadata.explicit
|
||||
this.abridged = !!metadata.abridged
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
|
@ -55,7 +57,8 @@ class BookMetadata {
|
|||
isbn: this.isbn,
|
||||
asin: this.asin,
|
||||
language: this.language,
|
||||
explicit: this.explicit
|
||||
explicit: this.explicit,
|
||||
abridged: this.abridged
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +79,8 @@ class BookMetadata {
|
|||
isbn: this.isbn,
|
||||
asin: this.asin,
|
||||
language: this.language,
|
||||
explicit: this.explicit
|
||||
explicit: this.explicit,
|
||||
abridged: this.abridged
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,10 +104,21 @@ class BookMetadata {
|
|||
authorName: this.authorName,
|
||||
authorNameLF: this.authorNameLF,
|
||||
narratorName: this.narratorName,
|
||||
seriesName: this.seriesName
|
||||
seriesName: this.seriesName,
|
||||
abridged: this.abridged
|
||||
}
|
||||
}
|
||||
|
||||
toJSONForMetadataFile() {
|
||||
const json = this.toJSON()
|
||||
json.authors = json.authors.map(au => au.name)
|
||||
json.series = json.series.map(se => {
|
||||
if (!se.sequence) return se.name
|
||||
return `${se.name} #${se.sequence}`
|
||||
})
|
||||
return json
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new BookMetadata(this.toJSON())
|
||||
}
|
||||
|
|
@ -209,8 +224,9 @@ class BookMetadata {
|
|||
}
|
||||
}
|
||||
update(payload) {
|
||||
var json = this.toJSON()
|
||||
var hasUpdates = false
|
||||
const json = this.toJSON()
|
||||
let hasUpdates = false
|
||||
|
||||
for (const key in json) {
|
||||
if (payload[key] !== undefined) {
|
||||
if (!areEquivalent(payload[key], json[key])) {
|
||||
|
|
@ -239,6 +255,32 @@ class BookMetadata {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Update narrator name if narrator is in book
|
||||
* @param {String} oldNarratorName - Narrator name to get updated
|
||||
* @param {String} newNarratorName - Updated narrator name
|
||||
* @return {Boolean} True if narrator was updated
|
||||
*/
|
||||
updateNarrator(oldNarratorName, newNarratorName) {
|
||||
if (!this.hasNarrator(oldNarratorName)) return false
|
||||
this.narrators = this.narrators.filter(n => n !== oldNarratorName)
|
||||
if (newNarratorName && !this.hasNarrator(newNarratorName)) {
|
||||
this.narrators.push(newNarratorName)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove narrator name if narrator is in book
|
||||
* @param {String} narratorName - Narrator name to remove
|
||||
* @return {Boolean} True if narrator was updated
|
||||
*/
|
||||
removeNarrator(narratorName) {
|
||||
if (!this.hasNarrator(narratorName)) return false
|
||||
this.narrators = this.narrators.filter(n => n !== narratorName)
|
||||
return true
|
||||
}
|
||||
|
||||
setData(scanMediaData = {}) {
|
||||
this.title = scanMediaData.title || null
|
||||
this.subtitle = scanMediaData.subtitle || null
|
||||
|
|
@ -365,8 +407,10 @@ class BookMetadata {
|
|||
const parsed = parseNameString.parse(authorsTag)
|
||||
if (!parsed) return []
|
||||
return (parsed.names || []).map((au) => {
|
||||
const findAuthor = this.authors.find(_au => _au.name == au)
|
||||
|
||||
return {
|
||||
id: `new-${Math.floor(Math.random() * 1000000)}`,
|
||||
id: findAuthor?.id || `new-${Math.floor(Math.random() * 1000000)}`,
|
||||
name: au
|
||||
}
|
||||
})
|
||||
|
|
@ -399,8 +443,11 @@ class BookMetadata {
|
|||
searchAuthors(query) {
|
||||
return this.authors.filter(au => cleanStringForSearch(au.name).includes(query))
|
||||
}
|
||||
searchNarrators(query) {
|
||||
return this.narrators.filter(n => cleanStringForSearch(n).includes(query))
|
||||
}
|
||||
searchQuery(query) { // Returns key if match is found
|
||||
const keysToCheck = ['title', 'asin', 'isbn']
|
||||
const keysToCheck = ['title', 'asin', 'isbn', 'subtitle']
|
||||
for (const key of keysToCheck) {
|
||||
if (this[key] && cleanStringForSearch(String(this[key])).includes(query)) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class PodcastMetadata {
|
|||
this.itunesArtistId = null
|
||||
this.explicit = false
|
||||
this.language = null
|
||||
this.type = null
|
||||
|
||||
if (metadata) {
|
||||
this.construct(metadata)
|
||||
|
|
@ -34,6 +35,7 @@ class PodcastMetadata {
|
|||
this.itunesArtistId = metadata.itunesArtistId
|
||||
this.explicit = metadata.explicit
|
||||
this.language = metadata.language || null
|
||||
this.type = metadata.type || 'episodic'
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
|
@ -49,7 +51,8 @@ class PodcastMetadata {
|
|||
itunesId: this.itunesId,
|
||||
itunesArtistId: this.itunesArtistId,
|
||||
explicit: this.explicit,
|
||||
language: this.language
|
||||
language: this.language,
|
||||
type: this.type
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +70,8 @@ class PodcastMetadata {
|
|||
itunesId: this.itunesId,
|
||||
itunesArtistId: this.itunesArtistId,
|
||||
explicit: this.explicit,
|
||||
language: this.language
|
||||
language: this.language,
|
||||
type: this.type
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +116,7 @@ class PodcastMetadata {
|
|||
this.itunesArtistId = mediaMetadata.itunesArtistId || null
|
||||
this.explicit = !!mediaMetadata.explicit
|
||||
this.language = mediaMetadata.language || null
|
||||
this.type = mediaMetadata.type || null
|
||||
if (mediaMetadata.genres && mediaMetadata.genres.length) {
|
||||
this.genres = [...mediaMetadata.genres]
|
||||
}
|
||||
|
|
@ -131,5 +136,74 @@ class PodcastMetadata {
|
|||
}
|
||||
return hasUpdates
|
||||
}
|
||||
|
||||
setDataFromAudioMetaTags(audioFileMetaTags, overrideExistingDetails = false) {
|
||||
const MetadataMapArray = [
|
||||
{
|
||||
tag: 'tagAlbum',
|
||||
altTag: 'tagSeries',
|
||||
key: 'title'
|
||||
},
|
||||
{
|
||||
tag: 'tagArtist',
|
||||
key: 'author'
|
||||
},
|
||||
{
|
||||
tag: 'tagGenre',
|
||||
key: 'genres'
|
||||
},
|
||||
{
|
||||
tag: 'tagLanguage',
|
||||
key: 'language'
|
||||
},
|
||||
{
|
||||
tag: 'tagItunesId',
|
||||
key: 'itunesId'
|
||||
},
|
||||
{
|
||||
tag: 'tagPodcastType',
|
||||
key: 'type',
|
||||
}
|
||||
]
|
||||
|
||||
const updatePayload = {}
|
||||
|
||||
MetadataMapArray.forEach((mapping) => {
|
||||
let value = audioFileMetaTags[mapping.tag]
|
||||
let tagToUse = mapping.tag
|
||||
if (!value && mapping.altTag) {
|
||||
value = audioFileMetaTags[mapping.altTag]
|
||||
tagToUse = mapping.altTag
|
||||
}
|
||||
|
||||
if (value && typeof value === 'string') {
|
||||
value = value.trim() // Trim whitespace
|
||||
|
||||
if (mapping.key === 'genres' && (!this.genres.length || overrideExistingDetails)) {
|
||||
updatePayload.genres = this.parseGenresTag(value)
|
||||
Logger.debug(`[Podcast] Mapping metadata to key ${tagToUse} => ${mapping.key}: ${updatePayload.genres.join(', ')}`)
|
||||
} else if (!this[mapping.key] || overrideExistingDetails) {
|
||||
updatePayload[mapping.key] = value
|
||||
Logger.debug(`[Podcast] Mapping metadata to key ${tagToUse} => ${mapping.key}: ${updatePayload[mapping.key]}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (Object.keys(updatePayload).length) {
|
||||
return this.update(updatePayload)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
module.exports = PodcastMetadata
|
||||
module.exports = PodcastMetadata
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue