Add sampleRate and profile extraction for audio files

- Extract sampleRate and profile from audio streams in ffprobe output
- Store sampleRate and profile in AudioFile objects
- Expose sampleRate and profile through API endpoints
- Add JSDoc documentation for new fields
This commit is contained in:
Quentin King 2026-01-03 01:19:05 -06:00
parent 122fc34a75
commit fadd14484e
4 changed files with 18 additions and 5 deletions

View file

@ -61,6 +61,8 @@ const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilter
* @property {string} timeBase * @property {string} timeBase
* @property {number} channels * @property {number} channels
* @property {string} channelLayout * @property {string} channelLayout
* @property {number} sampleRate
* @property {string} profile
* @property {ChapterObject[]} chapters * @property {ChapterObject[]} chapters
* @property {Object} metaTags * @property {Object} metaTags
* @property {string} mimeType * @property {string} mimeType

View file

@ -24,6 +24,8 @@ class AudioFile {
this.timeBase = null this.timeBase = null
this.channels = null this.channels = null
this.channelLayout = null this.channelLayout = null
this.sampleRate = null
this.profile = null
this.chapters = [] this.chapters = []
this.embeddedCoverArt = null this.embeddedCoverArt = null
@ -62,6 +64,8 @@ class AudioFile {
timeBase: this.timeBase, timeBase: this.timeBase,
channels: this.channels, channels: this.channels,
channelLayout: this.channelLayout, channelLayout: this.channelLayout,
sampleRate: this.sampleRate,
profile: this.profile,
chapters: this.chapters, chapters: this.chapters,
embeddedCoverArt: this.embeddedCoverArt, embeddedCoverArt: this.embeddedCoverArt,
metaTags: this.metaTags?.toJSON() || {}, metaTags: this.metaTags?.toJSON() || {},
@ -94,6 +98,8 @@ class AudioFile {
this.timeBase = data.timeBase this.timeBase = data.timeBase
this.channels = data.channels this.channels = data.channels
this.channelLayout = data.channelLayout this.channelLayout = data.channelLayout
this.sampleRate = data.sampleRate
this.profile = data.profile
this.chapters = data.chapters this.chapters = data.chapters
this.embeddedCoverArt = data.embeddedCoverArt || null this.embeddedCoverArt = data.embeddedCoverArt || null
@ -130,6 +136,8 @@ class AudioFile {
this.timeBase = probeData.timeBase this.timeBase = probeData.timeBase
this.channels = probeData.channels this.channels = probeData.channels
this.channelLayout = probeData.channelLayout this.channelLayout = probeData.channelLayout
this.sampleRate = probeData.sampleRate
this.profile = probeData.profile
this.chapters = probeData.chapters || [] this.chapters = probeData.chapters || []
this.metaTags = probeData.audioMetaTags this.metaTags = probeData.audioMetaTags
this.embeddedCoverArt = probeData.embeddedCoverArt this.embeddedCoverArt = probeData.embeddedCoverArt
@ -137,7 +145,7 @@ class AudioFile {
syncChapters(updatedChapters) { syncChapters(updatedChapters) {
if (this.chapters.length !== updatedChapters.length) { if (this.chapters.length !== updatedChapters.length) {
this.chapters = updatedChapters.map(ch => ({ ...ch })) this.chapters = updatedChapters.map((ch) => ({ ...ch }))
return true return true
} else if (updatedChapters.length === 0) { } else if (updatedChapters.length === 0) {
if (this.chapters.length > 0) { if (this.chapters.length > 0) {
@ -154,7 +162,7 @@ class AudioFile {
} }
} }
if (hasUpdates) { if (hasUpdates) {
this.chapters = updatedChapters.map(ch => ({ ...ch })) this.chapters = updatedChapters.map((ch) => ({ ...ch }))
} }
return hasUpdates return hasUpdates
} }
@ -164,8 +172,8 @@ class AudioFile {
} }
/** /**
* *
* @param {AudioFile} scannedAudioFile * @param {AudioFile} scannedAudioFile
* @returns {boolean} true if updates were made * @returns {boolean} true if updates were made
*/ */
updateFromScan(scannedAudioFile) { updateFromScan(scannedAudioFile) {
@ -196,4 +204,4 @@ class AudioFile {
return hasUpdated return hasUpdated
} }
} }
module.exports = AudioFile module.exports = AudioFile

View file

@ -17,6 +17,7 @@ class MediaProbeData {
this.channelLayout = null this.channelLayout = null
this.channels = null this.channels = null
this.sampleRate = null this.sampleRate = null
this.profile = null
this.chapters = [] this.chapters = []
this.audioMetaTags = null this.audioMetaTags = null
@ -58,6 +59,7 @@ class MediaProbeData {
this.channelLayout = this.audioStream.channel_layout this.channelLayout = this.audioStream.channel_layout
this.channels = this.audioStream.channels this.channels = this.audioStream.channels
this.sampleRate = this.audioStream.sample_rate this.sampleRate = this.audioStream.sample_rate
this.profile = this.audioStream.profile
this.chapters = data.chapters || [] this.chapters = data.chapters || []
this.audioMetaTags = new AudioMetaTags() this.audioMetaTags = new AudioMetaTags()

View file

@ -114,6 +114,7 @@ function parseMediaStreamInfo(stream, all_streams, total_bit_rate) {
info.channels = stream.channels || null info.channels = stream.channels || null
info.sample_rate = tryGrabSampleRate(stream) info.sample_rate = tryGrabSampleRate(stream)
info.channel_layout = tryGrabChannelLayout(stream) info.channel_layout = tryGrabChannelLayout(stream)
info.profile = stream.profile || null
} }
return info return info