Skip to content

Commit

Permalink
Update podcasts to new library item model
Browse files Browse the repository at this point in the history
  • Loading branch information
advplyr committed Jan 4, 2025
1 parent 4a398f6 commit d8823c8
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 284 deletions.
104 changes: 75 additions & 29 deletions server/controllers/PodcastController.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const Path = require('path')
const { Request, Response, NextFunction } = require('express')
const Logger = require('../Logger')
const SocketAuthority = require('../SocketAuthority')
Expand All @@ -12,8 +13,6 @@ const { validateUrl } = require('../utils/index')
const Scanner = require('../scanner/Scanner')
const CoverManager = require('../managers/CoverManager')

const LibraryItem = require('../objects/LibraryItem')

/**
* @typedef RequestUserObject
* @property {import('../models/User')} user
Expand Down Expand Up @@ -42,6 +41,9 @@ class PodcastController {
return res.sendStatus(403)
}
const payload = req.body
if (!payload.media || !payload.media.metadata) {
return res.status(400).send('Invalid request body. "media" and "media.metadata" are required')
}

const library = await Database.libraryModel.findByIdWithFolders(payload.libraryId)
if (!library) {
Expand Down Expand Up @@ -83,43 +85,87 @@ class PodcastController {
let relPath = payload.path.replace(folder.fullPath, '')
if (relPath.startsWith('/')) relPath = relPath.slice(1)

const libraryItemPayload = {
path: podcastPath,
relPath,
folderId: payload.folderId,
libraryId: payload.libraryId,
ino: libraryItemFolderStats.ino,
mtimeMs: libraryItemFolderStats.mtimeMs || 0,
ctimeMs: libraryItemFolderStats.ctimeMs || 0,
birthtimeMs: libraryItemFolderStats.birthtimeMs || 0,
media: payload.media
}

const libraryItem = new LibraryItem()
libraryItem.setData('podcast', libraryItemPayload)
let newLibraryItem = null
const transaction = await Database.sequelize.transaction()
try {
const podcast = await Database.podcastModel.createFromRequest(payload.media, transaction)

newLibraryItem = await Database.libraryItemModel.create(
{
ino: libraryItemFolderStats.ino,
path: podcastPath,
relPath,
mediaId: podcast.id,
mediaType: 'podcast',
isFile: false,
isMissing: false,
isInvalid: false,
mtime: libraryItemFolderStats.mtimeMs || 0,
ctime: libraryItemFolderStats.ctimeMs || 0,
birthtime: libraryItemFolderStats.birthtimeMs || 0,
size: 0,
libraryFiles: [],
extraData: {},
libraryId: library.id,
libraryFolderId: folder.id
},
{ transaction }
)

await transaction.commit()
} catch (error) {
Logger.error(`[PodcastController] Failed to create podcast: ${error}`)
await transaction.rollback()
return res.status(500).send('Failed to create podcast')
}

newLibraryItem.media = await newLibraryItem.getMediaExpanded()

// Download and save cover image
if (payload.media.metadata.imageUrl) {
// TODO: Scan cover image to library files
if (typeof payload.media.metadata.imageUrl === 'string' && payload.media.metadata.imageUrl.startsWith('http')) {
// Podcast cover will always go into library item folder
const coverResponse = await CoverManager.downloadCoverFromUrl(libraryItem, payload.media.metadata.imageUrl, true)
if (coverResponse) {
if (coverResponse.error) {
Logger.error(`[PodcastController] Download cover error from "${payload.media.metadata.imageUrl}": ${coverResponse.error}`)
} else if (coverResponse.cover) {
libraryItem.media.coverPath = coverResponse.cover
const coverResponse = await CoverManager.downloadCoverFromUrlNew(payload.media.metadata.imageUrl, newLibraryItem.id, newLibraryItem.path, true)
if (coverResponse.error) {
Logger.error(`[PodcastController] Download cover error from "${payload.media.metadata.imageUrl}": ${coverResponse.error}`)
} else if (coverResponse.cover) {
const coverImageFileStats = await getFileTimestampsWithIno(coverResponse.cover)
if (!coverImageFileStats) {
Logger.error(`[PodcastController] Failed to get cover image stats for "${coverResponse.cover}"`)
} else {
// Add libraryFile to libraryItem and coverPath to podcast
const newLibraryFile = {
ino: coverImageFileStats.ino,
fileType: 'image',
addedAt: Date.now(),
updatedAt: Date.now(),
metadata: {
filename: Path.basename(coverResponse.cover),
ext: Path.extname(coverResponse.cover).slice(1),
path: coverResponse.cover,
relPath: Path.basename(coverResponse.cover),
size: coverImageFileStats.size,
mtimeMs: coverImageFileStats.mtimeMs || 0,
ctimeMs: coverImageFileStats.ctimeMs || 0,
birthtimeMs: coverImageFileStats.birthtimeMs || 0
}
}
newLibraryItem.libraryFiles.push(newLibraryFile)
newLibraryItem.changed('libraryFiles', true)
await newLibraryItem.save()

newLibraryItem.media.coverPath = coverResponse.cover
await newLibraryItem.media.save()
}
}
}

await Database.createLibraryItem(libraryItem)
SocketAuthority.emitter('item_added', libraryItem.toJSONExpanded())
SocketAuthority.emitter('item_added', newLibraryItem.toOldJSONExpanded())

res.json(libraryItem.toJSONExpanded())
res.json(newLibraryItem.toOldJSONExpanded())

// Turn on podcast auto download cron if not already on
if (libraryItem.media.autoDownloadEpisodes) {
this.cronManager.checkUpdatePodcastCron(libraryItem)
if (newLibraryItem.media.autoDownloadEpisodes) {
this.cronManager.checkUpdatePodcastCron(newLibraryItem)
}
}

Expand Down
7 changes: 4 additions & 3 deletions server/managers/CoverManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,13 +338,14 @@ class CoverManager {
*
* @param {string} url
* @param {string} libraryItemId
* @param {string} [libraryItemPath] null if library item isFile or is from adding new podcast
* @param {string} [libraryItemPath] - null if library item isFile
* @param {boolean} [forceLibraryItemFolder=false] - force save cover with library item (used for adding new podcasts)
* @returns {Promise<{error:string}|{cover:string}>}
*/
async downloadCoverFromUrlNew(url, libraryItemId, libraryItemPath) {
async downloadCoverFromUrlNew(url, libraryItemId, libraryItemPath, forceLibraryItemFolder = false) {
try {
let coverDirPath = null
if (global.ServerSettings.storeCoverWithItem && libraryItemPath) {
if ((global.ServerSettings.storeCoverWithItem || forceLibraryItemFolder) && libraryItemPath) {
coverDirPath = libraryItemPath
} else {
coverDirPath = Path.posix.join(global.MetadataPath, 'items', libraryItemId)
Expand Down
2 changes: 1 addition & 1 deletion server/managers/CronManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class CronManager {

/**
*
* @param {import('../models/LibraryItem')} libraryItem - this can be the old model
* @param {import('../models/LibraryItem')} libraryItem
*/
checkUpdatePodcastCron(libraryItem) {
// Remove from old cron by library item id
Expand Down
15 changes: 10 additions & 5 deletions server/managers/NotificationManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ class NotificationManager {
return notificationData
}

/**
*
* @param {import('../models/LibraryItem')} libraryItem
* @param {import('../models/PodcastEpisode')} episode
*/
async onPodcastEpisodeDownloaded(libraryItem, episode) {
if (!Database.notificationSettings.isUseable) return

Expand All @@ -22,17 +27,17 @@ class NotificationManager {
return
}

Logger.debug(`[NotificationManager] onPodcastEpisodeDownloaded: Episode "${episode.title}" for podcast ${libraryItem.media.metadata.title}`)
Logger.debug(`[NotificationManager] onPodcastEpisodeDownloaded: Episode "${episode.title}" for podcast ${libraryItem.media.title}`)
const library = await Database.libraryModel.findByPk(libraryItem.libraryId)
const eventData = {
libraryItemId: libraryItem.id,
libraryId: libraryItem.libraryId,
libraryName: library?.name || 'Unknown',
mediaTags: (libraryItem.media.tags || []).join(', '),
podcastTitle: libraryItem.media.metadata.title,
podcastAuthor: libraryItem.media.metadata.author || '',
podcastDescription: libraryItem.media.metadata.description || '',
podcastGenres: (libraryItem.media.metadata.genres || []).join(', '),
podcastTitle: libraryItem.media.title,
podcastAuthor: libraryItem.media.author || '',
podcastDescription: libraryItem.media.description || '',
podcastGenres: (libraryItem.media.genres || []).join(', '),
episodeId: episode.id,
episodeTitle: episode.title,
episodeSubtitle: episode.subtitle || '',
Expand Down
Loading

0 comments on commit d8823c8

Please sign in to comment.