mirror of
https://github.com/TeamPiped/Piped.git
synced 2024-08-30 18:43:17 +00:00
190 lines
6.7 KiB
JavaScript
190 lines
6.7 KiB
JavaScript
|
// Based of https://github.com/GilgusMaximus/yt-dash-manifest-generator/blob/master/src/DashGenerator.js
|
||
|
|
||
|
const xml = require('xml-js')
|
||
|
|
||
|
const DashUtils = {
|
||
|
generate_dash_file_from_formats(VideoFormats, VideoLength) {
|
||
|
const generatedJSON = this.generate_xmljs_json_from_data(VideoFormats, VideoLength)
|
||
|
return xml.json2xml(generatedJSON)
|
||
|
},
|
||
|
generate_xmljs_json_from_data(VideoFormatArray, VideoLength) {
|
||
|
const convertJSON = {
|
||
|
"declaration": {
|
||
|
"attributes": {
|
||
|
"version": "1.0",
|
||
|
"encoding": "utf-8"
|
||
|
}
|
||
|
},
|
||
|
"elements": [
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "MPD",
|
||
|
"attributes": {
|
||
|
"xmlns": "urn:mpeg:dash:schema:mpd:2011",
|
||
|
"profiles": "urn:mpeg:dash:profile:full:2011",
|
||
|
"minBufferTime": "PT1.5S",
|
||
|
"type": "static",
|
||
|
"mediaPresentationDuration": `PT${VideoLength}S`
|
||
|
},
|
||
|
"elements": [
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "Period",
|
||
|
"elements": this.generate_adaptation_set(VideoFormatArray)
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
return convertJSON
|
||
|
},
|
||
|
generate_adaptation_set(VideoFormatArray) {
|
||
|
const adaptationSets = []
|
||
|
const mimeTypes = []
|
||
|
const mimeObjects = [[]]
|
||
|
// sort the formats by mime types
|
||
|
VideoFormatArray.forEach((videoFormat) => {
|
||
|
// the dual formats should not be used
|
||
|
if (videoFormat.mimeType.indexOf("video") != -1 && !videoFormat.videoOnly) {
|
||
|
return
|
||
|
}
|
||
|
// if these properties are not available, then we skip it because we cannot set these properties
|
||
|
//if (!(videoFormat.hasOwnProperty('initRange') && videoFormat.hasOwnProperty('indexRange'))) {
|
||
|
// return
|
||
|
//}
|
||
|
const mimeType = videoFormat.mimeType
|
||
|
const mimeTypeIndex = mimeTypes.indexOf(mimeType)
|
||
|
if (mimeTypeIndex > -1) {
|
||
|
mimeObjects[mimeTypeIndex].push(videoFormat)
|
||
|
} else {
|
||
|
mimeTypes.push(mimeType)
|
||
|
mimeObjects.push([])
|
||
|
mimeObjects[mimeTypes.length - 1].push(videoFormat)
|
||
|
}
|
||
|
})
|
||
|
// for each MimeType generate a new Adaptation set with Representations as sub elements
|
||
|
for (let i = 0; i < mimeTypes.length; i++) {
|
||
|
let isVideoFormat = false
|
||
|
const adapSet = {
|
||
|
"type": "element",
|
||
|
"name": "AdaptationSet",
|
||
|
"attributes": {
|
||
|
"id": i,
|
||
|
"mimeType": mimeTypes[i],
|
||
|
"startWithSAP": "1",
|
||
|
"subsegmentAlignment": "true"
|
||
|
},
|
||
|
"elements": []
|
||
|
}
|
||
|
if (!mimeTypes[i].includes("audio")) {
|
||
|
adapSet.attributes.scanType = "progressive"
|
||
|
isVideoFormat = true
|
||
|
}
|
||
|
mimeObjects[i].forEach((format) => {
|
||
|
if (isVideoFormat) {
|
||
|
adapSet.elements.push(this.generate_representation_video(format))
|
||
|
} else {
|
||
|
adapSet.elements.push(this.generate_representation_audio(format))
|
||
|
}
|
||
|
})
|
||
|
adaptationSets.push(adapSet)
|
||
|
}
|
||
|
return adaptationSets
|
||
|
}, generate_representation_audio(Format) {
|
||
|
const representation =
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "Representation",
|
||
|
"attributes": {
|
||
|
"id": Format.itag,
|
||
|
"codecs": Format.codec,
|
||
|
"bandwidth": Format.bitrate
|
||
|
},
|
||
|
"elements": [
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "AudioChannelConfiguration",
|
||
|
"attributes": {
|
||
|
"schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011",
|
||
|
"value": "2"
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "BaseURL",
|
||
|
"elements": [
|
||
|
{
|
||
|
"type": "text",
|
||
|
"text": Format.url
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "SegmentBase",
|
||
|
"attributes": {
|
||
|
"indexRange": `${Format.indexStart}-${Format.indexEnd}`
|
||
|
},
|
||
|
"elements": [
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "Initialization",
|
||
|
"attributes": {
|
||
|
"range": `${Format.initStart}-${Format.initEnd}`
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
return representation
|
||
|
},
|
||
|
generate_representation_video(Format) {
|
||
|
const representation =
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "Representation",
|
||
|
"attributes": {
|
||
|
"id": Format.itag,
|
||
|
"codecs": Format.codec,
|
||
|
"bandwidth": Format.bitrate,
|
||
|
"width": Format.width,
|
||
|
"height": Format.height,
|
||
|
"maxPlayoutRate": "1",
|
||
|
"frameRate": Format.fps
|
||
|
},
|
||
|
"elements": [
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "BaseURL",
|
||
|
"elements": [
|
||
|
{
|
||
|
"type": "text",
|
||
|
"text": Format.url
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "SegmentBase",
|
||
|
"attributes": {
|
||
|
"indexRange": `${Format.indexStart}-${Format.indexEnd}`
|
||
|
},
|
||
|
"elements": [
|
||
|
{
|
||
|
"type": "element",
|
||
|
"name": "Initialization",
|
||
|
"attributes": {
|
||
|
"range": `${Format.initStart}-${Format.initEnd}`
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
return representation
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export default DashUtils;
|