mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Update Task object to handle translation keys with subs
This commit is contained in:
		
							parent
							
								
									bb481ccfb4
								
							
						
					
					
						commit
						8512d5e693
					
				@ -301,7 +301,12 @@ class FolderWatcher extends EventEmitter {
 | 
				
			|||||||
        libraryId,
 | 
					        libraryId,
 | 
				
			||||||
        libraryName: libwatcher.name
 | 
					        libraryName: libwatcher.name
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.pendingTask = TaskManager.createAndAddTask('watcher-scan', `Scanning file changes in "${libwatcher.name}"`, null, true, taskData)
 | 
					      const taskTitleString = {
 | 
				
			||||||
 | 
					        text: `Scanning file changes in "${libwatcher.name}"`,
 | 
				
			||||||
 | 
					        key: 'MessageTaskScanningFileChanges',
 | 
				
			||||||
 | 
					        subs: [libwatcher.name]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.pendingTask = TaskManager.createAndAddTask('watcher-scan', taskTitleString, null, true, taskData)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.pendingFileUpdates.push({
 | 
					    this.pendingFileUpdates.push({
 | 
				
			||||||
      path,
 | 
					      path,
 | 
				
			||||||
 | 
				
			|||||||
@ -40,7 +40,11 @@ class AbMergeManager {
 | 
				
			|||||||
   * @returns {Promise<void>}
 | 
					   * @returns {Promise<void>}
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  cancelEncode(task) {
 | 
					  cancelEncode(task) {
 | 
				
			||||||
    task.setFailed('Task canceled by user')
 | 
					    const taskFailedString = {
 | 
				
			||||||
 | 
					      text: 'Task canceled by user',
 | 
				
			||||||
 | 
					      key: 'MessageTaskCanceledByUser'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    task.setFailed(taskFailedString)
 | 
				
			||||||
    return this.removeTask(task, true)
 | 
					    return this.removeTask(task, true)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -76,8 +80,17 @@ class AbMergeManager {
 | 
				
			|||||||
      duration: libraryItem.media.duration,
 | 
					      duration: libraryItem.media.duration,
 | 
				
			||||||
      encodeOptions: options
 | 
					      encodeOptions: options
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const taskDescription = `Encoding audiobook "${libraryItem.media.metadata.title}" into a single m4b file.`
 | 
					
 | 
				
			||||||
    task.setData('encode-m4b', 'Encoding M4b', taskDescription, false, taskData)
 | 
					    const taskTitleString = {
 | 
				
			||||||
 | 
					      text: 'Encoding M4b',
 | 
				
			||||||
 | 
					      key: 'MessageTaskEncodingM4b'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const taskDescriptionString = {
 | 
				
			||||||
 | 
					      text: `Encoding audiobook "${libraryItem.media.metadata.title}" into a single m4b file.`,
 | 
				
			||||||
 | 
					      key: 'MessageTaskEncodingM4bDescription',
 | 
				
			||||||
 | 
					      subs: [libraryItem.media.metadata.title]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    task.setData('encode-m4b', taskTitleString, taskDescriptionString, false, taskData)
 | 
				
			||||||
    TaskManager.addTask(task)
 | 
					    TaskManager.addTask(task)
 | 
				
			||||||
    Logger.info(`Start m4b encode for ${libraryItem.id} - TaskId: ${task.id}`)
 | 
					    Logger.info(`Start m4b encode for ${libraryItem.id} - TaskId: ${task.id}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -98,7 +111,11 @@ class AbMergeManager {
 | 
				
			|||||||
    // Make sure the target directory is writable
 | 
					    // Make sure the target directory is writable
 | 
				
			||||||
    if (!(await isWritable(task.data.libraryItemDir))) {
 | 
					    if (!(await isWritable(task.data.libraryItemDir))) {
 | 
				
			||||||
      Logger.error(`[AbMergeManager] Target directory is not writable: ${task.data.libraryItemDir}`)
 | 
					      Logger.error(`[AbMergeManager] Target directory is not writable: ${task.data.libraryItemDir}`)
 | 
				
			||||||
      task.setFailed('Target directory is not writable')
 | 
					      const taskFailedString = {
 | 
				
			||||||
 | 
					        text: 'Target directory is not writable',
 | 
				
			||||||
 | 
					        key: 'MessageTaskTargetDirectoryNotWritable'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      task.setFailed(taskFailedString)
 | 
				
			||||||
      this.removeTask(task, true)
 | 
					      this.removeTask(task, true)
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -106,7 +123,11 @@ class AbMergeManager {
 | 
				
			|||||||
    // Create ffmetadata file
 | 
					    // Create ffmetadata file
 | 
				
			||||||
    if (!(await ffmpegHelpers.writeFFMetadataFile(task.data.ffmetadataObject, task.data.chapters, task.data.ffmetadataPath))) {
 | 
					    if (!(await ffmpegHelpers.writeFFMetadataFile(task.data.ffmetadataObject, task.data.chapters, task.data.ffmetadataPath))) {
 | 
				
			||||||
      Logger.error(`[AudioMetadataManager] Failed to write ffmetadata file for audiobook "${task.data.libraryItemId}"`)
 | 
					      Logger.error(`[AudioMetadataManager] Failed to write ffmetadata file for audiobook "${task.data.libraryItemId}"`)
 | 
				
			||||||
      task.setFailed('Failed to write metadata file.')
 | 
					      const taskFailedString = {
 | 
				
			||||||
 | 
					        text: 'Failed to write metadata file',
 | 
				
			||||||
 | 
					        key: 'MessageTaskFailedToWriteMetadataFile'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      task.setFailed(taskFailedString)
 | 
				
			||||||
      this.removeTask(task, true)
 | 
					      this.removeTask(task, true)
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -137,7 +158,11 @@ class AbMergeManager {
 | 
				
			|||||||
        Logger.info(`[AbMergeManager] Task cancelled ${task.id}`)
 | 
					        Logger.info(`[AbMergeManager] Task cancelled ${task.id}`)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        Logger.error(`[AbMergeManager] mergeAudioFiles failed`, error)
 | 
					        Logger.error(`[AbMergeManager] mergeAudioFiles failed`, error)
 | 
				
			||||||
        task.setFailed('Failed to merge audio files')
 | 
					        const taskFailedString = {
 | 
				
			||||||
 | 
					          text: 'Failed to merge audio files',
 | 
				
			||||||
 | 
					          key: 'MessageTaskFailedToMergeAudioFiles'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        task.setFailed(taskFailedString)
 | 
				
			||||||
        this.removeTask(task, true)
 | 
					        this.removeTask(task, true)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
@ -164,7 +189,11 @@ class AbMergeManager {
 | 
				
			|||||||
        Logger.info(`[AbMergeManager] Task cancelled ${task.id}`)
 | 
					        Logger.info(`[AbMergeManager] Task cancelled ${task.id}`)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        Logger.error(`[AbMergeManager] Failed to write metadata to file "${task.data.tempFilepath}"`)
 | 
					        Logger.error(`[AbMergeManager] Failed to write metadata to file "${task.data.tempFilepath}"`)
 | 
				
			||||||
        task.setFailed('Failed to write metadata to m4b file')
 | 
					        const taskFailedString = {
 | 
				
			||||||
 | 
					          text: 'Failed to write metadata to m4b file',
 | 
				
			||||||
 | 
					          key: 'MessageTaskFailedToWriteMetadataToM4bFile'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        task.setFailed(taskFailedString)
 | 
				
			||||||
        this.removeTask(task, true)
 | 
					        this.removeTask(task, true)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
@ -196,7 +225,11 @@ class AbMergeManager {
 | 
				
			|||||||
      await fs.remove(task.data.tempFilepath)
 | 
					      await fs.remove(task.data.tempFilepath)
 | 
				
			||||||
    } catch (err) {
 | 
					    } catch (err) {
 | 
				
			||||||
      Logger.error(`[AbMergeManager] Failed to move m4b from ${task.data.tempFilepath} to ${task.data.targetFilepath}`, err)
 | 
					      Logger.error(`[AbMergeManager] Failed to move m4b from ${task.data.tempFilepath} to ${task.data.targetFilepath}`, err)
 | 
				
			||||||
      task.setFailed('Failed to move m4b file')
 | 
					      const taskFailedString = {
 | 
				
			||||||
 | 
					        text: 'Failed to move m4b file',
 | 
				
			||||||
 | 
					        key: 'MessageTaskFailedToMoveM4bFile'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      task.setFailed(taskFailedString)
 | 
				
			||||||
      this.removeTask(task, true)
 | 
					      this.removeTask(task, true)
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -97,8 +97,17 @@ class AudioMetadataMangaer {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      duration: libraryItem.media.duration
 | 
					      duration: libraryItem.media.duration
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const taskDescription = `Embedding metadata in audiobook "${libraryItem.media.metadata.title}".`
 | 
					
 | 
				
			||||||
    task.setData('embed-metadata', 'Embedding Metadata', taskDescription, false, taskData)
 | 
					    const taskTitleString = {
 | 
				
			||||||
 | 
					      text: 'Embedding Metadata',
 | 
				
			||||||
 | 
					      key: 'MessageTaskEmbeddingMetadata'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const taskDescriptionString = {
 | 
				
			||||||
 | 
					      text: `Embedding metadata in audiobook "${libraryItem.media.metadata.title}".`,
 | 
				
			||||||
 | 
					      key: 'MessageTaskEmbeddingMetadataDescription',
 | 
				
			||||||
 | 
					      subs: [libraryItem.media.metadata.title]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    task.setData('embed-metadata', taskTitleString, taskDescriptionString, false, taskData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.tasksRunning.length >= this.MAX_CONCURRENT_TASKS) {
 | 
					    if (this.tasksRunning.length >= this.MAX_CONCURRENT_TASKS) {
 | 
				
			||||||
      Logger.info(`[AudioMetadataManager] Queueing embed metadata for audiobook "${libraryItem.media.metadata.title}"`)
 | 
					      Logger.info(`[AudioMetadataManager] Queueing embed metadata for audiobook "${libraryItem.media.metadata.title}"`)
 | 
				
			||||||
@ -123,7 +132,7 @@ class AudioMetadataMangaer {
 | 
				
			|||||||
    Logger.debug(`[AudioMetadataManager] Target directory ${task.data.libraryItemDir} writable: ${targetDirWritable}`)
 | 
					    Logger.debug(`[AudioMetadataManager] Target directory ${task.data.libraryItemDir} writable: ${targetDirWritable}`)
 | 
				
			||||||
    if (!targetDirWritable) {
 | 
					    if (!targetDirWritable) {
 | 
				
			||||||
      Logger.error(`[AudioMetadataManager] Target directory is not writable: ${task.data.libraryItemDir}`)
 | 
					      Logger.error(`[AudioMetadataManager] Target directory is not writable: ${task.data.libraryItemDir}`)
 | 
				
			||||||
      task.setFailed('Target directory is not writable')
 | 
					      task.setFailedText('Target directory is not writable')
 | 
				
			||||||
      this.handleTaskFinished(task)
 | 
					      this.handleTaskFinished(task)
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -134,7 +143,7 @@ class AudioMetadataMangaer {
 | 
				
			|||||||
        await fs.access(af.path, fs.constants.W_OK)
 | 
					        await fs.access(af.path, fs.constants.W_OK)
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
        Logger.error(`[AudioMetadataManager] Audio file is not writable: ${af.path}`)
 | 
					        Logger.error(`[AudioMetadataManager] Audio file is not writable: ${af.path}`)
 | 
				
			||||||
        task.setFailed(`Audio file "${Path.basename(af.path)}" is not writable`)
 | 
					        task.setFailedText(`Audio file "${Path.basename(af.path)}" is not writable`)
 | 
				
			||||||
        this.handleTaskFinished(task)
 | 
					        this.handleTaskFinished(task)
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -148,7 +157,7 @@ class AudioMetadataMangaer {
 | 
				
			|||||||
        cacheDirCreated = true
 | 
					        cacheDirCreated = true
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
        Logger.error(`[AudioMetadataManager] Failed to create cache directory ${task.data.itemCachePath}`, err)
 | 
					        Logger.error(`[AudioMetadataManager] Failed to create cache directory ${task.data.itemCachePath}`, err)
 | 
				
			||||||
        task.setFailed('Failed to create cache directory')
 | 
					        task.setFailedText('Failed to create cache directory')
 | 
				
			||||||
        this.handleTaskFinished(task)
 | 
					        this.handleTaskFinished(task)
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -159,7 +168,7 @@ class AudioMetadataMangaer {
 | 
				
			|||||||
    const success = await ffmpegHelpers.writeFFMetadataFile(task.data.metadataObject, task.data.chapters, ffmetadataPath)
 | 
					    const success = await ffmpegHelpers.writeFFMetadataFile(task.data.metadataObject, task.data.chapters, ffmetadataPath)
 | 
				
			||||||
    if (!success) {
 | 
					    if (!success) {
 | 
				
			||||||
      Logger.error(`[AudioMetadataManager] Failed to write ffmetadata file for audiobook "${task.data.libraryItemId}"`)
 | 
					      Logger.error(`[AudioMetadataManager] Failed to write ffmetadata file for audiobook "${task.data.libraryItemId}"`)
 | 
				
			||||||
      task.setFailed('Failed to write metadata file.')
 | 
					      task.setFailedText('Failed to write metadata file.')
 | 
				
			||||||
      this.handleTaskFinished(task)
 | 
					      this.handleTaskFinished(task)
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -181,7 +190,7 @@ class AudioMetadataMangaer {
 | 
				
			|||||||
          Logger.debug(`[AudioMetadataManager] Backed up audio file at "${backupFilePath}"`)
 | 
					          Logger.debug(`[AudioMetadataManager] Backed up audio file at "${backupFilePath}"`)
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
          Logger.error(`[AudioMetadataManager] Failed to backup audio file "${af.path}"`, err)
 | 
					          Logger.error(`[AudioMetadataManager] Failed to backup audio file "${af.path}"`, err)
 | 
				
			||||||
          task.setFailed(`Failed to backup audio file "${Path.basename(af.path)}"`)
 | 
					          task.setFailedText(`Failed to backup audio file "${Path.basename(af.path)}"`)
 | 
				
			||||||
          this.handleTaskFinished(task)
 | 
					          this.handleTaskFinished(task)
 | 
				
			||||||
          return
 | 
					          return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -195,7 +204,7 @@ class AudioMetadataMangaer {
 | 
				
			|||||||
        Logger.info(`[AudioMetadataManager] Successfully tagged audio file "${af.path}"`)
 | 
					        Logger.info(`[AudioMetadataManager] Successfully tagged audio file "${af.path}"`)
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
        Logger.error(`[AudioMetadataManager] Failed to tag audio file "${af.path}"`, err)
 | 
					        Logger.error(`[AudioMetadataManager] Failed to tag audio file "${af.path}"`, err)
 | 
				
			||||||
        task.setFailed(`Failed to tag audio file "${Path.basename(af.path)}"`)
 | 
					        task.setFailedText(`Failed to tag audio file "${Path.basename(af.path)}"`)
 | 
				
			||||||
        this.handleTaskFinished(task)
 | 
					        this.handleTaskFinished(task)
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
				
			|||||||
@ -71,12 +71,20 @@ class PodcastManager {
 | 
				
			|||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const taskDescription = `Downloading episode "${podcastEpisodeDownload.podcastEpisode.title}".`
 | 
					 | 
				
			||||||
    const taskData = {
 | 
					    const taskData = {
 | 
				
			||||||
      libraryId: podcastEpisodeDownload.libraryId,
 | 
					      libraryId: podcastEpisodeDownload.libraryId,
 | 
				
			||||||
      libraryItemId: podcastEpisodeDownload.libraryItemId
 | 
					      libraryItemId: podcastEpisodeDownload.libraryItemId
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const task = TaskManager.createAndAddTask('download-podcast-episode', 'Downloading Episode', taskDescription, false, taskData)
 | 
					    const taskTitleString = {
 | 
				
			||||||
 | 
					      text: 'Downloading episode',
 | 
				
			||||||
 | 
					      key: 'MessageDownloadingEpisode'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const taskDescriptionString = {
 | 
				
			||||||
 | 
					      text: `Downloading episode "${podcastEpisodeDownload.podcastEpisode.title}".`,
 | 
				
			||||||
 | 
					      key: 'MessageTaskDownloadingEpisodeDescription',
 | 
				
			||||||
 | 
					      subs: [podcastEpisodeDownload.podcastEpisode.title]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const task = TaskManager.createAndAddTask('download-podcast-episode', taskTitleString, taskDescriptionString, false, taskData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SocketAuthority.emitter('episode_download_started', podcastEpisodeDownload.toJSONForClient())
 | 
					    SocketAuthority.emitter('episode_download_started', podcastEpisodeDownload.toJSONForClient())
 | 
				
			||||||
    this.currentDownload = podcastEpisodeDownload
 | 
					    this.currentDownload = podcastEpisodeDownload
 | 
				
			||||||
@ -119,14 +127,14 @@ class PodcastManager {
 | 
				
			|||||||
      if (!success) {
 | 
					      if (!success) {
 | 
				
			||||||
        await fs.remove(this.currentDownload.targetPath)
 | 
					        await fs.remove(this.currentDownload.targetPath)
 | 
				
			||||||
        this.currentDownload.setFinished(false)
 | 
					        this.currentDownload.setFinished(false)
 | 
				
			||||||
        task.setFailed('Failed to download episode')
 | 
					        task.setFailedText('Failed to download episode')
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        Logger.info(`[PodcastManager] Successfully downloaded podcast episode "${this.currentDownload.podcastEpisode.title}"`)
 | 
					        Logger.info(`[PodcastManager] Successfully downloaded podcast episode "${this.currentDownload.podcastEpisode.title}"`)
 | 
				
			||||||
        this.currentDownload.setFinished(true)
 | 
					        this.currentDownload.setFinished(true)
 | 
				
			||||||
        task.setFinished()
 | 
					        task.setFinished()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      task.setFailed('Failed to download episode')
 | 
					      task.setFailedText('Failed to download episode')
 | 
				
			||||||
      this.currentDownload.setFinished(false)
 | 
					      this.currentDownload.setFinished(false)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -407,13 +415,35 @@ class PodcastManager {
 | 
				
			|||||||
   * @param {import('../managers/CronManager')} cronManager
 | 
					   * @param {import('../managers/CronManager')} cronManager
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  async createPodcastsFromFeedUrls(rssFeedUrls, folder, autoDownloadEpisodes, cronManager) {
 | 
					  async createPodcastsFromFeedUrls(rssFeedUrls, folder, autoDownloadEpisodes, cronManager) {
 | 
				
			||||||
    const task = TaskManager.createAndAddTask('opml-import', 'OPML import', `Creating podcasts from ${rssFeedUrls.length} RSS feeds`, true, null)
 | 
					    const taskTitleString = {
 | 
				
			||||||
 | 
					      text: 'OPML import',
 | 
				
			||||||
 | 
					      key: 'MessageTaskOpmlImport'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const taskDescriptionString = {
 | 
				
			||||||
 | 
					      text: `Creating podcasts from ${rssFeedUrls.length} RSS feeds`,
 | 
				
			||||||
 | 
					      key: 'MessageTaskOpmlImportDescription',
 | 
				
			||||||
 | 
					      subs: [rssFeedUrls.length]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const task = TaskManager.createAndAddTask('opml-import', taskTitleString, taskDescriptionString, true, null)
 | 
				
			||||||
    let numPodcastsAdded = 0
 | 
					    let numPodcastsAdded = 0
 | 
				
			||||||
    Logger.info(`[PodcastManager] createPodcastsFromFeedUrls: Importing ${rssFeedUrls.length} RSS feeds to folder "${folder.path}"`)
 | 
					    Logger.info(`[PodcastManager] createPodcastsFromFeedUrls: Importing ${rssFeedUrls.length} RSS feeds to folder "${folder.path}"`)
 | 
				
			||||||
    for (const feedUrl of rssFeedUrls) {
 | 
					    for (const feedUrl of rssFeedUrls) {
 | 
				
			||||||
      const feed = await getPodcastFeed(feedUrl).catch(() => null)
 | 
					      const feed = await getPodcastFeed(feedUrl).catch(() => null)
 | 
				
			||||||
      if (!feed?.episodes) {
 | 
					      if (!feed?.episodes) {
 | 
				
			||||||
        TaskManager.createAndEmitFailedTask('opml-import-feed', 'OPML import feed', `Importing RSS feed "${feedUrl}"`, 'Failed to get podcast feed')
 | 
					        const taskTitleStringFeed = {
 | 
				
			||||||
 | 
					          text: 'OPML import feed',
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeed'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const taskDescriptionStringFeed = {
 | 
				
			||||||
 | 
					          text: `Importing RSS feed "${feedUrl}"`,
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeedDescription',
 | 
				
			||||||
 | 
					          subs: [feedUrl]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const taskErrorString = {
 | 
				
			||||||
 | 
					          text: 'Failed to get podcast feed',
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeedFailed'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        TaskManager.createAndEmitFailedTask('opml-import-feed', taskTitleStringFeed, taskDescriptionStringFeed, taskErrorString)
 | 
				
			||||||
        Logger.error(`[PodcastManager] createPodcastsFromFeedUrls: Failed to get podcast feed for "${feedUrl}"`)
 | 
					        Logger.error(`[PodcastManager] createPodcastsFromFeedUrls: Failed to get podcast feed for "${feedUrl}"`)
 | 
				
			||||||
        continue
 | 
					        continue
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -429,7 +459,20 @@ class PodcastManager {
 | 
				
			|||||||
        })) > 0
 | 
					        })) > 0
 | 
				
			||||||
      if (existingLibraryItem) {
 | 
					      if (existingLibraryItem) {
 | 
				
			||||||
        Logger.error(`[PodcastManager] createPodcastsFromFeedUrls: Podcast already exists at path "${podcastPath}"`)
 | 
					        Logger.error(`[PodcastManager] createPodcastsFromFeedUrls: Podcast already exists at path "${podcastPath}"`)
 | 
				
			||||||
        TaskManager.createAndEmitFailedTask('opml-import-feed', 'OPML import feed', `Creating podcast "${feed.metadata.title}"`, 'Podcast already exists at path')
 | 
					        const taskTitleStringFeed = {
 | 
				
			||||||
 | 
					          text: 'OPML import feed',
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeed'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const taskDescriptionStringPodcast = {
 | 
				
			||||||
 | 
					          text: `Creating podcast "${feed.metadata.title}"`,
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeedPodcastDescription',
 | 
				
			||||||
 | 
					          subs: [feed.metadata.title]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const taskErrorString = {
 | 
				
			||||||
 | 
					          text: 'Podcast already exists at path',
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeedPodcastExists'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        TaskManager.createAndEmitFailedTask('opml-import-feed', taskTitleStringFeed, taskDescriptionStringPodcast, taskErrorString)
 | 
				
			||||||
        continue
 | 
					        continue
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -442,7 +485,20 @@ class PodcastManager {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
      if (!successCreatingPath) {
 | 
					      if (!successCreatingPath) {
 | 
				
			||||||
        Logger.error(`[PodcastManager] createPodcastsFromFeedUrls: Failed to create podcast folder at "${podcastPath}"`)
 | 
					        Logger.error(`[PodcastManager] createPodcastsFromFeedUrls: Failed to create podcast folder at "${podcastPath}"`)
 | 
				
			||||||
        TaskManager.createAndEmitFailedTask('opml-import-feed', 'OPML import feed', `Creating podcast "${feed.metadata.title}"`, 'Failed to create podcast folder')
 | 
					        const taskTitleStringFeed = {
 | 
				
			||||||
 | 
					          text: 'OPML import feed',
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeed'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const taskDescriptionStringPodcast = {
 | 
				
			||||||
 | 
					          text: `Creating podcast "${feed.metadata.title}"`,
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeedPodcastDescription',
 | 
				
			||||||
 | 
					          subs: [feed.metadata.title]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const taskErrorString = {
 | 
				
			||||||
 | 
					          text: 'Failed to create podcast folder',
 | 
				
			||||||
 | 
					          key: 'MessageTaskOpmlImportFeedPodcastFailed'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        TaskManager.createAndEmitFailedTask('opml-import-feed', taskTitleStringFeed, taskDescriptionStringPodcast, taskErrorString)
 | 
				
			||||||
        continue
 | 
					        continue
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,13 @@
 | 
				
			|||||||
const SocketAuthority = require('../SocketAuthority')
 | 
					const SocketAuthority = require('../SocketAuthority')
 | 
				
			||||||
const Task = require('../objects/Task')
 | 
					const Task = require('../objects/Task')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @typedef TaskString
 | 
				
			||||||
 | 
					 * @property {string} text
 | 
				
			||||||
 | 
					 * @property {string} key
 | 
				
			||||||
 | 
					 * @property {string[]} [subs]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TaskManager {
 | 
					class TaskManager {
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    /** @type {Task[]} */
 | 
					    /** @type {Task[]} */
 | 
				
			||||||
@ -33,14 +40,14 @@ class TaskManager {
 | 
				
			|||||||
   * Create new task and add
 | 
					   * Create new task and add
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @param {string} action
 | 
					   * @param {string} action
 | 
				
			||||||
   * @param {string} title
 | 
					   * @param {TaskString} titleString
 | 
				
			||||||
   * @param {string} description
 | 
					   * @param {TaskString|null} descriptionString
 | 
				
			||||||
   * @param {boolean} showSuccess
 | 
					   * @param {boolean} showSuccess
 | 
				
			||||||
   * @param {Object} [data]
 | 
					   * @param {Object} [data]
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  createAndAddTask(action, title, description, showSuccess, data = {}) {
 | 
					  createAndAddTask(action, titleString, descriptionString, showSuccess, data = {}) {
 | 
				
			||||||
    const task = new Task()
 | 
					    const task = new Task()
 | 
				
			||||||
    task.setData(action, title, description, showSuccess, data)
 | 
					    task.setData(action, titleString, descriptionString, showSuccess, data)
 | 
				
			||||||
    this.addTask(task)
 | 
					    this.addTask(task)
 | 
				
			||||||
    return task
 | 
					    return task
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -49,14 +56,14 @@ class TaskManager {
 | 
				
			|||||||
   * Create new failed task and add
 | 
					   * Create new failed task and add
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @param {string} action
 | 
					   * @param {string} action
 | 
				
			||||||
   * @param {string} title
 | 
					   * @param {TaskString} titleString
 | 
				
			||||||
   * @param {string} description
 | 
					   * @param {TaskString|null} descriptionString
 | 
				
			||||||
   * @param {string} errorMessage
 | 
					   * @param {TaskString} errorMessageString
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  createAndEmitFailedTask(action, title, description, errorMessage) {
 | 
					  createAndEmitFailedTask(action, titleString, descriptionString, errorMessageString) {
 | 
				
			||||||
    const task = new Task()
 | 
					    const task = new Task()
 | 
				
			||||||
    task.setData(action, title, description, false)
 | 
					    task.setData(action, titleString, descriptionString, false)
 | 
				
			||||||
    task.setFailed(errorMessage)
 | 
					    task.setFailedText(errorMessageString)
 | 
				
			||||||
    SocketAuthority.emitter('task_started', task.toJSON())
 | 
					    SocketAuthority.emitter('task_started', task.toJSON())
 | 
				
			||||||
    return task
 | 
					    return task
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,11 @@
 | 
				
			|||||||
const uuidv4 = require("uuid").v4
 | 
					const uuidv4 = require('uuid').v4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @typedef TaskString
 | 
				
			||||||
 | 
					 * @property {string} text
 | 
				
			||||||
 | 
					 * @property {string} key
 | 
				
			||||||
 | 
					 * @property {string[]} [subs]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Task {
 | 
					class Task {
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
@ -11,10 +18,25 @@ class Task {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /** @type {string} */
 | 
					    /** @type {string} */
 | 
				
			||||||
    this.title = null
 | 
					    this.title = null
 | 
				
			||||||
 | 
					    /** @type {string} - Used for translation */
 | 
				
			||||||
 | 
					    this.titleKey = null
 | 
				
			||||||
 | 
					    /** @type {string[]} - Used for translation */
 | 
				
			||||||
 | 
					    this.titleSubs = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @type {string} */
 | 
					    /** @type {string} */
 | 
				
			||||||
    this.description = null
 | 
					    this.description = null
 | 
				
			||||||
 | 
					    /** @type {string} - Used for translation */
 | 
				
			||||||
 | 
					    this.descriptionKey = null
 | 
				
			||||||
 | 
					    /** @type {string[]} - Used for translation */
 | 
				
			||||||
 | 
					    this.descriptionSubs = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @type {string} */
 | 
					    /** @type {string} */
 | 
				
			||||||
    this.error = null
 | 
					    this.error = null
 | 
				
			||||||
 | 
					    /** @type {string} - Used for translation */
 | 
				
			||||||
 | 
					    this.errorKey = null
 | 
				
			||||||
 | 
					    /** @type {string[]} - Used for translation */
 | 
				
			||||||
 | 
					    this.errorSubs = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @type {boolean} client should keep the task visible after success */
 | 
					    /** @type {boolean} client should keep the task visible after success */
 | 
				
			||||||
    this.showSuccess = false
 | 
					    this.showSuccess = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,17 +71,21 @@ class Task {
 | 
				
			|||||||
   * Set initial task data
 | 
					   * Set initial task data
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @param {string} action
 | 
					   * @param {string} action
 | 
				
			||||||
   * @param {string} title 
 | 
					   * @param {TaskString} titleString
 | 
				
			||||||
   * @param {string} description 
 | 
					   * @param {TaskString|null} descriptionString
 | 
				
			||||||
   * @param {boolean} showSuccess
 | 
					   * @param {boolean} showSuccess
 | 
				
			||||||
   * @param {Object} [data]
 | 
					   * @param {Object} [data]
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  setData(action, title, description, showSuccess, data = {}) {
 | 
					  setData(action, titleString, descriptionString, showSuccess, data = {}) {
 | 
				
			||||||
    this.id = uuidv4()
 | 
					    this.id = uuidv4()
 | 
				
			||||||
    this.action = action
 | 
					    this.action = action
 | 
				
			||||||
    this.data = { ...data }
 | 
					    this.data = { ...data }
 | 
				
			||||||
    this.title = title
 | 
					    this.title = titleString.text
 | 
				
			||||||
    this.description = description
 | 
					    this.titleKey = titleString.key || null
 | 
				
			||||||
 | 
					    this.titleSubs = titleString.subs || null
 | 
				
			||||||
 | 
					    this.description = descriptionString?.text || null
 | 
				
			||||||
 | 
					    this.descriptionKey = descriptionString?.key || null
 | 
				
			||||||
 | 
					    this.descriptionSubs = descriptionString?.subs || null
 | 
				
			||||||
    this.showSuccess = showSuccess
 | 
					    this.showSuccess = showSuccess
 | 
				
			||||||
    this.startedAt = Date.now()
 | 
					    this.startedAt = Date.now()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -67,10 +93,27 @@ class Task {
 | 
				
			|||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Set task as failed
 | 
					   * Set task as failed
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @param {string} message error message
 | 
					   * @param {TaskString} messageString
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  setFailed(message) {
 | 
					  setFailed(messageString) {
 | 
				
			||||||
 | 
					    this.error = messageString.text
 | 
				
			||||||
 | 
					    this.errorKey = messageString.key || null
 | 
				
			||||||
 | 
					    this.errorSubs = messageString.subs || null
 | 
				
			||||||
 | 
					    this.isFailed = true
 | 
				
			||||||
 | 
					    this.failedAt = Date.now()
 | 
				
			||||||
 | 
					    this.setFinished()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Set task as failed without translation key
 | 
				
			||||||
 | 
					   * TODO: Remove this method after all tasks are using translation keys
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param {string} message
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  setFailedText(message) {
 | 
				
			||||||
    this.error = message
 | 
					    this.error = message
 | 
				
			||||||
 | 
					    this.errorKey = null
 | 
				
			||||||
 | 
					    this.errorSubs = null
 | 
				
			||||||
    this.isFailed = true
 | 
					    this.isFailed = true
 | 
				
			||||||
    this.failedAt = Date.now()
 | 
					    this.failedAt = Date.now()
 | 
				
			||||||
    this.setFinished()
 | 
					    this.setFinished()
 | 
				
			||||||
@ -78,12 +121,15 @@ class Task {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Set task as finished
 | 
					   * Set task as finished
 | 
				
			||||||
 | 
					   * TODO: Update to use translation keys
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
   * @param {string} [newDescription] update description
 | 
					   * @param {string} [newDescription] update description
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  setFinished(newDescription = null) {
 | 
					  setFinished(newDescription = null) {
 | 
				
			||||||
    if (newDescription) {
 | 
					    if (newDescription) {
 | 
				
			||||||
      this.description = newDescription
 | 
					      this.description = newDescription
 | 
				
			||||||
 | 
					      this.descriptionKey = null
 | 
				
			||||||
 | 
					      this.descriptionSubs = null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.isFinished = true
 | 
					    this.isFinished = true
 | 
				
			||||||
    this.finishedAt = Date.now()
 | 
					    this.finishedAt = Date.now()
 | 
				
			||||||
 | 
				
			|||||||
@ -76,7 +76,12 @@ class LibraryScanner {
 | 
				
			|||||||
      libraryName: library.name,
 | 
					      libraryName: library.name,
 | 
				
			||||||
      libraryMediaType: library.mediaType
 | 
					      libraryMediaType: library.mediaType
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const task = TaskManager.createAndAddTask('library-scan', `Scanning "${library.name}" library`, null, true, taskData)
 | 
					    const taskTitleString = {
 | 
				
			||||||
 | 
					      text: `Scanning "${library.name}" library`,
 | 
				
			||||||
 | 
					      key: 'MessageTaskScanningLibrary',
 | 
				
			||||||
 | 
					      subs: [library.name]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const task = TaskManager.createAndAddTask('library-scan', taskTitleString, null, true, taskData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Logger.info(`[LibraryScanner] Starting${forceRescan ? ' (forced)' : ''} library scan ${libraryScan.id} for ${libraryScan.libraryName}`)
 | 
					    Logger.info(`[LibraryScanner] Starting${forceRescan ? ' (forced)' : ''} library scan ${libraryScan.id} for ${libraryScan.libraryName}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -104,7 +109,7 @@ class LibraryScanner {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      Logger.error(`[LibraryScanner] Library scan ${libraryScan.id} failed after ${libraryScan.elapsedTimestamp} | ${libraryScan.resultStats}.`, err)
 | 
					      Logger.error(`[LibraryScanner] Library scan ${libraryScan.id} failed after ${libraryScan.elapsedTimestamp} | ${libraryScan.resultStats}.`, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      task.setFailed(`Failed. ${libraryScan.scanResultsString}`)
 | 
					      task.setFailedText(`Failed. ${libraryScan.scanResultsString}`)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (this.cancelLibraryScan[libraryScan.libraryId]) delete this.cancelLibraryScan[libraryScan.libraryId]
 | 
					    if (this.cancelLibraryScan[libraryScan.libraryId]) delete this.cancelLibraryScan[libraryScan.libraryId]
 | 
				
			||||||
 | 
				
			|||||||
@ -368,7 +368,12 @@ class Scanner {
 | 
				
			|||||||
    const taskData = {
 | 
					    const taskData = {
 | 
				
			||||||
      libraryId: library.id
 | 
					      libraryId: library.id
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const task = TaskManager.createAndAddTask('library-match-all', `Matching books in "${library.name}"`, null, true, taskData)
 | 
					    const taskTitleString = {
 | 
				
			||||||
 | 
					      text: `Matching books in "${library.name}"`,
 | 
				
			||||||
 | 
					      key: 'MessageTaskMatchingBooksInLibrary',
 | 
				
			||||||
 | 
					      subs: [library.name]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const task = TaskManager.createAndAddTask('library-match-all', taskTitleString, null, true, taskData)
 | 
				
			||||||
    Logger.info(`[Scanner] matchLibraryItems: Starting library match scan ${libraryScan.id} for ${libraryScan.libraryName}`)
 | 
					    Logger.info(`[Scanner] matchLibraryItems: Starting library match scan ${libraryScan.id} for ${libraryScan.libraryName}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let hasMoreChunks = true
 | 
					    let hasMoreChunks = true
 | 
				
			||||||
@ -393,7 +398,7 @@ class Scanner {
 | 
				
			|||||||
    if (offset === 0) {
 | 
					    if (offset === 0) {
 | 
				
			||||||
      Logger.error(`[Scanner] matchLibraryItems: Library has no items ${library.id}`)
 | 
					      Logger.error(`[Scanner] matchLibraryItems: Library has no items ${library.id}`)
 | 
				
			||||||
      libraryScan.setComplete('Library has no items')
 | 
					      libraryScan.setComplete('Library has no items')
 | 
				
			||||||
      task.setFailed(libraryScan.error)
 | 
					      task.setFailedText(libraryScan.error)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      libraryScan.setComplete()
 | 
					      libraryScan.setComplete()
 | 
				
			||||||
      task.setFinished(isCanceled ? 'Canceled' : libraryScan.scanResultsString)
 | 
					      task.setFinished(isCanceled ? 'Canceled' : libraryScan.scanResultsString)
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user