init: working multi download
This commit is contained in:
commit
8d983de8ef
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
yt-mdl
|
140
downloader.go
Normal file
140
downloader.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jedib0t/go-pretty/v6/progress"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
const YT_DLP string = "yt-dlp"
|
||||||
|
const YT_DLP_FLAG_GET_TITLE string = "--get-title"
|
||||||
|
|
||||||
|
const JOB_STATUS_NEW = 1
|
||||||
|
const JOB_STATUS_COMPLETED = 2
|
||||||
|
const JOB_STATUS_ERR = 3
|
||||||
|
|
||||||
|
type DownloadCtx struct {
|
||||||
|
link string
|
||||||
|
title string
|
||||||
|
progress uint8
|
||||||
|
status uint8
|
||||||
|
tracker *progress.Tracker
|
||||||
|
err *error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func Download(links *[]string) {
|
||||||
|
// Start and manage goroutines here
|
||||||
|
// Now get titles only
|
||||||
|
downloadCtxsChan := make(chan *DownloadCtx)
|
||||||
|
getTitleErrChan := make(chan *DownloadCtx)
|
||||||
|
startedGoRoutines := 0
|
||||||
|
pw := progress.NewWriter()
|
||||||
|
pw.SetTrackerLength(10)
|
||||||
|
pw.SetNumTrackersExpected(len(*links))
|
||||||
|
pw.SetStyle(progress.StyleDefault)
|
||||||
|
pw.SetUpdateFrequency(time.Millisecond * 100)
|
||||||
|
pw.Style().Visibility.ETA = false
|
||||||
|
pw.Style().Colors = progress.StyleColorsExample
|
||||||
|
pw.Style().Visibility.Speed = false
|
||||||
|
pw.Style().Visibility.Percentage = false
|
||||||
|
pw.Style().Visibility.Value = false
|
||||||
|
pw.Style().Visibility.TrackerOverall = true
|
||||||
|
|
||||||
|
go pw.Render()
|
||||||
|
|
||||||
|
for _, val := range *links {
|
||||||
|
ctx := createDownloadCtx(val)
|
||||||
|
|
||||||
|
pw.AppendTracker(ctx.tracker)
|
||||||
|
|
||||||
|
go startJob(&ctx, downloadCtxsChan, getTitleErrChan)
|
||||||
|
startedGoRoutines++
|
||||||
|
}
|
||||||
|
|
||||||
|
for startedGoRoutines > 0 {
|
||||||
|
select {
|
||||||
|
case ctx := <- downloadCtxsChan:
|
||||||
|
ctx.tracker.UpdateMessage(ctx.title)
|
||||||
|
startedGoRoutines--
|
||||||
|
|
||||||
|
case ctx := <- getTitleErrChan:
|
||||||
|
ctx.tracker.MarkAsErrored()
|
||||||
|
startedGoRoutines--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pw.Stop()
|
||||||
|
|
||||||
|
for pw.IsRenderInProgress() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDownloadCtx(link string) DownloadCtx {
|
||||||
|
var ctx DownloadCtx
|
||||||
|
ctx.tracker = &progress.Tracker{}
|
||||||
|
ctx.tracker.SetValue(0)
|
||||||
|
ctx.tracker.Message = link
|
||||||
|
ctx.tracker.Units = progress.UnitsDefault
|
||||||
|
ctx.link = link
|
||||||
|
ctx.status = JOB_STATUS_NEW
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func startJob(
|
||||||
|
ctx *DownloadCtx, downloadCtxs chan *DownloadCtx, errs chan *DownloadCtx) {
|
||||||
|
|
||||||
|
getTitle(ctx)
|
||||||
|
|
||||||
|
if ctx.err != nil {
|
||||||
|
errs <- ctx
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.tracker.UpdateMessage(ctx.title)
|
||||||
|
|
||||||
|
ytdlpDownload(ctx, downloadCtxs, errs)
|
||||||
|
|
||||||
|
if ctx.err != nil {
|
||||||
|
errs <- ctx
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadCtxs <- ctx
|
||||||
|
}
|
||||||
|
func getTitle(
|
||||||
|
ctx *DownloadCtx) {
|
||||||
|
|
||||||
|
cmd := exec.Command(YT_DLP, YT_DLP_FLAG_GET_TITLE, ctx.link)
|
||||||
|
stdout, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
ctx.err = &err
|
||||||
|
ctx.status = JOB_STATUS_ERR
|
||||||
|
return
|
||||||
|
}
|
||||||
|
title := string(stdout)
|
||||||
|
title = strings.TrimSpace(title)
|
||||||
|
ctx.title = title
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ytdlpDownload(
|
||||||
|
ctx *DownloadCtx, downloadCtxs chan *DownloadCtx, errs chan *DownloadCtx) {
|
||||||
|
|
||||||
|
cmd := exec.Command(YT_DLP, ctx.link)
|
||||||
|
stdout, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
ctx.err = &err
|
||||||
|
ctx.status = JOB_STATUS_ERR
|
||||||
|
errs <- ctx
|
||||||
|
return
|
||||||
|
}
|
||||||
|
title := string(stdout)
|
||||||
|
title = strings.TrimSpace(title)
|
||||||
|
ctx.title = title
|
||||||
|
|
||||||
|
downloadCtxs <- ctx
|
||||||
|
|
||||||
|
}
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module github.com/HeshamTB/yt-mdl
|
||||||
|
|
||||||
|
go 1.21.0
|
||||||
|
|
||||||
|
require github.com/jedib0t/go-pretty/v6 v6.4.6
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
golang.org/x/sys v0.1.0 // indirect
|
||||||
|
)
|
23
go.sum
Normal file
23
go.sum
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw=
|
||||||
|
github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM=
|
||||||
|
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
52
yt-mdl.go
Normal file
52
yt-mdl.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var verbose bool
|
||||||
|
var videoLinks []string
|
||||||
|
var maxConcurrent int
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&verbose, "verbose", false, "Be verbose")
|
||||||
|
flag.IntVar(&maxConcurrent, "goroutines", 8, "Maximum concurrent downloads")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
parseArgs()
|
||||||
|
|
||||||
|
fmt.Println("Links to be downloaded ", videoLinks)
|
||||||
|
|
||||||
|
Download(&videoLinks)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseArgs() {
|
||||||
|
flag.Parse()
|
||||||
|
for _, arg := range flag.Args(){
|
||||||
|
if !isValidURL(arg) {
|
||||||
|
fmt.Println("Skipping ", arg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
videoLinks = append(videoLinks, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidURL(str string) bool {
|
||||||
|
_, err := url.ParseRequestURI(str)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(str)
|
||||||
|
|
||||||
|
if err != nil || u.Scheme != "https" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user