This commit is contained in:
Shaun Guimond 2025-12-17 01:09:30 +01:00 committed by GitHub
commit 04790013c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 110 additions and 8 deletions

View file

@ -29,15 +29,18 @@
</div>
</div>
<div class="flex flex-wrap">
<div class="md:w-1/4 p-2">
<div class="w-full md:w-1/4 p-2">
<ui-dropdown :label="$strings.LabelPodcastType" v-model="podcast.type" :items="podcastTypes" small />
</div>
<div class="md:w-1/4 p-2">
<div class="w-full md:w-1/4 p-2">
<ui-text-input-with-label v-model="podcast.language" :label="$strings.LabelLanguage" />
</div>
<div class="md:w-1/4 px-2 pt-7">
<div class="w-full md:w-1/4 px-2 pt-7">
<ui-checkbox v-model="podcast.explicit" :label="$strings.LabelExplicit" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" />
</div>
<div class="w-full md:w-1/4 px-2 pt-7">
<ui-checkbox v-model="podcast.isAuthenticatedFeed" :label="$strings.LabelAuthenticatedFeed" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" />
</div>
</div>
<div class="p-2 w-full">
<ui-textarea-with-label v-model="podcast.description" :label="$strings.LabelDescription" :rows="3" />
@ -96,6 +99,7 @@ export default {
autoDownloadEpisodes: false,
language: '',
explicit: false,
isAuthenticatedFeed: false,
type: ''
}
}
@ -194,6 +198,7 @@ export default {
itunesArtistId: this.podcast.itunesArtistId,
language: this.podcast.language,
explicit: this.podcast.explicit,
isAuthenticatedFeed: this.podcast.isAuthenticatedFeed,
type: this.podcast.type
},
autoDownloadEpisodes: this.podcast.autoDownloadEpisodes
@ -234,6 +239,7 @@ export default {
this.podcast.type = this._podcastData.type || this.feedMetadata.type || 'episodic'
this.podcast.explicit = this._podcastData.explicit || this.feedMetadata.explicit === 'yes' || this.feedMetadata.explicit == 'true'
this.podcast.isAuthenticatedFeed = this._podcastData.isAuthenticatedFeed || false
if (this.folderItems[0]) {
this.selectedFolderId = this.folderItems[0].value
this.folderUpdated()

View file

@ -34,8 +34,9 @@
<ui-text-input-with-label ref="languageInput" v-model="details.language" :label="$strings.LabelLanguage" trim-whitespace @input="handleInputChange" />
</div>
<div class="grow px-1 pt-6">
<div class="flex justify-center">
<div class="flex justify-center space-x-4">
<ui-checkbox v-model="details.explicit" :label="$strings.LabelExplicit" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" @input="handleInputChange" />
<ui-checkbox v-model="details.isAuthenticatedFeed" :label="$strings.LabelAuthenticatedFeed" checkbox-bg="primary" border-color="gray-600" label-class="pl-2 text-base font-semibold" @input="handleInputChange" />
</div>
</div>
</div>
@ -70,6 +71,7 @@ export default {
itunesId: null,
itunesArtistId: null,
explicit: false,
isAuthenticatedFeed: false,
language: null,
type: null
},
@ -241,6 +243,7 @@ export default {
this.details.itunesArtistId = this.mediaMetadata.itunesArtistId || ''
this.details.language = this.mediaMetadata.language || ''
this.details.explicit = !!this.mediaMetadata.explicit
this.details.isAuthenticatedFeed = !!this.mediaMetadata.isAuthenticatedFeed
this.details.type = this.mediaMetadata.type || 'episodic'
this.newTags = [...(this.media.tags || [])]

View file

@ -366,6 +366,7 @@
"LabelExplicit": "Explicit",
"LabelExplicitChecked": "Explicit (checked)",
"LabelExplicitUnchecked": "Not Explicit (unchecked)",
"LabelAuthenticatedFeed": "Authenticated Feed",
"LabelExportOPML": "Export OPML",
"LabelFeedURL": "Feed URL",
"LabelFetchingMetadata": "Fetching Metadata",

View file

@ -0,0 +1,68 @@
/**
* @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.
*/
const migrationVersion = '2.30.1'
const migrationName = `${migrationVersion}-add-is-authenticated-feed`
const loggerPrefix = `[${migrationVersion} migration]`
/**
* This upward migration adds the isAuthenticatedFeed column to the podcasts table.
*
* @param {MigrationOptions} options - an object containing the migration context.
* @returns {Promise<void>} - A promise that resolves when the migration is complete.
*/
async function up({ context: { queryInterface, logger } }) {
// Upwards migration script
logger.info(`${loggerPrefix} UPGRADE BEGIN: ${migrationName}`)
const DataTypes = queryInterface.sequelize.Sequelize.DataTypes
// Check if column exists
const tableDescription = await queryInterface.describeTable('podcasts')
if (tableDescription.isAuthenticatedFeed) {
logger.info(`${loggerPrefix} column "isAuthenticatedFeed" already exists in "podcasts" table`)
} else {
// Add column
logger.info(`${loggerPrefix} adding column "isAuthenticatedFeed" to "podcasts" table`)
await queryInterface.addColumn('podcasts', 'isAuthenticatedFeed', {
type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false
})
logger.info(`${loggerPrefix} added column "isAuthenticatedFeed" to "podcasts" table`)
}
logger.info(`${loggerPrefix} UPGRADE COMPLETE: ${migrationName}`)
}
/**
* This downward migration removes the isAuthenticatedFeed column from the podcasts table.
*
* @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 } }) {
// Downwards migration script
logger.info(`${loggerPrefix} DOWNGRADE BEGIN: ${migrationName}`)
// Check if column exists
const tableDescription = await queryInterface.describeTable('podcasts')
if (!tableDescription.isAuthenticatedFeed) {
logger.info(`${loggerPrefix} column "isAuthenticatedFeed" does not exist in "podcasts" table`)
} else {
// Remove column
logger.info(`${loggerPrefix} removing column "isAuthenticatedFeed" from "podcasts" table`)
await queryInterface.removeColumn('podcasts', 'isAuthenticatedFeed')
logger.info(`${loggerPrefix} removed column "isAuthenticatedFeed" from "podcasts" table`)
}
logger.info(`${loggerPrefix} DOWNGRADE COMPLETE: ${migrationName}`)
}
module.exports = { up, down }

View file

@ -44,6 +44,8 @@ class Podcast extends Model {
/** @type {boolean} */
this.explicit
/** @type {boolean} */
this.isAuthenticatedFeed
/** @type {boolean} */
this.autoDownloadEpisodes
/** @type {string} */
this.autoDownloadSchedule
@ -104,6 +106,7 @@ class Podcast extends Model {
language: typeof payload.metadata.language === 'string' ? payload.metadata.language : null,
podcastType: typeof payload.metadata.type === 'string' ? payload.metadata.type : null,
explicit: !!payload.metadata.explicit,
isAuthenticatedFeed: !!payload.metadata.isAuthenticatedFeed,
autoDownloadEpisodes: !!payload.autoDownloadEpisodes,
autoDownloadSchedule: autoDownloadSchedule || global.ServerSettings.podcastEpisodeSchedule,
lastEpisodeCheck: new Date(),
@ -141,6 +144,7 @@ class Podcast extends Model {
language: DataTypes.STRING,
podcastType: DataTypes.STRING,
explicit: DataTypes.BOOLEAN,
isAuthenticatedFeed: DataTypes.BOOLEAN,
autoDownloadEpisodes: DataTypes.BOOLEAN,
autoDownloadSchedule: DataTypes.STRING,
@ -252,6 +256,11 @@ class Podcast extends Model {
hasUpdates = true
}
if (payload.metadata.isAuthenticatedFeed !== undefined && payload.metadata.isAuthenticatedFeed !== this.isAuthenticatedFeed) {
this.isAuthenticatedFeed = !!payload.metadata.isAuthenticatedFeed
hasUpdates = true
}
if (Array.isArray(payload.metadata.genres) && !payload.metadata.genres.some((item) => typeof item !== 'string') && JSON.stringify(this.genres) !== JSON.stringify(payload.metadata.genres)) {
this.genres = payload.metadata.genres
this.changed('genres', true)
@ -407,6 +416,7 @@ class Podcast extends Model {
itunesId: this.itunesId,
itunesArtistId: this.itunesArtistId,
explicit: this.explicit,
isAuthenticatedFeed: this.isAuthenticatedFeed,
language: this.language,
type: this.podcastType
}

View file

@ -108,19 +108,33 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => {
// See: https://github.com/advplyr/audiobookshelf/issues/4401 (requires no iTMS user agent)
const userAgents = ['audiobookshelf (+https://audiobookshelf.org; like iTMS)', 'audiobookshelf (+https://audiobookshelf.org)']
let refererHeader = null
const feedURL = podcastEpisodeDownload.libraryItem?.media?.feedURL
const isAuthenticatedFeed = podcastEpisodeDownload.libraryItem?.media?.isAuthenticatedFeed || false
if (feedURL && isAuthenticatedFeed) {
refererHeader = feedURL
}
let response = null
let lastError = null
for (const userAgent of userAgents) {
try {
const headers = {
Accept: '*/*',
'User-Agent': userAgent
}
if (refererHeader) {
headers['Referer'] = refererHeader
}
response = await axios({
url: podcastEpisodeDownload.url,
method: 'GET',
responseType: 'stream',
headers: {
Accept: '*/*',
'User-Agent': userAgent
},
headers,
timeout: global.PodcastDownloadTimeout
})