-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathservice.go
More file actions
139 lines (119 loc) · 3.36 KB
/
service.go
File metadata and controls
139 lines (119 loc) · 3.36 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
package sprite
import (
"fmt"
"github.com/Paxx-RnD/go-ffmpeg/ffprobe"
"github.com/Paxx-RnD/go-helper/helpers/math_helper/mathint"
"github.com/Paxx-RnD/go-helper/helpers/random_helper"
"github.com/Paxx-RnD/go-helper/helpers/time_helper"
"math"
"os"
"os/exec"
"path"
"sprite-preview/types"
"strconv"
"strings"
"time"
)
type IService interface {
GenerateFrames() []string
Montage(frames []string)
GenerateVtt(frames []string)
}
type service struct {
flags types.Flags
ffprobe ffprobe.IFfprobe
}
func NewService(flags types.Flags, ffprobe ffprobe.IFfprobe) IService {
return &service{flags: flags, ffprobe: ffprobe}
}
func (s *service) GenerateFrames() []string {
probe, err := s.ffprobe.GetProbe(s.flags.Input)
if err != nil {
panic(fmt.Sprintf("could not probe: %v", err))
}
duration, err := strconv.ParseFloat(probe.GetFirstVideoStream().Duration, 64)
if err != nil {
panic(fmt.Sprintf("could not get video duration: %v", err))
}
frames := make([]string, 0, 0)
for i := 0; i < int(duration); i += s.flags.Frequency {
frame := s.extract(i)
frames = append(frames, frame)
}
return frames
}
func (s *service) extract(seek int) string {
seekString := strconv.Itoa(seek)
now := time.Now()
output := random_helper.Generate(5, random_helper.AZAndCaps) + fmt.Sprintf("%d", now.UnixMilli()) + s.flags.Extension
outputPath := path.Join(path.Dir(s.flags.Vtt), output)
cmd := exec.Command(
"ffmpeg",
"-ss",
seekString,
"-i",
s.flags.Input,
"-vf",
fmt.Sprintf("scale=%dx%d", s.flags.Width, s.flags.Height),
"-frames",
"1",
outputPath,
)
_, err := cmd.CombinedOutput()
fmt.Println(strings.Join(cmd.Args, " "))
if err != nil {
panic(fmt.Sprintf("failed to extract frame: %v", err))
}
return outputPath
}
func (s *service) Montage(frames []string) {
cmd := exec.Command("montage", "-mode", "concatenate", "-tile", fmt.Sprintf("%dx%d", s.flags.Columns, s.flags.Rows))
for _, image := range frames {
cmd.Args = append(cmd.Args, image)
}
spritePath := path.Join(path.Dir(frames[0]), s.flags.Prefix+s.flags.Extension)
cmd.Args = append(cmd.Args, spritePath)
_, err := cmd.CombinedOutput()
s.clean(frames)
if err != nil {
panic(fmt.Sprintf("failed to created montage: %v", err))
}
}
func (s *service) GenerateVtt(frames []string) {
file, err := os.Create(s.flags.Vtt)
if err != nil {
panic("failed to create vtt file")
}
_, err = file.WriteString("WEBVTT\n\n")
if err != nil {
panic("failed to write string in vtt")
}
t1 := time.Second * 0
grid := s.flags.Columns * s.flags.Rows
max := float64(len(frames)) / float64(grid)
nSprites := mathint.Max(1, int(math.Ceil(max)))
for n := 0; n < nSprites; n++ {
output := fmt.Sprintf("%s-%d%s", s.flags.Prefix, n, s.flags.Extension)
for y := 0; y < s.flags.Columns; y++ {
for x := 0; x < s.flags.Rows; x++ {
t2 := time.Duration(s.flags.Frequency) * time.Second
start := time_helper.FormatHHMMSSmm(t1)
end := time_helper.FormatHHMMSSmm(t1 + t2)
line := fmt.Sprintf("%s --> %s %s#xywh=%d,%d,%d,%d\n\n", start, end, output, x*s.flags.Width, y*s.flags.Height, s.flags.Width, s.flags.Height)
_, err = file.WriteString(line)
if err != nil {
panic("failed to sprite string in vtt")
}
t1 += t2
}
}
}
}
func (s *service) clean(frames []string) {
for _, frame := range frames {
err := os.Remove(frame)
if err != nil {
fmt.Println(err.Error())
}
}
}