mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-12 08:58:42 +01:00
Remove unused old model functions
This commit is contained in:
parent
4a5f534a65
commit
920ddf43d7
@ -153,7 +153,6 @@ class Server {
|
||||
// Static folder
|
||||
router.use(express.static(Path.join(global.appRoot, 'static')))
|
||||
|
||||
// router.use('/api/v1', routes) // TODO: New routes
|
||||
router.use('/api', this.authMiddleware.bind(this), this.apiRouter.router)
|
||||
router.use('/hls', this.authMiddleware.bind(this), this.hlsRouter.router)
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
const itemDb = require('../db/item.db')
|
||||
|
||||
const getLibraryItem = async (req, res) => {
|
||||
let libraryItem = null
|
||||
if (req.query.expanded == 1) {
|
||||
libraryItem = await itemDb.getLibraryItemExpanded(req.params.id)
|
||||
} else {
|
||||
libraryItem = await itemDb.getLibraryItemMinified(req.params.id)
|
||||
}
|
||||
|
||||
res.json(libraryItem)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getLibraryItem
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/**
|
||||
* TODO: Unused for testing
|
||||
*/
|
||||
const { Sequelize } = require('sequelize')
|
||||
const Database = require('../Database')
|
||||
|
||||
const getLibraryItemMinified = (libraryItemId) => {
|
||||
return Database.libraryItemModel.findByPk(libraryItemId, {
|
||||
include: [
|
||||
{
|
||||
model: Database.bookModel,
|
||||
attributes: [
|
||||
'id', 'title', 'subtitle', 'publishedYear', 'publishedDate', 'publisher', 'description', 'isbn', 'asin', 'language', 'explicit', 'narrators', 'coverPath', 'genres', 'tags'
|
||||
],
|
||||
include: [
|
||||
{
|
||||
model: Database.authorModel,
|
||||
attributes: ['id', 'name'],
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.seriesModel,
|
||||
attributes: ['id', 'name'],
|
||||
through: {
|
||||
attributes: ['sequence']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: Database.podcastModel,
|
||||
attributes: [
|
||||
'id', 'title', 'author', 'releaseDate', 'feedURL', 'imageURL', 'description', 'itunesPageURL', 'itunesId', 'itunesArtistId', 'language', 'podcastType', 'explicit', 'autoDownloadEpisodes', 'genres', 'tags',
|
||||
[Sequelize.literal('(SELECT COUNT(*) FROM "podcastEpisodes" WHERE "podcastEpisodes"."podcastId" = podcast.id)'), 'numPodcastEpisodes']
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
const getLibraryItemExpanded = (libraryItemId) => {
|
||||
return Database.libraryItemModel.findByPk(libraryItemId, {
|
||||
include: [
|
||||
{
|
||||
model: Database.bookModel,
|
||||
include: [
|
||||
{
|
||||
model: Database.authorModel,
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
},
|
||||
{
|
||||
model: Database.seriesModel,
|
||||
through: {
|
||||
attributes: ['sequence']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
model: Database.podcastModel,
|
||||
include: [
|
||||
{
|
||||
model: Database.podcastEpisodeModel
|
||||
}
|
||||
]
|
||||
},
|
||||
'libraryFolder',
|
||||
'library'
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getLibraryItemMinified,
|
||||
getLibraryItemExpanded
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
const uuidv4 = require("uuid").v4
|
||||
const fs = require('../libs/fsExtra')
|
||||
const Path = require('path')
|
||||
const { version } = require('../../package.json')
|
||||
const Logger = require('../Logger')
|
||||
const abmetadataGenerator = require('../utils/generators/abmetadataGenerator')
|
||||
const LibraryFile = require('./files/LibraryFile')
|
||||
@ -9,7 +8,7 @@ const Book = require('./mediaTypes/Book')
|
||||
const Podcast = require('./mediaTypes/Podcast')
|
||||
const Video = require('./mediaTypes/Video')
|
||||
const Music = require('./mediaTypes/Music')
|
||||
const { areEquivalent, copyValue, cleanStringForSearch } = require('../utils/index')
|
||||
const { areEquivalent, copyValue } = require('../utils/index')
|
||||
const { filePathToPOSIX, getFileTimestampsWithIno } = require('../utils/fileUtils')
|
||||
|
||||
class LibraryItem {
|
||||
@ -180,34 +179,23 @@ class LibraryItem {
|
||||
this.libraryFiles.forEach((lf) => total += lf.metadata.size)
|
||||
return total
|
||||
}
|
||||
get audioFileTotalSize() {
|
||||
let total = 0
|
||||
this.libraryFiles.filter(lf => lf.fileType == 'audio').forEach((lf) => total += lf.metadata.size)
|
||||
return total
|
||||
}
|
||||
get hasAudioFiles() {
|
||||
return this.libraryFiles.some(lf => lf.fileType === 'audio')
|
||||
}
|
||||
get hasMediaEntities() {
|
||||
return this.media.hasMediaEntities
|
||||
}
|
||||
get hasIssues() {
|
||||
if (this.isMissing || this.isInvalid) return true
|
||||
return this.media.hasIssues
|
||||
}
|
||||
|
||||
// Data comes from scandir library item data
|
||||
// TODO: Remove this function. Only used when creating a new podcast now
|
||||
setData(libraryMediaType, payload) {
|
||||
this.id = uuidv4()
|
||||
this.mediaType = libraryMediaType
|
||||
if (libraryMediaType === 'video') {
|
||||
this.media = new Video()
|
||||
} else if (libraryMediaType === 'podcast') {
|
||||
if (libraryMediaType === 'podcast') {
|
||||
this.media = new Podcast()
|
||||
} else if (libraryMediaType === 'book') {
|
||||
this.media = new Book()
|
||||
} else if (libraryMediaType === 'music') {
|
||||
this.media = new Music()
|
||||
} else {
|
||||
Logger.error(`[LibraryItem] setData called with unsupported media type "${libraryMediaType}"`)
|
||||
return
|
||||
}
|
||||
this.media.id = uuidv4()
|
||||
this.media.libraryItemId = this.id
|
||||
@ -270,85 +258,13 @@ class LibraryItem {
|
||||
this.updatedAt = Date.now()
|
||||
}
|
||||
|
||||
setInvalid() {
|
||||
this.isInvalid = true
|
||||
this.updatedAt = Date.now()
|
||||
}
|
||||
|
||||
setLastScan() {
|
||||
this.lastScan = Date.now()
|
||||
this.updatedAt = Date.now()
|
||||
this.scanVersion = version
|
||||
}
|
||||
|
||||
// Returns null if file not found, true if file was updated, false if up to date
|
||||
// updates existing LibraryFile, AudioFile, EBookFile's
|
||||
checkFileFound(fileFound) {
|
||||
let hasUpdated = false
|
||||
|
||||
let existingFile = this.libraryFiles.find(lf => lf.ino === fileFound.ino)
|
||||
let mediaFile = null
|
||||
if (!existingFile) {
|
||||
existingFile = this.libraryFiles.find(lf => lf.metadata.path === fileFound.metadata.path)
|
||||
if (existingFile) {
|
||||
// Update media file ino
|
||||
mediaFile = this.media.findFileWithInode(existingFile.ino)
|
||||
if (mediaFile) {
|
||||
mediaFile.ino = fileFound.ino
|
||||
}
|
||||
|
||||
// file inode was updated
|
||||
existingFile.ino = fileFound.ino
|
||||
hasUpdated = true
|
||||
} else {
|
||||
// file not found
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
mediaFile = this.media.findFileWithInode(existingFile.ino)
|
||||
}
|
||||
|
||||
if (existingFile.metadata.path !== fileFound.metadata.path) {
|
||||
existingFile.metadata.path = fileFound.metadata.path
|
||||
existingFile.metadata.relPath = fileFound.metadata.relPath
|
||||
if (mediaFile) {
|
||||
mediaFile.metadata.path = fileFound.metadata.path
|
||||
mediaFile.metadata.relPath = fileFound.metadata.relPath
|
||||
}
|
||||
hasUpdated = true
|
||||
}
|
||||
|
||||
// FileMetadata keys
|
||||
['filename', 'ext', 'mtimeMs', 'ctimeMs', 'birthtimeMs', 'size'].forEach((key) => {
|
||||
if (existingFile.metadata[key] !== fileFound.metadata[key]) {
|
||||
// Add modified flag on file data object if exists and was changed
|
||||
if (key === 'mtimeMs' && existingFile.metadata[key]) {
|
||||
fileFound.metadata.wasModified = true
|
||||
}
|
||||
|
||||
existingFile.metadata[key] = fileFound.metadata[key]
|
||||
if (mediaFile) {
|
||||
if (key === 'mtimeMs') mediaFile.metadata.wasModified = true
|
||||
mediaFile.metadata[key] = fileFound.metadata[key]
|
||||
}
|
||||
hasUpdated = true
|
||||
}
|
||||
})
|
||||
|
||||
return hasUpdated
|
||||
}
|
||||
|
||||
searchQuery(query) {
|
||||
query = cleanStringForSearch(query)
|
||||
return this.media.searchQuery(query)
|
||||
}
|
||||
|
||||
getDirectPlayTracklist(episodeId) {
|
||||
return this.media.getDirectPlayTracklist(episodeId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Save metadata.json/metadata.abs file
|
||||
* TODO: Move to new LibraryItem model
|
||||
* @returns {Promise<LibraryFile>} null if not saved
|
||||
*/
|
||||
async saveMetadata() {
|
||||
|
@ -1,7 +1,5 @@
|
||||
const uuidv4 = require("uuid").v4
|
||||
const Path = require('path')
|
||||
const Logger = require('../../Logger')
|
||||
const { cleanStringForSearch, areEquivalent, copyValue } = require('../../utils/index')
|
||||
const { areEquivalent, copyValue } = require('../../utils/index')
|
||||
const AudioFile = require('../files/AudioFile')
|
||||
const AudioTrack = require('../files/AudioTrack')
|
||||
|
||||
@ -146,19 +144,6 @@ class PodcastEpisode {
|
||||
this.updatedAt = Date.now()
|
||||
}
|
||||
|
||||
setDataFromAudioFile(audioFile, index) {
|
||||
this.id = uuidv4()
|
||||
this.audioFile = audioFile
|
||||
this.title = Path.basename(audioFile.metadata.filename, Path.extname(audioFile.metadata.filename))
|
||||
this.index = index
|
||||
|
||||
this.setDataFromAudioMetaTags(audioFile.metaTags, true)
|
||||
|
||||
this.chapters = audioFile.chapters?.map((c) => ({ ...c }))
|
||||
this.addedAt = Date.now()
|
||||
this.updatedAt = Date.now()
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
let hasUpdates = false
|
||||
for (const key in this.toJSON()) {
|
||||
@ -192,80 +177,5 @@ class PodcastEpisode {
|
||||
if (!this.enclosure || !this.enclosure.url) return false
|
||||
return this.enclosure.url == url
|
||||
}
|
||||
|
||||
searchQuery(query) {
|
||||
return cleanStringForSearch(this.title).includes(query)
|
||||
}
|
||||
|
||||
setDataFromAudioMetaTags(audioFileMetaTags, overrideExistingDetails = false) {
|
||||
if (!audioFileMetaTags) return false
|
||||
|
||||
const MetadataMapArray = [
|
||||
{
|
||||
tag: 'tagComment',
|
||||
altTag: 'tagSubtitle',
|
||||
key: 'description'
|
||||
},
|
||||
{
|
||||
tag: 'tagSubtitle',
|
||||
key: 'subtitle'
|
||||
},
|
||||
{
|
||||
tag: 'tagDate',
|
||||
key: 'pubDate'
|
||||
},
|
||||
{
|
||||
tag: 'tagDisc',
|
||||
key: 'season',
|
||||
},
|
||||
{
|
||||
tag: 'tagTrack',
|
||||
altTag: 'tagSeriesPart',
|
||||
key: 'episode'
|
||||
},
|
||||
{
|
||||
tag: 'tagTitle',
|
||||
key: 'title'
|
||||
},
|
||||
{
|
||||
tag: 'tagEpisodeType',
|
||||
key: 'episodeType'
|
||||
}
|
||||
]
|
||||
|
||||
MetadataMapArray.forEach((mapping) => {
|
||||
let value = audioFileMetaTags[mapping.tag]
|
||||
let tagToUse = mapping.tag
|
||||
if (!value && mapping.altTag) {
|
||||
tagToUse = mapping.altTag
|
||||
value = audioFileMetaTags[mapping.altTag]
|
||||
}
|
||||
|
||||
if (value && typeof value === 'string') {
|
||||
value = value.trim() // Trim whitespace
|
||||
|
||||
if (mapping.key === 'pubDate' && (!this.pubDate || overrideExistingDetails)) {
|
||||
const pubJsDate = new Date(value)
|
||||
if (pubJsDate && !isNaN(pubJsDate)) {
|
||||
this.publishedAt = pubJsDate.valueOf()
|
||||
this.pubDate = value
|
||||
Logger.debug(`[PodcastEpisode] Mapping metadata to key ${tagToUse} => ${mapping.key}: ${this[mapping.key]}`)
|
||||
} else {
|
||||
Logger.warn(`[PodcastEpisode] Mapping pubDate with tag ${tagToUse} has invalid date "${value}"`)
|
||||
}
|
||||
} else if (mapping.key === 'episodeType' && (!this.episodeType || overrideExistingDetails)) {
|
||||
if (['full', 'trailer', 'bonus'].includes(value)) {
|
||||
this.episodeType = value
|
||||
Logger.debug(`[PodcastEpisode] Mapping metadata to key ${tagToUse} => ${mapping.key}: ${this[mapping.key]}`)
|
||||
} else {
|
||||
Logger.warn(`[PodcastEpisode] Mapping episodeType with invalid value "${value}". Must be one of [full, trailer, bonus].`)
|
||||
}
|
||||
} else if (!this[mapping.key] || overrideExistingDetails) {
|
||||
this[mapping.key] = value
|
||||
Logger.debug(`[PodcastEpisode] Mapping metadata to key ${tagToUse} => ${mapping.key}: ${this[mapping.key]}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = PodcastEpisode
|
||||
|
@ -1,9 +1,7 @@
|
||||
const Logger = require('../../Logger')
|
||||
const BookMetadata = require('../metadata/BookMetadata')
|
||||
const { areEquivalent, copyValue, cleanStringForSearch } = require('../../utils/index')
|
||||
const { parseOpfMetadataXML } = require('../../utils/parsers/parseOpfMetadata')
|
||||
const abmetadataGenerator = require('../../utils/generators/abmetadataGenerator')
|
||||
const { readTextFile, filePathToPOSIX } = require('../../utils/fileUtils')
|
||||
const { areEquivalent, copyValue } = require('../../utils/index')
|
||||
const { filePathToPOSIX } = require('../../utils/fileUtils')
|
||||
const AudioFile = require('../files/AudioFile')
|
||||
const AudioTrack = require('../files/AudioTrack')
|
||||
const EBookFile = require('../files/EBookFile')
|
||||
@ -111,23 +109,12 @@ class Book {
|
||||
get hasMediaEntities() {
|
||||
return !!this.tracks.length || this.ebookFile
|
||||
}
|
||||
get shouldSearchForCover() {
|
||||
if (this.coverPath) return false
|
||||
if (!this.lastCoverSearch || this.metadata.coverSearchQuery !== this.lastCoverSearchQuery) return true
|
||||
return (Date.now() - this.lastCoverSearch) > 1000 * 60 * 60 * 24 * 7 // 7 day
|
||||
}
|
||||
get hasEmbeddedCoverArt() {
|
||||
return this.audioFiles.some(af => af.embeddedCoverArt)
|
||||
}
|
||||
get invalidAudioFiles() {
|
||||
return this.audioFiles.filter(af => af.invalid)
|
||||
}
|
||||
get includedAudioFiles() {
|
||||
return this.audioFiles.filter(af => !af.exclude && !af.invalid)
|
||||
}
|
||||
get hasIssues() {
|
||||
return this.missingParts.length || this.invalidAudioFiles.length
|
||||
}
|
||||
get tracks() {
|
||||
let startOffset = 0
|
||||
return this.includedAudioFiles.map((af) => {
|
||||
@ -226,57 +213,6 @@ class Book {
|
||||
return null
|
||||
}
|
||||
|
||||
updateLastCoverSearch(coverWasFound) {
|
||||
this.lastCoverSearch = coverWasFound ? null : Date.now()
|
||||
this.lastCoverSearchQuery = coverWasFound ? null : this.metadata.coverSearchQuery
|
||||
}
|
||||
|
||||
// Audio file metadata tags map to book details (will not overwrite)
|
||||
setMetadataFromAudioFile(overrideExistingDetails = false) {
|
||||
if (!this.audioFiles.length) return false
|
||||
var audioFile = this.audioFiles[0]
|
||||
if (!audioFile.metaTags) return false
|
||||
return this.metadata.setDataFromAudioMetaTags(audioFile.metaTags, overrideExistingDetails)
|
||||
}
|
||||
|
||||
setData(mediaPayload) {
|
||||
this.metadata = new BookMetadata()
|
||||
if (mediaPayload.metadata) {
|
||||
this.metadata.setData(mediaPayload.metadata)
|
||||
}
|
||||
}
|
||||
|
||||
searchQuery(query) {
|
||||
const payload = {
|
||||
tags: this.tags.filter(t => cleanStringForSearch(t).includes(query)),
|
||||
series: this.metadata.searchSeries(query),
|
||||
authors: this.metadata.searchAuthors(query),
|
||||
narrators: this.metadata.searchNarrators(query),
|
||||
matchKey: null,
|
||||
matchText: null
|
||||
}
|
||||
const metadataMatch = this.metadata.searchQuery(query)
|
||||
if (metadataMatch) {
|
||||
payload.matchKey = metadataMatch.matchKey
|
||||
payload.matchText = metadataMatch.matchText
|
||||
} else {
|
||||
if (payload.authors.length) {
|
||||
payload.matchKey = 'authors'
|
||||
payload.matchText = this.metadata.authorName
|
||||
} else if (payload.series.length) {
|
||||
payload.matchKey = 'series'
|
||||
payload.matchText = this.metadata.seriesName
|
||||
} else if (payload.tags.length) {
|
||||
payload.matchKey = 'tags'
|
||||
payload.matchText = this.tags.join(', ')
|
||||
} else if (payload.narrators.length) {
|
||||
payload.matchKey = 'narrators'
|
||||
payload.matchText = this.metadata.narratorName
|
||||
}
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the EBookFile from a LibraryFile
|
||||
* If null then ebookFile will be removed from the book
|
||||
|
@ -65,15 +65,6 @@ class Music {
|
||||
get hasMediaEntities() {
|
||||
return !!this.audioFile
|
||||
}
|
||||
get shouldSearchForCover() {
|
||||
return false
|
||||
}
|
||||
get hasEmbeddedCoverArt() {
|
||||
return this.audioFile.embeddedCoverArt
|
||||
}
|
||||
get hasIssues() {
|
||||
return false
|
||||
}
|
||||
get duration() {
|
||||
return this.audioFile.duration || 0
|
||||
}
|
||||
@ -134,16 +125,6 @@ class Music {
|
||||
this.audioFile = audioFile
|
||||
}
|
||||
|
||||
setMetadataFromAudioFile(overrideExistingDetails = false) {
|
||||
if (!this.audioFile) return false
|
||||
if (!this.audioFile.metaTags) return false
|
||||
return this.metadata.setDataFromAudioMetaTags(this.audioFile.metaTags, overrideExistingDetails)
|
||||
}
|
||||
|
||||
searchQuery(query) {
|
||||
return {}
|
||||
}
|
||||
|
||||
// Only checks container format
|
||||
checkCanDirectPlay(payload) {
|
||||
return true
|
||||
|
@ -1,9 +1,8 @@
|
||||
const Logger = require('../../Logger')
|
||||
const PodcastEpisode = require('../entities/PodcastEpisode')
|
||||
const PodcastMetadata = require('../metadata/PodcastMetadata')
|
||||
const { areEquivalent, copyValue, cleanStringForSearch } = require('../../utils/index')
|
||||
const abmetadataGenerator = require('../../utils/generators/abmetadataGenerator')
|
||||
const { readTextFile, filePathToPOSIX } = require('../../utils/fileUtils')
|
||||
const { areEquivalent, copyValue } = require('../../utils/index')
|
||||
const { filePathToPOSIX } = require('../../utils/fileUtils')
|
||||
|
||||
class Podcast {
|
||||
constructor(podcast) {
|
||||
@ -110,15 +109,6 @@ class Podcast {
|
||||
get hasMediaEntities() {
|
||||
return !!this.episodes.length
|
||||
}
|
||||
get shouldSearchForCover() {
|
||||
return false
|
||||
}
|
||||
get hasEmbeddedCoverArt() {
|
||||
return this.episodes.some(ep => ep.audioFile.embeddedCoverArt)
|
||||
}
|
||||
get hasIssues() {
|
||||
return false
|
||||
}
|
||||
get duration() {
|
||||
let total = 0
|
||||
this.episodes.forEach((ep) => total += ep.duration)
|
||||
@ -187,10 +177,6 @@ class Podcast {
|
||||
return null
|
||||
}
|
||||
|
||||
findEpisodeWithInode(inode) {
|
||||
return this.episodes.find(ep => ep.audioFile.ino === inode)
|
||||
}
|
||||
|
||||
setData(mediaData) {
|
||||
this.metadata = new PodcastMetadata()
|
||||
if (mediaData.metadata) {
|
||||
@ -203,31 +189,6 @@ class Podcast {
|
||||
this.lastEpisodeCheck = Date.now() // Makes sure new episodes are after this
|
||||
}
|
||||
|
||||
searchEpisodes(query) {
|
||||
return this.episodes.filter(ep => ep.searchQuery(query))
|
||||
}
|
||||
|
||||
searchQuery(query) {
|
||||
const payload = {
|
||||
tags: this.tags.filter(t => cleanStringForSearch(t).includes(query)),
|
||||
matchKey: null,
|
||||
matchText: null
|
||||
}
|
||||
const metadataMatch = this.metadata.searchQuery(query)
|
||||
if (metadataMatch) {
|
||||
payload.matchKey = metadataMatch.matchKey
|
||||
payload.matchText = metadataMatch.matchText
|
||||
} else {
|
||||
const matchingEpisodes = this.searchEpisodes(query)
|
||||
if (matchingEpisodes.length) {
|
||||
payload.matchKey = 'episode'
|
||||
payload.matchText = matchingEpisodes[0].title
|
||||
}
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
checkHasEpisode(episodeId) {
|
||||
return this.episodes.some(ep => ep.id === episodeId)
|
||||
}
|
||||
@ -294,14 +255,6 @@ class Podcast {
|
||||
return this.episodes.find(ep => ep.id == episodeId)
|
||||
}
|
||||
|
||||
// Audio file metadata tags map to podcast details
|
||||
setMetadataFromAudioFile(overrideExistingDetails = false) {
|
||||
if (!this.episodes.length) return false
|
||||
const audioFile = this.episodes[0].audioFile
|
||||
if (!audioFile?.metaTags) return false
|
||||
return this.metadata.setDataFromAudioMetaTags(audioFile.metaTags, overrideExistingDetails)
|
||||
}
|
||||
|
||||
getChapters(episodeId) {
|
||||
return this.getEpisode(episodeId)?.chapters?.map(ch => ({ ...ch })) || []
|
||||
}
|
||||
|
@ -69,15 +69,6 @@ class Video {
|
||||
get hasMediaEntities() {
|
||||
return true
|
||||
}
|
||||
get shouldSearchForCover() {
|
||||
return false
|
||||
}
|
||||
get hasEmbeddedCoverArt() {
|
||||
return false
|
||||
}
|
||||
get hasIssues() {
|
||||
return false
|
||||
}
|
||||
get duration() {
|
||||
return 0
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
const Logger = require('../../Logger')
|
||||
const { areEquivalent, copyValue, cleanStringForSearch, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
||||
const { areEquivalent, copyValue, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
||||
const parseNameString = require('../../utils/parsers/parseNameString')
|
||||
class BookMetadata {
|
||||
constructor(metadata) {
|
||||
@ -144,20 +144,6 @@ class BookMetadata {
|
||||
return `${se.name} #${se.sequence}`
|
||||
}).join(', ')
|
||||
}
|
||||
get seriesNameIgnorePrefix() {
|
||||
if (!this.series.length) return ''
|
||||
return this.series.map(se => {
|
||||
if (!se.sequence) return getTitleIgnorePrefix(se.name)
|
||||
return `${getTitleIgnorePrefix(se.name)} #${se.sequence}`
|
||||
}).join(', ')
|
||||
}
|
||||
get seriesNamePrefixAtEnd() {
|
||||
if (!this.series.length) return ''
|
||||
return this.series.map(se => {
|
||||
if (!se.sequence) return getTitlePrefixAtEnd(se.name)
|
||||
return `${getTitlePrefixAtEnd(se.name)} #${se.sequence}`
|
||||
}).join(', ')
|
||||
}
|
||||
get firstSeriesName() {
|
||||
if (!this.series.length) return ''
|
||||
return this.series[0].name
|
||||
@ -169,36 +155,15 @@ class BookMetadata {
|
||||
get narratorName() {
|
||||
return this.narrators.join(', ')
|
||||
}
|
||||
get coverSearchQuery() {
|
||||
if (!this.authorName) return this.title
|
||||
return this.title + '&' + this.authorName
|
||||
}
|
||||
|
||||
hasAuthor(id) {
|
||||
return !!this.authors.find(au => au.id == id)
|
||||
}
|
||||
hasSeries(seriesId) {
|
||||
return !!this.series.find(se => se.id == seriesId)
|
||||
}
|
||||
hasNarrator(narratorName) {
|
||||
return this.narrators.includes(narratorName)
|
||||
}
|
||||
getSeries(seriesId) {
|
||||
return this.series.find(se => se.id == seriesId)
|
||||
}
|
||||
getFirstSeries() {
|
||||
return this.series.length ? this.series[0] : null
|
||||
}
|
||||
getSeriesSequence(seriesId) {
|
||||
const series = this.series.find(se => se.id == seriesId)
|
||||
if (!series) return null
|
||||
return series.sequence || ''
|
||||
}
|
||||
getSeriesSortTitle(series) {
|
||||
if (!series) return ''
|
||||
if (!series.sequence) return series.name
|
||||
return `${series.name} #${series.sequence}`
|
||||
}
|
||||
|
||||
update(payload) {
|
||||
const json = this.toJSON()
|
||||
@ -231,205 +196,5 @@ class BookMetadata {
|
||||
name: newAuthor.name
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
this.narrators = this.parseNarratorsTag(scanMediaData.narrators)
|
||||
this.publishedYear = scanMediaData.publishedYear || null
|
||||
this.description = scanMediaData.description || null
|
||||
this.isbn = scanMediaData.isbn || null
|
||||
this.asin = scanMediaData.asin || null
|
||||
this.language = scanMediaData.language || null
|
||||
this.genres = []
|
||||
this.explicit = !!scanMediaData.explicit
|
||||
|
||||
if (scanMediaData.author) {
|
||||
this.authors = this.parseAuthorsTag(scanMediaData.author)
|
||||
}
|
||||
if (scanMediaData.series) {
|
||||
this.series = this.parseSeriesTag(scanMediaData.series, scanMediaData.sequence)
|
||||
}
|
||||
}
|
||||
|
||||
setDataFromAudioMetaTags(audioFileMetaTags, overrideExistingDetails = false) {
|
||||
const MetadataMapArray = [
|
||||
{
|
||||
tag: 'tagComposer',
|
||||
key: 'narrators'
|
||||
},
|
||||
{
|
||||
tag: 'tagDescription',
|
||||
altTag: 'tagComment',
|
||||
key: 'description'
|
||||
},
|
||||
{
|
||||
tag: 'tagPublisher',
|
||||
key: 'publisher'
|
||||
},
|
||||
{
|
||||
tag: 'tagDate',
|
||||
key: 'publishedYear'
|
||||
},
|
||||
{
|
||||
tag: 'tagSubtitle',
|
||||
key: 'subtitle'
|
||||
},
|
||||
{
|
||||
tag: 'tagAlbum',
|
||||
altTag: 'tagTitle',
|
||||
key: 'title',
|
||||
},
|
||||
{
|
||||
tag: 'tagArtist',
|
||||
altTag: 'tagAlbumArtist',
|
||||
key: 'authors'
|
||||
},
|
||||
{
|
||||
tag: 'tagGenre',
|
||||
key: 'genres'
|
||||
},
|
||||
{
|
||||
tag: 'tagSeries',
|
||||
key: 'series'
|
||||
},
|
||||
{
|
||||
tag: 'tagIsbn',
|
||||
key: 'isbn'
|
||||
},
|
||||
{
|
||||
tag: 'tagLanguage',
|
||||
key: 'language'
|
||||
},
|
||||
{
|
||||
tag: 'tagASIN',
|
||||
key: 'asin'
|
||||
}
|
||||
]
|
||||
|
||||
const updatePayload = {}
|
||||
|
||||
// Metadata is only mapped to the book if it is empty
|
||||
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 === 'narrators' && (!this.narrators.length || overrideExistingDetails)) {
|
||||
updatePayload.narrators = this.parseNarratorsTag(value)
|
||||
} else if (mapping.key === 'authors' && (!this.authors.length || overrideExistingDetails)) {
|
||||
updatePayload.authors = this.parseAuthorsTag(value)
|
||||
} else if (mapping.key === 'genres' && (!this.genres.length || overrideExistingDetails)) {
|
||||
updatePayload.genres = this.parseGenresTag(value)
|
||||
} else if (mapping.key === 'series' && (!this.series.length || overrideExistingDetails)) {
|
||||
const sequenceTag = audioFileMetaTags.tagSeriesPart || null
|
||||
updatePayload.series = this.parseSeriesTag(value, sequenceTag)
|
||||
} 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
|
||||
}
|
||||
|
||||
// Returns array of names in First Last format
|
||||
parseNarratorsTag(narratorsTag) {
|
||||
const parsed = parseNameString.parse(narratorsTag)
|
||||
return parsed ? parsed.names : []
|
||||
}
|
||||
|
||||
// Return array of authors minified with placeholder id
|
||||
parseAuthorsTag(authorsTag) {
|
||||
const parsed = parseNameString.parse(authorsTag)
|
||||
if (!parsed) return []
|
||||
return (parsed.names || []).map((au) => {
|
||||
const findAuthor = this.authors.find(_au => _au.name == au)
|
||||
|
||||
return {
|
||||
id: findAuthor?.id || `new-${Math.floor(Math.random() * 1000000)}`,
|
||||
name: au
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
// Return array with series with placeholder id
|
||||
parseSeriesTag(seriesTag, sequenceTag) {
|
||||
if (!seriesTag) return []
|
||||
return [{
|
||||
id: `new-${Math.floor(Math.random() * 1000000)}`,
|
||||
name: seriesTag,
|
||||
sequence: sequenceTag || ''
|
||||
}]
|
||||
}
|
||||
|
||||
searchSeries(query) {
|
||||
return this.series.filter(se => cleanStringForSearch(se.name).includes(query))
|
||||
}
|
||||
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', 'subtitle']
|
||||
for (const key of keysToCheck) {
|
||||
if (this[key] && cleanStringForSearch(String(this[key])).includes(query)) {
|
||||
return {
|
||||
matchKey: key,
|
||||
matchText: this[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
module.exports = BookMetadata
|
||||
|
@ -1,5 +1,5 @@
|
||||
const Logger = require('../../Logger')
|
||||
const { areEquivalent, copyValue, cleanStringForSearch, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
||||
const { areEquivalent, copyValue, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
||||
|
||||
class MusicMetadata {
|
||||
constructor(metadata) {
|
||||
@ -133,19 +133,6 @@ class MusicMetadata {
|
||||
return getTitlePrefixAtEnd(this.title)
|
||||
}
|
||||
|
||||
searchQuery(query) { // Returns key if match is found
|
||||
const keysToCheck = ['title', 'album']
|
||||
for (const key of keysToCheck) {
|
||||
if (this[key] && cleanStringForSearch(String(this[key])).includes(query)) {
|
||||
return {
|
||||
matchKey: key,
|
||||
matchText: this[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
setData(mediaMetadata = {}) {
|
||||
this.title = mediaMetadata.title || null
|
||||
this.artist = mediaMetadata.artist || null
|
||||
|
@ -1,5 +1,5 @@
|
||||
const Logger = require('../../Logger')
|
||||
const { areEquivalent, copyValue, cleanStringForSearch, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
||||
const { areEquivalent, copyValue, getTitleIgnorePrefix, getTitlePrefixAtEnd } = require('../../utils/index')
|
||||
|
||||
class PodcastMetadata {
|
||||
constructor(metadata) {
|
||||
@ -91,19 +91,6 @@ class PodcastMetadata {
|
||||
return getTitlePrefixAtEnd(this.title)
|
||||
}
|
||||
|
||||
searchQuery(query) { // Returns key if match is found
|
||||
const keysToCheck = ['title', 'author', 'itunesId', 'itunesArtistId']
|
||||
for (const key of keysToCheck) {
|
||||
if (this[key] && cleanStringForSearch(String(this[key])).includes(query)) {
|
||||
return {
|
||||
matchKey: key,
|
||||
matchText: this[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
setData(mediaMetadata = {}) {
|
||||
this.title = mediaMetadata.title || null
|
||||
this.author = mediaMetadata.author || null
|
||||
@ -136,74 +123,5 @@ 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
|
||||
|
@ -55,19 +55,6 @@ class VideoMetadata {
|
||||
return getTitlePrefixAtEnd(this.title)
|
||||
}
|
||||
|
||||
searchQuery(query) { // Returns key if match is found
|
||||
var keysToCheck = ['title']
|
||||
for (var key of keysToCheck) {
|
||||
if (this[key] && String(this[key]).toLowerCase().includes(query)) {
|
||||
return {
|
||||
matchKey: key,
|
||||
matchText: this[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
setData(mediaMetadata = {}) {
|
||||
this.title = mediaMetadata.title || null
|
||||
this.description = mediaMetadata.description || null
|
||||
|
@ -1,4 +1,3 @@
|
||||
const sequelize = require('sequelize')
|
||||
const express = require('express')
|
||||
const Path = require('path')
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
const express = require('express')
|
||||
const libraries = require('./libraries')
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
router.use('/libraries', libraries)
|
||||
|
||||
module.exports = router
|
@ -1,7 +0,0 @@
|
||||
const express = require('express')
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
// TODO: Add library routes
|
||||
|
||||
module.exports = router
|
Loading…
Reference in New Issue
Block a user