Skip to content

Commit 255a3e8

Browse files
authored
Merge pull request #75 from J0nDoe/feat/refactors
Updates and refactors
2 parents 9fb2916 + 3cc73a6 commit 255a3e8

File tree

540 files changed

+998
-57956
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

540 files changed

+998
-57956
lines changed

chaturbate/channel.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type Channel struct {
3838
IsPaused bool
3939
isStopped bool
4040
Logs []string
41-
logType logType
41+
LogType LogType
4242

4343
bufferLock sync.Mutex
4444
buffer map[int][]byte
@@ -61,32 +61,32 @@ type Channel struct {
6161
// Run
6262
func (w *Channel) Run() {
6363
if w.Username == "" {
64-
w.log(logTypeError, "username is empty, use `-u USERNAME` to specify")
64+
w.log(LogTypeError, "username is empty, use `-u USERNAME` to specify")
6565
return
6666
}
6767

6868
for {
6969
if w.IsPaused {
70-
w.log(logTypeInfo, "channel is paused")
70+
w.log(LogTypeInfo, "channel is paused")
7171
<-w.ResumeChannel // blocking
72-
w.log(logTypeInfo, "channel is resumed")
72+
w.log(LogTypeInfo, "channel is resumed")
7373
}
7474
if w.isStopped {
75-
w.log(logTypeInfo, "channel is stopped")
75+
w.log(LogTypeInfo, "channel is stopped")
7676
break
7777
}
7878

7979
body, err := w.requestChannelBody()
8080
if err != nil {
81-
w.log(logTypeError, "body request error: %w", err)
81+
w.log(LogTypeError, "body request error: %v", err)
8282
}
8383
if strings.Contains(body, "playlist.m3u8") {
8484
w.IsOnline = true
8585
w.LastStreamedAt = time.Now().Format("2006-01-02 15:04:05")
86-
w.log(logTypeInfo, "channel is online, start fetching...")
86+
w.log(LogTypeInfo, "channel is online, start fetching...")
8787

8888
if err := w.record(body); err != nil { // blocking
89-
w.log(logTypeError, "record error: %w", err)
89+
w.log(LogTypeError, "record error: %v", err)
9090
}
9191
continue // this excutes when recording is over/interrupted
9292
}
@@ -95,11 +95,11 @@ func (w *Channel) Run() {
9595
// close file when offline so user can move/delete it
9696
if w.file != nil {
9797
if err := w.releaseFile(); err != nil {
98-
w.log(logTypeError, "release file: %w", err)
98+
w.log(LogTypeError, "release file: %v", err)
9999
}
100100
}
101101

102-
w.log(logTypeInfo, "channel is offline, check again %d min(s) later", w.Interval)
102+
w.log(LogTypeInfo, "channel is offline, check again %d min(s) later", w.Interval)
103103
<-time.After(time.Duration(w.Interval) * time.Minute) // minutes cooldown to check online status
104104
}
105105
}

chaturbate/channel_file.go

+36-24
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ import (
99
"time"
1010
)
1111

12-
// filename
12+
// filename generates the filename based on the session pattern and current split index.
1313
func (w *Channel) filename() (string, error) {
14-
data := w.sessionPattern
15-
if data == nil {
16-
data = map[string]any{
14+
if w.sessionPattern == nil {
15+
w.sessionPattern = map[string]any{
1716
"Username": w.Username,
1817
"Year": time.Now().Format("2006"),
1918
"Month": time.Now().Format("01"),
@@ -23,69 +22,82 @@ func (w *Channel) filename() (string, error) {
2322
"Second": time.Now().Format("05"),
2423
"Sequence": 0,
2524
}
26-
w.sessionPattern = data
27-
} else {
28-
data["Sequence"] = w.splitIndex
2925
}
30-
t, err := template.New("filename").Parse(w.filenamePattern)
26+
27+
w.sessionPattern["Sequence"] = w.splitIndex
28+
29+
var buf bytes.Buffer
30+
tmpl, err := template.New("filename").Parse(w.filenamePattern)
3131
if err != nil {
32-
return "", err
32+
return "", fmt.Errorf("filename pattern error: %w", err)
3333
}
34-
var buf bytes.Buffer
35-
if err := t.Execute(&buf, data); err != nil {
36-
return "", err
34+
if err := tmpl.Execute(&buf, w.sessionPattern); err != nil {
35+
return "", fmt.Errorf("template execution error: %w", err)
3736
}
37+
3838
return buf.String(), nil
3939
}
4040

41-
// newFile
41+
// newFile creates a new file and prepares it for writing stream data.
4242
func (w *Channel) newFile() error {
4343
filename, err := w.filename()
4444
if err != nil {
45-
return fmt.Errorf("filename pattern error: %w", err)
45+
return err
4646
}
47+
4748
if err := os.MkdirAll(filepath.Dir(filename), 0777); err != nil {
4849
return fmt.Errorf("create folder: %w", err)
4950
}
51+
5052
file, err := os.OpenFile(filename+".ts", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0777)
5153
if err != nil {
5254
return fmt.Errorf("cannot open file: %s: %w", filename, err)
5355
}
54-
w.log(logTypeInfo, "the stream will be saved as %s.ts", filename)
56+
57+
w.log(LogTypeInfo, "the stream will be saved as %s.ts", filename)
5558
w.file = file
5659
return nil
5760
}
5861

59-
// releaseFile
62+
// releaseFile closes the current file and removes it if empty.
6063
func (w *Channel) releaseFile() error {
6164
if w.file == nil {
6265
return nil
6366
}
64-
// close the file to remove it
67+
6568
if err := w.file.Close(); err != nil {
6669
return fmt.Errorf("close file: %s: %w", w.file.Name(), err)
6770
}
68-
// remove it if it was empty
69-
if w.SegmentFilesize == 0 {
70-
w.log(logTypeInfo, "%s was removed because it was empty", w.file.Name())
7171

72+
if w.SegmentFilesize == 0 {
73+
w.log(LogTypeInfo, "%s was removed because it was empty", w.file.Name())
7274
if err := os.Remove(w.file.Name()); err != nil {
7375
return fmt.Errorf("remove zero file: %s: %w", w.file.Name(), err)
7476
}
7577
}
78+
7679
w.file = nil
7780
return nil
7881
}
7982

80-
// nextFile
81-
func (w *Channel) nextFile() error {
83+
// nextFile handles the transition to a new file segment, ensuring correct timing.
84+
func (w *Channel) nextFile(startTime time.Time) error {
85+
// Release the current file before creating a new one.
8286
if err := w.releaseFile(); err != nil {
83-
w.log(logTypeError, "release file: %w", err)
87+
w.log(LogTypeError, "release file: %v", err)
88+
return err
8489
}
8590

91+
// Increment the split index for the next file.
8692
w.splitIndex++
93+
94+
// Reset segment data.
8795
w.SegmentFilesize = 0
88-
w.SegmentDuration = 0
8996

97+
// Calculate the actual segment duration using the elapsed time.
98+
elapsed := int(time.Since(startTime).Minutes())
99+
w.SegmentDuration = elapsed
100+
101+
// Create the new file.
90102
return w.newFile()
91103
}

chaturbate/channel_internal.go

+44-24
Original file line numberDiff line numberDiff line change
@@ -175,17 +175,17 @@ func (w *Channel) resolveSource(body string) (string, string, error) {
175175
if variant == nil {
176176
return "", "", fmt.Errorf("no available resolution")
177177
}
178-
w.log(logTypeInfo, "resolution %dp is used", variant.width)
178+
w.log(LogTypeInfo, "resolution %dp is used", variant.width)
179179

180180
url, ok := variant.framerate[w.Framerate]
181181
// If the framerate is not found, fallback to the first found framerate, this block pretends there're only 30 and 60 fps.
182182
// no complex logic here, im lazy.
183183
if ok {
184-
w.log(logTypeInfo, "framerate %dfps is used", w.Framerate)
184+
w.log(LogTypeInfo, "framerate %dfps is used", w.Framerate)
185185
} else {
186186
for k, v := range variant.framerate {
187187
url = v
188-
w.log(logTypeWarning, "framerate %dfps not found, fallback to %dfps", w.Framerate, k)
188+
w.log(LogTypeWarning, "framerate %dfps not found, fallback to %dfps", w.Framerate, k)
189189
w.Framerate = k
190190
break
191191
}
@@ -196,66 +196,86 @@ func (w *Channel) resolveSource(body string) (string, string, error) {
196196
return rootURL, sourceURL, nil
197197
}
198198

199-
// mergeSegments is a async function that runs in background for the channel,
200-
// and it merges the segments from buffer to the file.
199+
// mergeSegments runs in the background and merges segments from the buffer to the file.
201200
func (w *Channel) mergeSegments() {
202201
var segmentRetries int
202+
startTime := time.Now() // Track the start time of the current segment.
203203

204204
for {
205205
if w.IsPaused || w.isStopped {
206206
break
207207
}
208+
209+
// Handle segment retries if not found.
208210
if segmentRetries > 5 {
209-
w.log(logTypeWarning, "segment #%d not found in buffer, skipped", w.bufferIndex)
211+
w.log(LogTypeWarning, "segment #%d not found in buffer, skipped", w.bufferIndex)
210212
w.bufferIndex++
211213
segmentRetries = 0
212214
continue
213215
}
216+
217+
// If buffer is empty, wait and retry.
214218
if len(w.buffer) == 0 {
215-
<-time.After(1 * time.Second)
219+
time.Sleep(1 * time.Second)
216220
continue
217221
}
222+
223+
// Retrieve segment from buffer.
218224
w.bufferLock.Lock()
219225
buf, ok := w.buffer[w.bufferIndex]
220226
w.bufferLock.Unlock()
227+
221228
if !ok {
222229
segmentRetries++
223-
<-time.After(time.Duration(segmentRetries) * time.Second)
230+
time.Sleep(time.Duration(segmentRetries) * time.Second)
224231
continue
225232
}
233+
234+
// Write the segment to the file.
226235
lens, err := w.file.Write(buf)
227236
if err != nil {
228-
w.log(logTypeError, "segment #%d written error: %v", w.bufferIndex, err)
237+
w.log(LogTypeError, "segment #%d written error: %v", w.bufferIndex, err)
229238
w.retries++
230239
continue
231240
}
232-
w.log(logTypeInfo, "segment #%d written", w.bufferIndex)
233-
w.log(logTypeDebug, "duration: %s, size: %s", DurationStr(w.SegmentDuration), ByteStr(w.SegmentFilesize))
234241

242+
// Update segment size and log progress.
235243
w.SegmentFilesize += lens
236-
segmentRetries = 0
244+
w.log(LogTypeInfo, "segment #%d written", w.bufferIndex)
245+
w.log(LogTypeDebug, "duration: %s, size: %s", DurationStr(w.SegmentDuration), ByteStr(w.SegmentFilesize))
237246

247+
// Check if the file size limit has been reached.
238248
if w.SplitFilesize > 0 && w.SegmentFilesize >= w.SplitFilesize*1024*1024 {
239-
w.log(logTypeInfo, "filesize exceeded, creating new file")
249+
w.log(LogTypeInfo, "filesize exceeded, creating new file")
240250

241-
if err := w.nextFile(); err != nil {
242-
w.log(logTypeError, "next file error: %v", err)
251+
if err := w.nextFile(startTime); err != nil {
252+
w.log(LogTypeError, "next file error: %v", err)
243253
break
244254
}
245-
} else if w.SplitDuration > 0 && w.SegmentDuration >= w.SplitDuration*60 {
246-
w.log(logTypeInfo, "duration exceeded, creating new file")
247255

248-
if err := w.nextFile(); err != nil {
249-
w.log(logTypeError, "next file error: %v", err)
256+
startTime = time.Now() // Reset start time for the new segment.
257+
}
258+
259+
// Check if the duration limit has been reached.
260+
elapsed := int(time.Since(startTime).Minutes())
261+
if w.SplitDuration > 0 && elapsed >= w.SplitDuration {
262+
w.log(LogTypeInfo, "duration exceeded, creating new file")
263+
264+
if err := w.nextFile(startTime); err != nil {
265+
w.log(LogTypeError, "next file error: %v", err)
250266
break
251267
}
268+
269+
startTime = time.Now() // Reset start time for the new segment.
252270
}
253271

272+
// Remove the processed segment from the buffer.
254273
w.bufferLock.Lock()
255274
delete(w.buffer, w.bufferIndex)
256275
w.bufferLock.Unlock()
257276

258-
w.bufferIndex++
277+
w.bufferIndex++ // Move to the next segment.
278+
segmentRetries = 0 // Reset retries for the next segment.
259279
}
260280
}
261281

@@ -276,15 +296,15 @@ func (w *Channel) fetchSegments() {
276296
break
277297
}
278298

279-
w.log(logTypeError, "segment list error, will try again [%d/10]: %v", disconnectRetries, err)
299+
w.log(LogTypeError, "segment list error, will try again [%d/10]: %v", disconnectRetries, err)
280300
disconnectRetries++
281301

282302
<-time.After(time.Duration(wait) * time.Second)
283303
continue
284304
}
285305

286306
if disconnectRetries > 0 {
287-
w.log(logTypeInfo, "channel is back online!")
307+
w.log(LogTypeInfo, "channel is back online!")
288308
w.IsOnline = true
289309
disconnectRetries = 0
290310
}
@@ -296,7 +316,7 @@ func (w *Channel) fetchSegments() {
296316

297317
go func(index int, uri string) {
298318
if err := w.requestSegment(uri, index); err != nil {
299-
w.log(logTypeError, "segment #%d request error, ignored: %v", index, err)
319+
w.log(LogTypeError, "segment #%d request error, ignored: %v", index, err)
300320
return
301321
}
302322
}(w.segmentIndex, v.URI)
@@ -379,7 +399,7 @@ func (w *Channel) requestSegment(url string, index int) error {
379399
return fmt.Errorf("read body: %w", err)
380400
}
381401

382-
w.log(logTypeDebug, "segment #%d fetched", index)
402+
w.log(LogTypeDebug, "segment #%d fetched", index)
383403

384404
w.bufferLock.Lock()
385405
w.buffer[index] = body

chaturbate/channel_util.go

+14-19
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,28 @@ import (
55
"time"
66
)
77

8-
type logType string
9-
10-
const (
11-
logTypeDebug logType = "DEBUG"
12-
logTypeInfo logType = "INFO"
13-
logTypeWarning logType = "WARN"
14-
logTypeError logType = "ERROR"
15-
)
16-
178
// log
18-
func (w *Channel) log(typ logType, message string, v ...interface{}) {
19-
switch w.logType {
20-
case logTypeInfo:
21-
if typ == logTypeDebug {
9+
func (w *Channel) log(typ LogType, message string, v ...interface{}) {
10+
// Check the global log level
11+
currentLogLevel := GetGlobalLogLevel()
12+
13+
switch currentLogLevel {
14+
case LogTypeInfo:
15+
if typ == LogTypeDebug {
2216
return
2317
}
24-
case logTypeWarning:
25-
if typ == logTypeDebug || typ == logTypeInfo {
18+
case LogTypeWarning:
19+
if typ == LogTypeDebug || typ == LogTypeInfo {
2620
return
2721
}
28-
case logTypeError:
29-
if typ == logTypeDebug || typ == logTypeInfo || typ == logTypeWarning {
22+
case LogTypeError:
23+
if typ == LogTypeDebug || typ == LogTypeInfo || typ == LogTypeWarning {
3024
return
3125
}
3226
}
3327

34-
updateLog := fmt.Sprintf("[%s] [%s] %s", time.Now().Format("2006-01-02 15:04:05"), typ, fmt.Errorf(message, v...))
35-
consoleLog := fmt.Sprintf("[%s] [%s] [%s] %s", time.Now().Format("2006-01-02 15:04:05"), typ, w.Username, fmt.Errorf(message, v...))
28+
updateLog := fmt.Sprintf("[%s] [%s] %s", time.Now().Format("2006-01-02 15:04:05"), typ, fmt.Sprintf(message, v...))
29+
consoleLog := fmt.Sprintf("[%s] [%s] [%s] %s", time.Now().Format("2006-01-02 15:04:05"), typ, w.Username, fmt.Sprintf(message, v...))
3630

3731
update := &Update{
3832
Username: w.Username,
@@ -43,6 +37,7 @@ func (w *Channel) log(typ logType, message string, v ...interface{}) {
4337
SegmentDuration: w.SegmentDuration,
4438
SegmentFilesize: w.SegmentFilesize,
4539
}
40+
4641
if w.file != nil {
4742
update.Filename = w.file.Name()
4843
}

0 commit comments

Comments
 (0)