tangled
alpha
login
or
join now
arimelody.space
/
vodular
0
fork
atom
Helper tool for stitching together livestream VOD segments and uploading them to YouTube!
0
fork
atom
overview
issues
pulls
pipelines
check if fullvod exists; store config in UserConfigDir
arimelody.space
1 month ago
aa28ffed
c3dc6f09
+58
-13
3 changed files
expand all
collapse all
unified
split
config
config.go
main.go
scanner
scanner.go
+1
-1
config/config.go
···
33
33
},
34
34
}
35
35
36
36
-
const CONFIG_FILENAME = "config.toml"
36
36
+
var CONFIG_FILENAME string = "config.toml"
37
37
38
38
func ReadConfig(filename string) (*Config, error) {
39
39
cfgBytes, err := os.ReadFile(filename)
+35
-12
main.go
···
28
28
const segmentExtension = "mkv"
29
29
30
30
func showHelp() {
31
31
-
execSplits := strings.Split(os.Args[0], "/")
32
32
-
execName := execSplits[len(execSplits) - 1]
33
33
-
fmt.Printf(helpText, execName)
31
31
+
fmt.Println(helpText)
32
32
+
os.Exit(0)
34
33
}
35
34
36
35
func main() {
37
36
// config
37
37
+
userConfigDir, err := os.UserConfigDir()
38
38
+
if err != nil {
39
39
+
log.Fatalf("Could not determine user configuration directory: %v", err)
40
40
+
os.Exit(1)
41
41
+
}
42
42
+
config.CONFIG_FILENAME = path.Join(userConfigDir, "vodular", "config.toml")
38
43
cfg, err := config.ReadConfig(config.CONFIG_FILENAME)
39
44
if err != nil {
40
45
log.Fatalf("Failed to read config: %v", err)
41
46
os.Exit(1)
42
47
}
43
48
if cfg == nil {
49
49
+
err = os.MkdirAll(path.Dir(config.CONFIG_FILENAME), 0750)
50
50
+
if err != nil {
51
51
+
log.Fatalf("Failed to create config directory: %v", err)
52
52
+
os.Exit(1)
53
53
+
}
44
54
err = config.GenerateConfig(config.CONFIG_FILENAME)
45
55
if err != nil {
46
56
log.Fatalf("Failed to generate config: %v", err)
···
57
67
// arguments
58
68
if len(os.Args) < 2 || os.Args[1] == "--help" || os.Args[1] == "-h" {
59
69
showHelp()
60
60
-
os.Exit(0)
61
70
}
62
71
63
72
var verbose bool = false
···
76
85
fallthrough
77
86
case "--help":
78
87
showHelp()
79
79
-
os.Exit(0)
80
88
81
89
case "-v":
82
90
fallthrough
···
228
236
os.Exit(1)
229
237
}
230
238
fmt.Printf(
231
231
-
"\n================================\n" +
232
232
-
"TITLE:\n%s\n\n" +
233
233
-
"DESCRIPTION:\n%s\n" +
239
239
+
"\n================================\n\n" +
240
240
+
"< TITLE >\n%s\n\n" +
241
241
+
"< DESCRIPTION >\n%s\n" +
234
242
"\n================================\n",
235
243
title, description,
236
244
)
237
245
}
238
246
239
247
// concatenate VOD segments into full VOD
240
240
-
video.SizeBytes, err = vid.ConcatVideo(video, vodFiles, verbose)
241
241
-
if err != nil {
242
242
-
log.Fatalf("Failed to concatenate VOD segments: %v", err)
243
243
-
os.Exit(1)
248
248
+
fullVodExists := func () bool {
249
249
+
// check if full VOD already exists with expected duration
250
250
+
if fullVodProbe, err := scanner.ProbeSegment(video.Filename); err != nil {
251
251
+
var totalLength float64 = 0
252
252
+
for _, filename := range vodFiles {
253
253
+
probe, err := scanner.ProbeSegment(filename)
254
254
+
if err != nil { continue }
255
255
+
totalLength += probe.Format.Duration
256
256
+
}
257
257
+
return fullVodProbe.Format.Duration == totalLength
258
258
+
}
259
259
+
return false
260
260
+
}()
261
261
+
if !fullVodExists {
262
262
+
video.SizeBytes, err = vid.ConcatVideo(video, vodFiles, verbose)
263
263
+
if err != nil {
264
264
+
log.Fatalf("Failed to concatenate VOD segments: %v", err)
265
265
+
os.Exit(1)
266
266
+
}
244
267
}
245
268
246
269
// youtube oauth flow
+22
scanner/scanner.go
···
1
1
package scanner
2
2
3
3
import (
4
4
+
"encoding/json"
4
5
"os"
5
6
"path"
6
7
"strings"
7
8
"time"
8
9
9
10
"github.com/pelletier/go-toml/v2"
11
11
+
ffmpeg_go "github.com/u2takey/ffmpeg-go"
10
12
)
11
13
12
14
type (
···
24
26
FootageDir string `toml:"footage_dir"`
25
27
Uploaded bool `toml:"uploaded"`
26
28
Category *Category `toml:"category" comment:"(Optional) Category details, for additional credits."`
29
29
+
}
30
30
+
31
31
+
ffprobeFormat struct {
32
32
+
Duration float64 `json:"duration"`
33
33
+
Size int64 `json:"size"`
34
34
+
}
35
35
+
36
36
+
ffprobeOutput struct {
37
37
+
Format ffprobeFormat `json:"format"`
27
38
}
28
39
)
29
40
···
45
56
}
46
57
47
58
return files, nil
59
59
+
}
60
60
+
61
61
+
func ProbeSegment(filename string) (*ffprobeOutput, error) {
62
62
+
out, err := ffmpeg_go.Probe(filename)
63
63
+
if err != nil { return nil, err }
64
64
+
65
65
+
probe := ffprobeOutput{}
66
66
+
err = json.Unmarshal([]byte(out), probe)
67
67
+
if err != nil { return nil, err }
68
68
+
69
69
+
return &probe, nil
48
70
}
49
71
50
72
func ReadMetadata(directory string) (*Metadata, error) {