init: working multi download

This commit is contained in:
HeshamTB 2023-08-20 12:22:32 +03:00
commit 8d983de8ef
5 changed files with 227 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
yt-mdl

140
downloader.go Normal file
View 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
View 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
View 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
View 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
}