mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-01-02 17:19:36 +00:00
feat: add podcastFilenameFormat
This commit is contained in:
parent
5e5a988f7a
commit
8d988cf353
13 changed files with 132 additions and 13 deletions
|
|
@ -95,7 +95,7 @@ class LibraryController {
|
|||
return res.status(400).send('Invalid request. Settings "metadataPrecedence" must be an array')
|
||||
}
|
||||
newLibraryPayload.settings[key] = [...req.body.settings[key]]
|
||||
} else if (key === 'autoScanCronExpression' || key === 'podcastSearchRegion') {
|
||||
} else if (key === 'autoScanCronExpression' || key === 'podcastSearchRegion' || key === 'podcastFilenameFormat') {
|
||||
if (!req.body.settings[key]) continue
|
||||
if (typeof req.body.settings[key] !== 'string') {
|
||||
return res.status(400).send(`Invalid request. Settings "${key}" must be a string`)
|
||||
|
|
@ -318,7 +318,7 @@ class LibraryController {
|
|||
updatedSettings[key] = [...req.body.settings[key]]
|
||||
Logger.debug(`[LibraryController] Library "${req.library.name}" updating setting "${key}" to "${updatedSettings[key]}"`)
|
||||
}
|
||||
} else if (key === 'autoScanCronExpression' || key === 'podcastSearchRegion') {
|
||||
} else if (key === 'autoScanCronExpression' || key === 'podcastSearchRegion' || key === 'podcastFilenameFormat' ) {
|
||||
if (req.body.settings[key] !== null && typeof req.body.settings[key] !== 'string') {
|
||||
Logger.error(`[LibraryController] Invalid request. Settings "${key}" must be a string`)
|
||||
return res.status(400).send(`Invalid request. Settings "${key}" must be a string`)
|
||||
|
|
|
|||
|
|
@ -622,6 +622,7 @@ class PodcastManager {
|
|||
itunesId: '',
|
||||
itunesArtistId: '',
|
||||
language: '',
|
||||
podcastFilenameFormat: '',
|
||||
numEpisodes: feed.numEpisodes
|
||||
}
|
||||
}
|
||||
|
|
|
|||
64
server/migrations/v2.23.1-add-podcastFilenameFormat.js
Normal file
64
server/migrations/v2.23.1-add-podcastFilenameFormat.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* @typedef MigrationContext
|
||||
* @property {import('sequelize').QueryInterface} queryInterface - a suquelize QueryInterface object.
|
||||
* @property {import('../Logger')} logger - a Logger object.
|
||||
*
|
||||
* @typedef MigrationOptions
|
||||
* @property {MigrationContext} context - an object containing the migration context.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This upward migration script adds a new variable to allow saving podcast filename formats
|
||||
*
|
||||
* @param {MigrationOptions} options - an object containing the migration context.
|
||||
* @returns {Promise<void>} - A promise that resolves when the migration is complete.
|
||||
*/
|
||||
const migrationVersion = '2.23.1'
|
||||
const migrationName = `${migrationVersion}-add-podcastFilenameFormat-to-podcasts`
|
||||
const loggerPrefix = `[${migrationVersion} migration]`
|
||||
async function up({ context: { queryInterface, logger } }) {
|
||||
// Upwards migration script
|
||||
logger.info(`${loggerPrefix} UPGRADE BEGIN: ${migrationName}`)
|
||||
|
||||
// Run reindex nocase to fix potential corruption issues due to the bad sqlite extension introduced in v2.12.0
|
||||
logger.info(`${loggerPrefix} adding the variable`)
|
||||
await addColumn(queryInterface, logger, 'podcasts', 'podcastFilenameFormat', { type: queryInterface.sequelize.Sequelize.STRING, allowNull: true })
|
||||
|
||||
logger.info(`${loggerPrefix} UPGRADE END: ${migrationName}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* This downward migration script is a no-op.
|
||||
*
|
||||
* @param {MigrationOptions} options - an object containing the migration context.
|
||||
* @returns {Promise<void>} - A promise that resolves when the migration is complete.
|
||||
*/
|
||||
async function down({ context: { queryInterface, logger } }) {
|
||||
// Downward migration script
|
||||
logger.info(`${loggerPrefix} DOWNGRADE BEGIN: ${migrationName}`)
|
||||
|
||||
logger.info(`${loggerPrefix} Dropping column from table`)
|
||||
await removeColumn(queryInterface, logger, 'podcasts', 'podcastFilenameFormat')
|
||||
|
||||
|
||||
logger.info(`${loggerPrefix} DOWNGRADE END: ${migrationName}`)
|
||||
}
|
||||
|
||||
async function addColumn(queryInterface, logger, table, column, options) {
|
||||
logger.info(`${loggerPrefix} adding column "${column}" to table "${table}"`)
|
||||
const tableDescription = await queryInterface.describeTable(table)
|
||||
if (!tableDescription[column]) {
|
||||
await queryInterface.addColumn(table, column, options)
|
||||
logger.info(`${loggerPrefix} added column "${column}" to table "${table}"`)
|
||||
} else {
|
||||
logger.info(`${loggerPrefix} column "${column}" already exists in table "${table}"`)
|
||||
}
|
||||
}
|
||||
|
||||
async function removeColumn(queryInterface, logger, table, column) {
|
||||
logger.info(`${loggerPrefix} removing column "${column}" from table "${table}"`)
|
||||
await queryInterface.removeColumn(table, column)
|
||||
logger.info(`${loggerPrefix} removed column "${column}" from table "${table}"`)
|
||||
}
|
||||
|
||||
module.exports = { up, down }
|
||||
|
|
@ -39,6 +39,8 @@ class Podcast extends Model {
|
|||
/** @type {string} */
|
||||
this.language
|
||||
/** @type {string} */
|
||||
this.podcastFilenameFormat
|
||||
/** @type {string} */
|
||||
this.podcastType
|
||||
/** @type {boolean} */
|
||||
this.explicit
|
||||
|
|
@ -94,6 +96,7 @@ class Podcast extends Model {
|
|||
itunesId: typeof payload.metadata.itunesId === 'string' ? payload.metadata.itunesId : null,
|
||||
itunesArtistId: typeof payload.metadata.itunesArtistId === 'string' ? payload.metadata.itunesArtistId : null,
|
||||
language: typeof payload.metadata.language === 'string' ? payload.metadata.language : null,
|
||||
podcastFilenameFormat: typeof payload.metadata.podcastFilenameFormat === 'string' ? payload.metadata.podcastFilenameFormat : null,
|
||||
podcastType: typeof payload.metadata.type === 'string' ? payload.metadata.type : null,
|
||||
explicit: !!payload.metadata.explicit,
|
||||
autoDownloadEpisodes: !!payload.autoDownloadEpisodes,
|
||||
|
|
@ -131,6 +134,7 @@ class Podcast extends Model {
|
|||
itunesId: DataTypes.STRING,
|
||||
itunesArtistId: DataTypes.STRING,
|
||||
language: DataTypes.STRING,
|
||||
podcastFilenameFormat: DataTypes.STRING,
|
||||
podcastType: DataTypes.STRING,
|
||||
explicit: DataTypes.BOOLEAN,
|
||||
|
||||
|
|
@ -186,6 +190,7 @@ class Podcast extends Model {
|
|||
itunesId: this.itunesId,
|
||||
itunesArtistId: this.itunesArtistId,
|
||||
language: this.language,
|
||||
podcastFilenameFormat: this.podcastFilenameFormat,
|
||||
explicit: !!this.explicit,
|
||||
podcastType: this.podcastType
|
||||
}
|
||||
|
|
@ -202,7 +207,7 @@ class Podcast extends Model {
|
|||
let hasUpdates = false
|
||||
|
||||
if (payload.metadata) {
|
||||
const stringKeys = ['title', 'author', 'releaseDate', 'feedUrl', 'imageUrl', 'description', 'itunesPageUrl', 'itunesId', 'itunesArtistId', 'language', 'type']
|
||||
const stringKeys = ['title', 'author', 'releaseDate', 'feedUrl', 'imageUrl', 'description', 'itunesPageUrl', 'itunesId', 'itunesArtistId', 'language', 'type','podcastFilenameFormat']
|
||||
stringKeys.forEach((key) => {
|
||||
let newKey = key
|
||||
if (key === 'type') {
|
||||
|
|
@ -386,6 +391,7 @@ class Podcast extends Model {
|
|||
itunesArtistId: this.itunesArtistId,
|
||||
explicit: this.explicit,
|
||||
language: this.language,
|
||||
podcastFilenameFormat: this.podcastFilenameFormat,
|
||||
type: this.podcastType
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class PodcastEpisodeDownload {
|
|||
this.appendRandomId = false
|
||||
|
||||
this.targetFilename = null
|
||||
this.podcastFilenameFormat = null
|
||||
|
||||
this.startedAt = null
|
||||
this.createdAt = null
|
||||
|
|
@ -89,13 +90,38 @@ class PodcastEpisodeDownload {
|
|||
if (!this.rssPodcastEpisode.publishedAt) return null
|
||||
return new Date(this.rssPodcastEpisode.publishedAt).getFullYear()
|
||||
}
|
||||
formatFilename() {
|
||||
const {publishedAt, season, episode, title} = this.rssPodcastEpisode;
|
||||
const fileformat = this.libraryItem.media.podcastFilenameFormat || '%T'
|
||||
let filename = fileformat
|
||||
if (publishedAt) {
|
||||
const dt = new Date(publishedAt)
|
||||
const year = dt.getFullYear()
|
||||
const month = String(dt.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(dt.getDate()).padStart(2,'0')
|
||||
filename = filename
|
||||
.replace("%Y",year)
|
||||
.replace("%M",month)
|
||||
.replace("%D",day)
|
||||
}
|
||||
if (season) {
|
||||
filename = filename.replace("%S",season)
|
||||
}
|
||||
if (episode){
|
||||
filename = filename.replace("%E",episode)
|
||||
}
|
||||
if (title){
|
||||
filename = filename.replace("%T",title.trim() || '')
|
||||
}
|
||||
return filename
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} title
|
||||
*/
|
||||
getSanitizedFilename(title) {
|
||||
getSanitizedFilename() {
|
||||
const appendage = this.appendRandomId ? ` (${this.id})` : ''
|
||||
const filename = `${title.trim()}${appendage}.${this.fileExtension}`
|
||||
const filename = `${this.formatFilename()}${appendage}.${this.fileExtension}`
|
||||
return sanitizeFilename(filename)
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +130,7 @@ class PodcastEpisodeDownload {
|
|||
*/
|
||||
setAppendRandomId(appendRandomId) {
|
||||
this.appendRandomId = appendRandomId
|
||||
this.targetFilename = this.getSanitizedFilename(this.rssPodcastEpisode.title || '')
|
||||
this.targetFilename = this.getSanitizedFilename()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,9 +152,9 @@ class PodcastEpisodeDownload {
|
|||
this.url = encodeURI(url)
|
||||
}
|
||||
|
||||
this.targetFilename = this.getSanitizedFilename(this.rssPodcastEpisode.title || '')
|
||||
|
||||
this.libraryItem = libraryItem
|
||||
this.targetFilename = this.getSanitizedFilename()
|
||||
this.isAutoDownload = isAutoDownload
|
||||
this.createdAt = Date.now()
|
||||
this.libraryId = libraryId
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ function migratePodcast(oldLibraryItem, LibraryItem) {
|
|||
itunesArtistId: oldPodcastMetadata.itunesArtistId,
|
||||
language: oldPodcastMetadata.language,
|
||||
podcastType: oldPodcastMetadata.type,
|
||||
podcastFilenameFormat: oldPodcastMetadata.podcastFilenameFormat,
|
||||
explicit: !!oldPodcastMetadata.explicit,
|
||||
autoDownloadEpisodes: !!oldPodcast.autoDownloadEpisodes,
|
||||
autoDownloadSchedule: oldPodcast.autoDownloadSchedule,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue