-
Notifications
You must be signed in to change notification settings - Fork 60
Expand file tree
/
Copy pathmkvod.go
More file actions
140 lines (121 loc) · 3.78 KB
/
Copy pathmkvod.go
File metadata and controls
140 lines (121 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package actions
import (
"bufio"
"context"
"fmt"
"io"
"log/slog"
"net/url"
"os"
"path"
"strings"
log "github.com/sirupsen/logrus"
"github.com/tum-dev/gocast/runner/config"
"github.com/tum-dev/gocast/runner/pkg/metrics"
"github.com/tum-dev/gocast/runner/pkg/ptr"
"github.com/tum-dev/gocast/runner/protobuf"
)
// MkVOD takes a stream that was streamed and moves the hls stream to long term storage.
// Additionally, the playlist type is transformed from event to VOD.
func MkVOD(_ context.Context, _ *slog.Logger, notify chan *protobuf.Notification, d map[string]any, metrics *metrics.Broker) error {
streamID, ok := d["streamID"].(uint64)
if !ok {
return AbortingError(fmt.Errorf("no stream id in context"))
}
streamVersion, ok := d["streamVersion"].(string)
if !ok {
return AbortingError(fmt.Errorf("no stream end in context"))
}
recordingDir, ok := d["recordingDir"].(string)
if !ok {
return AbortingError(fmt.Errorf("no recordingDir in context"))
}
vodDir := path.Join(config.Config.StoragePath, fmt.Sprintf("%d", streamID), streamVersion)
d["vodDir"] = vodDir
err := os.MkdirAll(vodDir, os.ModePerm)
if err != nil && !os.IsExist(err) {
return AbortingError(fmt.Errorf("create VOD directory: %w", err))
}
recordingContent, err := os.ReadDir(recordingDir)
if err != nil {
return AbortingError(fmt.Errorf("read recordingDir: %w", err))
}
for _, entry := range recordingContent {
if entry.IsDir() {
log.Warn("found dir in recordingDir, skipping", "name", entry.Name())
continue
}
if entry.Name() == "playlist.m3u8" {
srcPlst, err := os.Open(path.Join(recordingDir, entry.Name()))
if err != nil {
return AbortingError(fmt.Errorf("open recording playlist: %w", err))
}
dstPlstS := vodFromEventPlst(srcPlst)
dstPlst, err := os.Create(path.Join(vodDir, entry.Name()))
if err != nil {
return AbortingError(fmt.Errorf("create vod playlist: %w", err))
}
_, err = io.WriteString(dstPlst, dstPlstS)
if err != nil {
return fmt.Errorf("write vod to playlist: %w", err)
}
_ = dstPlst.Close()
continue
}
err = copyFile(path.Join(recordingDir, entry.Name()), path.Join(vodDir, entry.Name()))
}
vodUrl, err := url.JoinPath(config.Config.EdgeServer, fmt.Sprintf("%d", streamID), streamVersion, "playlist.m3u8")
if err != nil {
return fmt.Errorf("join vod url: %w", err)
}
notify <- &protobuf.Notification{
Data: &protobuf.Notification_VodReady{
VodReady: &protobuf.VODReadyNotification{
Stream: &protobuf.StreamInfo{Id: ptr.Take(streamID)},
StreamVersion: ptr.Take(protobuf.StreamVersion(protobuf.StreamVersion_value[streamVersion])),
Url: ptr.Take(vodUrl),
},
},
}
return nil
}
func copyFile(sourcePath, destPath string) error {
inputFile, err := os.Open(sourcePath)
if err != nil {
return fmt.Errorf("open source file: %w", err)
}
defer inputFile.Close()
outputFile, err := os.Create(destPath)
if err != nil {
return fmt.Errorf("open dest file: %w", err)
}
defer outputFile.Close()
_, err = io.Copy(outputFile, inputFile)
if err != nil {
return fmt.Errorf("copy to dest from source: %w", err)
}
return nil
}
// vodFromEventPlst modifies an HLS playlist from EVENT to VOD
func vodFromEventPlst(playlist io.Reader) string {
var lines []string
hasEndlist := false
scanner := bufio.NewScanner(playlist)
for scanner.Scan() {
line := scanner.Text()
// Replace #EXT-X-PLAYLIST-TYPE:EVENT with #EXT-X-PLAYLIST-TYPE:VOD
if strings.Contains(line, "#EXT-X-PLAYLIST-TYPE:EVENT") {
line = "#EXT-X-PLAYLIST-TYPE:VOD"
}
// Check if #EXT-X-ENDLIST is already present
if line == "#EXT-X-ENDLIST" {
hasEndlist = true
}
lines = append(lines, line)
}
// Append #EXT-X-ENDLIST if missing
if !hasEndlist {
lines = append(lines, "#EXT-X-ENDLIST")
}
return strings.Join(lines, "\n")
}