mirror of
https://github.com/crunchy-labs/crunchy-cli.git
synced 2026-01-21 04:02:00 -06:00
Updated download ffmpeg behavior
This commit is contained in:
parent
22525973c7
commit
e7b5a7bf30
2 changed files with 36 additions and 9 deletions
|
|
@ -43,9 +43,15 @@ type Downloader struct {
|
||||||
|
|
||||||
// A method to call when a segment was downloaded
|
// A method to call when a segment was downloaded
|
||||||
OnSegmentDownload func(segment *m3u8.MediaSegment, current, total int, file *os.File) error
|
OnSegmentDownload func(segment *m3u8.MediaSegment, current, total int, file *os.File) error
|
||||||
|
// If LockOnSegmentDownload is true, only one OnSegmentDownload function can be called at
|
||||||
|
// once. Normally (because of the use of goroutines while downloading) multiple could get
|
||||||
|
// called simultaneously
|
||||||
|
LockOnSegmentDownload bool
|
||||||
|
|
||||||
// If FFmpeg is true, ffmpeg will used to merge and convert files
|
// If FFmpegOpts is not nil, ffmpeg will be used to merge and convert files.
|
||||||
FFmpeg bool
|
// The given opts will be used as ffmpeg parameters while merging.
|
||||||
|
// Some opts are already used, see mergeSegmentsFFmpeg in format.go for more details
|
||||||
|
FFmpegOpts []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDownloader creates a downloader with default settings which should
|
// NewDownloader creates a downloader with default settings which should
|
||||||
|
|
@ -72,7 +78,7 @@ func NewDownloader(context context.Context, filename string, goroutines int, onS
|
||||||
// The actual crunchyroll video is split up in multiple segments (or video files) which have to be downloaded and merged after to generate a single video file.
|
// The actual crunchyroll video is split up in multiple segments (or video files) which have to be downloaded and merged after to generate a single video file.
|
||||||
// And this function just downloads each of this segment into the given directory.
|
// And this function just downloads each of this segment into the given directory.
|
||||||
// See https://en.wikipedia.org/wiki/MPEG_transport_stream for more information
|
// See https://en.wikipedia.org/wiki/MPEG_transport_stream for more information
|
||||||
func download(context context.Context, format *Format, tempDir string, goroutines int, onSegmentDownload func(segment *m3u8.MediaSegment, current, total int, file *os.File) error) error {
|
func download(context context.Context, format *Format, tempDir string, goroutines int, lockOnSegmentDownload bool, onSegmentDownload func(segment *m3u8.MediaSegment, current, total int, file *os.File) error) error {
|
||||||
req, err := http.NewRequest(http.MethodGet, format.Video.URI, nil)
|
req, err := http.NewRequest(http.MethodGet, format.Video.URI, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -99,6 +105,7 @@ func download(context context.Context, format *Format, tempDir string, goroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
var lock sync.Mutex
|
||||||
chunkSize := int(math.Ceil(float64(len(segments)) / float64(goroutines)))
|
chunkSize := int(math.Ceil(float64(len(segments)) / float64(goroutines)))
|
||||||
|
|
||||||
// when a onSegmentDownload call returns an error, this channel will be set to true and stop all goroutines
|
// when a onSegmentDownload call returns an error, this channel will be set to true and stop all goroutines
|
||||||
|
|
@ -145,11 +152,21 @@ func download(context context.Context, format *Format, tempDir string, goroutine
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if onSegmentDownload != nil {
|
if onSegmentDownload != nil {
|
||||||
|
if lockOnSegmentDownload {
|
||||||
|
lock.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
if err = onSegmentDownload(segment, int(atomic.AddInt32(&total, 1)), len(segments), file); err != nil {
|
if err = onSegmentDownload(segment, int(atomic.AddInt32(&total, 1)), len(segments), file); err != nil {
|
||||||
quit <- true
|
quit <- true
|
||||||
|
if lockOnSegmentDownload {
|
||||||
|
lock.Unlock()
|
||||||
|
}
|
||||||
file.Close()
|
file.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if lockOnSegmentDownload {
|
||||||
|
lock.Unlock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
file.Close()
|
file.Close()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
format.go
22
format.go
|
|
@ -56,12 +56,12 @@ func (f *Format) Download(downloader Downloader) error {
|
||||||
if downloader.DeleteTempAfter {
|
if downloader.DeleteTempAfter {
|
||||||
defer os.RemoveAll(downloader.TempDir)
|
defer os.RemoveAll(downloader.TempDir)
|
||||||
}
|
}
|
||||||
if err := download(downloader.Context, f, downloader.TempDir, downloader.Goroutines, downloader.OnSegmentDownload); err != nil {
|
if err := download(downloader.Context, f, downloader.TempDir, downloader.Goroutines, downloader.LockOnSegmentDownload, downloader.OnSegmentDownload); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if downloader.FFmpeg {
|
if downloader.FFmpegOpts != nil {
|
||||||
return mergeSegmentsFFmpeg(downloader.Context, downloader.TempDir, downloader.Filename)
|
return mergeSegmentsFFmpeg(downloader.Context, downloader.TempDir, downloader.Filename, downloader.FFmpegOpts)
|
||||||
} else {
|
} else {
|
||||||
return mergeSegments(downloader.Context, downloader.TempDir, downloader.Filename)
|
return mergeSegments(downloader.Context, downloader.TempDir, downloader.Filename)
|
||||||
}
|
}
|
||||||
|
|
@ -116,7 +116,7 @@ func mergeSegments(context context.Context, tempDir string, outputFile string) e
|
||||||
// mergeSegmentsFFmpeg reads every file in tempDir and merges their content to the outputFile
|
// mergeSegmentsFFmpeg reads every file in tempDir and merges their content to the outputFile
|
||||||
// with ffmpeg (https://ffmpeg.org/).
|
// with ffmpeg (https://ffmpeg.org/).
|
||||||
// The given output file gets created or overwritten if already existing
|
// The given output file gets created or overwritten if already existing
|
||||||
func mergeSegmentsFFmpeg(context context.Context, tempDir string, outputFile string) error {
|
func mergeSegmentsFFmpeg(context context.Context, tempDir string, outputFile string, opts []string) error {
|
||||||
dir, err := os.ReadDir(tempDir)
|
dir, err := os.ReadDir(tempDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -129,12 +129,21 @@ func mergeSegmentsFFmpeg(context context.Context, tempDir string, outputFile str
|
||||||
for i := 0; i < len(dir); i++ {
|
for i := 0; i < len(dir); i++ {
|
||||||
fmt.Fprintf(f, "file '%s.ts'\n", filepath.Join(tempDir, strconv.Itoa(i)))
|
fmt.Fprintf(f, "file '%s.ts'\n", filepath.Join(tempDir, strconv.Itoa(i)))
|
||||||
}
|
}
|
||||||
cmd := exec.Command("ffmpeg",
|
|
||||||
|
// predefined options ... custom options ... predefined output filename
|
||||||
|
command := []string{
|
||||||
"-f", "concat",
|
"-f", "concat",
|
||||||
"-safe", "0",
|
"-safe", "0",
|
||||||
"-i", f.Name(),
|
"-i", f.Name(),
|
||||||
"-c", "copy",
|
"-c", "copy",
|
||||||
outputFile)
|
}
|
||||||
|
if opts != nil {
|
||||||
|
command = append(command, opts...)
|
||||||
|
}
|
||||||
|
command = append(command, outputFile)
|
||||||
|
|
||||||
|
cmd := exec.Command("ffmpeg",
|
||||||
|
command...)
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -148,6 +157,7 @@ func mergeSegmentsFFmpeg(context context.Context, tempDir string, outputFile str
|
||||||
case err := <-cmdChan:
|
case err := <-cmdChan:
|
||||||
return err
|
return err
|
||||||
case <-context.Done():
|
case <-context.Done():
|
||||||
|
cmd.Process.Kill()
|
||||||
return context.Err()
|
return context.Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue