2024-09-06 13:28:12 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-09-06 14:27:09 +02:00
|
|
|
"fmt"
|
2024-09-06 13:28:12 +02:00
|
|
|
"net/url"
|
|
|
|
"regexp"
|
2024-09-06 14:27:09 +02:00
|
|
|
"strconv"
|
2024-09-06 13:28:12 +02:00
|
|
|
"strings"
|
2024-09-06 14:27:09 +02:00
|
|
|
|
|
|
|
"github.com/beevik/etree"
|
2024-09-06 13:28:12 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func sanitizeFilename(filename string) string {
|
|
|
|
filename = regexp.MustCompile(`[<>:"/\\|?*]`).ReplaceAllString(filename, "_")
|
|
|
|
|
|
|
|
filename = strings.Trim(filename, ".")
|
|
|
|
|
|
|
|
return filename
|
|
|
|
}
|
|
|
|
|
|
|
|
func isValidURL(toTest string) bool {
|
|
|
|
_, err := url.ParseRequestURI(toTest)
|
|
|
|
return err == nil
|
|
|
|
}
|
2024-09-06 14:27:09 +02:00
|
|
|
|
|
|
|
func fixGoPlay(mpdContent string) (string, error) {
|
|
|
|
doc := etree.NewDocument()
|
|
|
|
if err := doc.ReadFromString(mpdContent); err != nil {
|
|
|
|
return "", fmt.Errorf("error parsing MPD content: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
root := doc.Root()
|
|
|
|
|
|
|
|
// Remove ad periods
|
|
|
|
for _, period := range root.SelectElements("Period") {
|
|
|
|
if strings.Contains(period.SelectAttrValue("id", ""), "-ad-") {
|
|
|
|
root.RemoveChild(period)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find highest bandwidth for video
|
|
|
|
highestBandwidth := 0
|
|
|
|
for _, adaptationSet := range root.FindElements("//AdaptationSet") {
|
|
|
|
if strings.Contains(adaptationSet.SelectAttrValue("mimeType", ""), "video") {
|
|
|
|
for _, representation := range adaptationSet.SelectElements("Representation") {
|
|
|
|
bandwidth, _ := strconv.Atoi(representation.SelectAttrValue("bandwidth", "0"))
|
|
|
|
if bandwidth > highestBandwidth {
|
|
|
|
highestBandwidth = bandwidth
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove lower bitrate representations
|
|
|
|
for _, adaptationSet := range root.FindElements("//AdaptationSet") {
|
|
|
|
if strings.Contains(adaptationSet.SelectAttrValue("mimeType", ""), "video") {
|
|
|
|
for _, representation := range adaptationSet.SelectElements("Representation") {
|
|
|
|
bandwidth, _ := strconv.Atoi(representation.SelectAttrValue("bandwidth", "0"))
|
|
|
|
if bandwidth != highestBandwidth {
|
|
|
|
adaptationSet.RemoveChild(representation)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Combine periods
|
|
|
|
periods := root.SelectElements("Period")
|
|
|
|
if len(periods) > 1 {
|
|
|
|
firstPeriod := periods[0]
|
|
|
|
var newVideoTimeline, newAudioTimeline *etree.Element
|
|
|
|
|
|
|
|
// Find or create SegmentTimeline elements
|
|
|
|
for _, adaptationSet := range firstPeriod.SelectElements("AdaptationSet") {
|
|
|
|
mimeType := adaptationSet.SelectAttrValue("mimeType", "")
|
|
|
|
if strings.Contains(mimeType, "video") && newVideoTimeline == nil {
|
|
|
|
newVideoTimeline = findOrCreateSegmentTimeline(adaptationSet)
|
|
|
|
} else if strings.Contains(mimeType, "audio") && newAudioTimeline == nil {
|
|
|
|
newAudioTimeline = findOrCreateSegmentTimeline(adaptationSet)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, period := range periods[1:] {
|
|
|
|
for _, adaptationSet := range period.SelectElements("AdaptationSet") {
|
|
|
|
mimeType := adaptationSet.SelectAttrValue("mimeType", "")
|
|
|
|
var timeline *etree.Element
|
|
|
|
if strings.Contains(mimeType, "video") {
|
|
|
|
timeline = newVideoTimeline
|
|
|
|
} else if strings.Contains(mimeType, "audio") {
|
|
|
|
timeline = newAudioTimeline
|
|
|
|
}
|
|
|
|
|
|
|
|
if timeline != nil {
|
|
|
|
segmentTimeline := findOrCreateSegmentTimeline(adaptationSet)
|
|
|
|
for _, s := range segmentTimeline.SelectElements("S") {
|
|
|
|
timeline.AddChild(s.Copy())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
root.RemoveChild(period)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return doc.WriteToString()
|
|
|
|
}
|
|
|
|
|
|
|
|
func findOrCreateSegmentTimeline(adaptationSet *etree.Element) *etree.Element {
|
|
|
|
for _, representation := range adaptationSet.SelectElements("Representation") {
|
|
|
|
for _, segmentTemplate := range representation.SelectElements("SegmentTemplate") {
|
|
|
|
timeline := segmentTemplate.SelectElement("SegmentTimeline")
|
|
|
|
if timeline != nil {
|
|
|
|
return timeline
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no SegmentTimeline found, create one
|
|
|
|
representation := adaptationSet.CreateElement("Representation")
|
|
|
|
segmentTemplate := representation.CreateElement("SegmentTemplate")
|
|
|
|
return segmentTemplate.CreateElement("SegmentTimeline")
|
|
|
|
}
|