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