mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
- Introduces a new Music Library system for managing favorites (tracks, playlists, releases), replacing the previous TrackLike model. - Completely revamps the Studio TV profile page, adding live statistics, stream configuration, restream management, and media URL display. - Enhances the media player with a custom seekbar and improved audio playback logic for MPD and non-MPD sources. - Lays foundational groundwork for chat encryption with new models and APIs. - Refactors critical UI components like PlaylistView and PagePanel. - Standardizes monorepo development scripts to use npm. - Updates comty.js submodule and adds various new UI components.
153 lines
3.5 KiB
JavaScript
153 lines
3.5 KiB
JavaScript
import fs from "node:fs"
|
|
import path from "node:path"
|
|
|
|
import { FFMPEGLib, Utils } from "../FFMPEGLib"
|
|
|
|
export default class SegmentedAudioMPDJob extends FFMPEGLib {
|
|
constructor(params = {}) {
|
|
super()
|
|
|
|
this.params = {
|
|
outputMasterName: "master.mpd",
|
|
audioCodec: "libopus",
|
|
audioBitrate: "320k",
|
|
audioSampleRate: "48000",
|
|
segmentTime: 10,
|
|
minBufferTime: 5,
|
|
includeMetadata: true,
|
|
...params,
|
|
}
|
|
}
|
|
|
|
buildSegmentationArgs = () => {
|
|
const args = [
|
|
`-v error -hide_banner -progress pipe:1`,
|
|
`-i ${this.params.input}`,
|
|
`-c:a ${this.params.audioCodec}`,
|
|
`-map 0:a`,
|
|
`-f dash`,
|
|
`-dash_segment_type mp4`,
|
|
`-segment_time ${this.params.segmentTime}`,
|
|
`-use_template 1`,
|
|
`-use_timeline 1`,
|
|
`-init_seg_name "init.m4s"`,
|
|
]
|
|
|
|
if (this.params.includeMetadata === false) {
|
|
args.push(`-map_metadata -1`)
|
|
}
|
|
|
|
if (
|
|
typeof this.params.audioBitrate === "string" &&
|
|
this.params.audioBitrate !== "default"
|
|
) {
|
|
args.push(`-b:a ${this.params.audioBitrate}`)
|
|
}
|
|
|
|
if (
|
|
typeof this.params.audioSampleRate !== "undefined" &&
|
|
this.params.audioSampleRate !== "default"
|
|
) {
|
|
args.push(`-ar ${this.params.audioSampleRate}`)
|
|
}
|
|
|
|
args.push(this.params.outputMasterName)
|
|
|
|
return args
|
|
}
|
|
|
|
_updateMpdMinBufferTime = async (mpdPath, newMinBufferTimeSecs) => {
|
|
try {
|
|
const mpdTagRegex = /(<MPD[^>]*)/
|
|
let mpdContent = await fs.promises.readFile(mpdPath, "utf-8")
|
|
|
|
const minBufferTimeAttribute = `minBufferTime="PT${newMinBufferTimeSecs}.0S"`
|
|
const existingMinBufferTimeRegex =
|
|
/(<MPD[^>]*minBufferTime=")[^"]*(")/
|
|
|
|
if (existingMinBufferTimeRegex.test(mpdContent)) {
|
|
mpdContent = mpdContent.replace(
|
|
existingMinBufferTimeRegex,
|
|
`$1PT${newMinBufferTimeSecs}.0S$2`,
|
|
)
|
|
await fs.promises.writeFile(mpdPath, mpdContent, "utf-8")
|
|
} else {
|
|
if (mpdTagRegex.test(mpdContent)) {
|
|
mpdContent = mpdContent.replace(
|
|
mpdTagRegex,
|
|
`$1 ${minBufferTimeAttribute}`,
|
|
)
|
|
|
|
await fs.promises.writeFile(mpdPath, mpdContent, "utf-8")
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(
|
|
`[SegmentedAudioMPDJob] Error updating MPD minBufferTime for ${mpdPath}:`,
|
|
error,
|
|
)
|
|
}
|
|
}
|
|
|
|
run = async () => {
|
|
const segmentationCmd = this.buildSegmentationArgs()
|
|
const outputPath =
|
|
this.params.outputDir ?? `${path.dirname(this.params.input)}/dash`
|
|
const outputFile = path.join(outputPath, this.params.outputMasterName)
|
|
|
|
this.emit("start", {
|
|
input: this.params.input,
|
|
output: outputPath,
|
|
params: this.params,
|
|
})
|
|
|
|
if (!fs.existsSync(outputPath)) {
|
|
fs.mkdirSync(outputPath, { recursive: true })
|
|
}
|
|
|
|
const inputProbe = await Utils.probe(this.params.input)
|
|
|
|
try {
|
|
const ffmpegResult = await this.ffmpeg({
|
|
args: segmentationCmd,
|
|
onProcess: (process) => {
|
|
this.handleProgress(
|
|
process.stdout,
|
|
parseFloat(inputProbe.format.duration),
|
|
(progress) => {
|
|
this.emit("progress", progress)
|
|
},
|
|
)
|
|
},
|
|
cwd: outputPath,
|
|
})
|
|
|
|
if (fs.existsSync(outputFile)) {
|
|
await this._updateMpdMinBufferTime(
|
|
outputFile,
|
|
this.params.minBufferTime,
|
|
)
|
|
} else {
|
|
console.warn(
|
|
`[SegmentedAudioMPDJob] MPD file ${outputFile} not found after ffmpeg run. Skipping minBufferTime update.`,
|
|
)
|
|
}
|
|
|
|
let outputProbe = await Utils.probe(outputFile)
|
|
|
|
this.emit("end", {
|
|
probe: {
|
|
input: inputProbe,
|
|
output: outputProbe,
|
|
},
|
|
outputPath: outputPath,
|
|
outputFile: outputFile,
|
|
})
|
|
|
|
return ffmpegResult
|
|
} catch (err) {
|
|
this.emit("error", err)
|
|
}
|
|
}
|
|
}
|