diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index dc8f861..0000000 --- a/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -Dockerfile -**.md -.git \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fc81ca9..52cb09a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ * @ch3ck - diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..553ec76 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,14 @@ +--- +name: Bug Report +about: If something isn't working as its supposed to +labels: bug, feature request, needs triage, won't fix +assignees: ch3ck +--- + +Please provide more details: + +* What are you trying to do +* What happened +* What was the expected behaviour + +Please share relevent code sample or [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..3cd82d0 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ +## Proposed changes +Describe the *big picture* reason for your PR. + +## Checklist + +Please review the following checklist before submitting a PR: + +- [ ] **CONSIDER** adding a unit test to demonstrate your PR resolves an issue +- [ ] **DO** keep PRs small for easy review +- [ ] **DO** make sure unit tests pass +- [ ] **DO** ensure no compiler warnings are triggered +- [ ] **AVOID** breaking the CI builds + +## Other comments +Any other comments you might have. diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 8b31d81..0000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Tracking issue -about: Use this template for tracking new features. -title: "[DATE]: [FEATURE NAME]" -labels: bug, feature request, needs triage, won't fix -assignees: ch3ck ---- \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abd4329..b517db2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,22 +1,40 @@ -name: Build +name: CI on: [push, pull_request] jobs: - setup-build-test: - name: Setup, Build, Test + build-and-test-rust-library: + name: Build and test rust library + runs-on: ubuntu-latest + steps: + - name: checkout source + uses: actions/checkout@v2 + - name: install latest nightly + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + override: true + components: rustfmt, clippy + - name: build and test + working-directory: ./pkg/download + run: | + cargo build --tests --verbose && cargo test --verbose + build-and-test-go-module: + name: Build and test go source strategy: matrix: - go-version: [1.13.x] - platform: [macos-latest] - runs-on: ${{ matrix.platform }} + go-version: [1.16.x, 1.17.x, 1.18.x] + platform: [macos-latest, ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - - name: Setup Go - uses: actions/setup-go@v1 + - name: Install go + uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@v1 + uses: actions/checkout@v2 + - name: Test + run: go test -v -coverage ./... - name: Build - run: brew install ffmpeg && make build && make vet \ No newline at end of file + run: make diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..edeb6cc --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,18 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + # Maintain dependencies for Go + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + + # Maintain dependencies for build tools + - package-ecosystem: "gomod" + directory: "/tools" + schedule: + interval: "weekly" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..72ace2d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,51 @@ +name: release new binaries + +on: + workflow_dispatch: + inputs: + reason: + description: new release message + required: true + push: + tags: + - "*" + +permissions: + contents: write + + +jobs: + publish-crate-on-new-release: + name: "Publish crate on Release" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - uses: katyo/publish-crates@v1 + with: + registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} + publish-go-package-on-release: + name: Release go binaries + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: 1.17 + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + distribution: goreleaser + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # GoReleaser Pro key, for 'goreleaser-pro' distribution + # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..b66b67b --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,18 @@ +# Run security audit +# Author: Nyah Check +name: Run Security Audit + +on: + push: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' + +jobs: + run-security-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index e22a576..00efb8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,7 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so +# MacOS DS_Store files +.DS_Store -# Ignore all downloaded video and audio files +# Binaries for programs and plugins *.mp3 *.flv *.webm @@ -11,22 +9,49 @@ *.mp4 *.3gpp *.ogg +*.exe +*.exe~ +*.dll +*.o +*.a +*.so +*.dylib -# Compiled binaries +## Compiled binaries ytd -youtube-dl +ydl -*.exe + +# Test binary, built with `go test -c` *.test *.prof -# OSX -.DS_Store - -# CODE coverage results +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +coverage.html coverage.txt profile.out -# ENV files +# Dependency directories (remove the comment below to include it) +vendor/ + +# Output of GoReleaser +dist/ + +# Visual Studio Code files +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# GoLand and IntelliJ IDEA files +.idea/ + +# env files that usually contain secrets or local config +.env .envrc -.idea \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 2a25d3f..0000000 --- a/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ - -# Build container -FROM golang:1.13-alpine AS go-base -RUN apk add --no-cache git - -MAINTAINER Nyah Check - -ENV CGO_ENABLED=1 -ENV GO111MODULE=on -WORKDIR /app -RUN echo "Build container" -COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -o /youtube-dl . - - -# Runtime container -FROM scratch -COPY --from=go-base /youtube-dl /youtube-dl -ENTRYPOINT ["/youtube-dl"] diff --git a/LICENSE b/LICENSE index de90ebd..de1237c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 youtube-dl +Copyright (c) 2022 ydl Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index b07ac37..34464dd 100644 --- a/Makefile +++ b/Makefile @@ -1,45 +1,26 @@ -# Setup package name variables -NAME := ytd -PKG := github.com/ch3ck/$(NAME) -PREFIX?=$(shell pwd) -BUILDTAGS= -version=v1.1 - -.PHONY: clean all fmt vet build test install static -.DEFAULT: default - -all: clean fmt vet build test install - -build: clean fmt - @echo "+ $@" - CGO_ENABLED=1 go build -tags "$(BUILDTAGS) cgo" . - -static: - @echo "+ $@" - CGO_ENABLED=1 go build -tags "$(BUILDTAGS) cgo static_build" -ldflags "-w -extldflags -static" -o ytd . - -fmt: - @echo "+ $@" - @gofmt -s -l -w . | tee /dev/stderr - -test: - @echo "+ $@" - @find . -name \*.mp3 -delete #clean previous test files. - @go test -v -tags "$(BUILDTAGS) cgo" $(shell go list) - @find . -name \*.mp3 -delete # clean previous test downloads - @go test -bench=. $(shell go list) - -vet: - @echo "+ $@" - @go vet $(shell go list | grep -v vendor) - +.PHONY: build-all +build-all: build-static + +.PHONY: run-all +run-all: run-static + +.PHONY: build-static +build-static: + rustup toolchain install nightly + cd pkg/download && cargo +nightly build --release + cp pkg/download/target/release/libydl.a pkg/ + go build -v ./... + +.PHONY: run-static +run-static: + RUST_LOG=trace ./ydl + +# test rust lib +.PHONY: test-rs +test-rs: + cd pkg/download && RUST_LOG=trace cargo test -- --nocapture + +# clean all packages +.PHONY: clean clean: - @echo "+ $@" - @rm -rf ytd - @find . -name \*.mp3 -delete - @find . -name \*.flv -delete - -install: - @echo "+ $@" - @docker build -t ch3ck/youtube-dl:$(version) . - @go install . + rm -rf main_static pkg/libydl.so pkg/libydl.a pkg/download/target diff --git a/README.md b/README.md index c2375e2..65a4f57 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,43 @@ -# youtube-dl +# ydl -[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/ch3ck/youtube-dl/Build?style=for-the-badge)](https://github.com/ch3ck/youtube-dl/actions) -[![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://godoc.org/github.com/ch3ck/youtube-dl) -[![GitHub license](https://img.shields.io/github/license/ch3ck/youtube-dl?style=for-the-badge)](https://github.com/ch3ck/youtube-dl/blob/master/LICENSE) +[![Build](https://github.com/ch3ck/youtube-dl/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/ch3ck/youtube-dl/actions/workflows/ci.yml) +[![CodeQL](https://github.com/ch3ck/youtube-dl/actions/workflows/codeql-analysis.yml/badge.svg?branch=master)](https://github.com/ch3ck/youtube-dl/actions/workflows/codeql-analysis.yml)) +[![forthebadge](https://forthebadge.com/images/badges/contains-technical-debt.svg)](https://forthebadge.com) -`youtube-dl` is a simple youtube video downloader and can also download multiple videos concurrently. -Downloaded videos could be converted to `flv` or `mp3` formats. +`ydl` is a simple youtube video downloader. -## Build - -```bash -$ git clone https://github.com/Ch3ck/youtube-dl -$ cd youtube-dl -$ make -``` +## Build and Test +## Pre-requisites -## Usage +1. Install [rust nightly](https://rust-lang.github.io/rustup/concepts/channels.html) +2. Install the [go]https://go.dev/doc/install() -To install youtube-dl - -```console -$ go get github.com/Ch3ck/youtube-dl +```bash +$ git clone https://github.com/Ch3ck/ydl +$ cd ydl +$ make ``` -To run download: - -```console -youtube-dl -h -youtube-dl - Simple youtube video/audio downloader +## Install and Run -Usage: youtube-dl [OPTIONS] [ARGS] - -Flags: - -bitrate Audio Bitrate (default 123) - -format File Format(mp3, webm, flv) - -id Youtube Video ID - -path Output Path (default ".") - -version print version and exit - -h Help page -``` - -### Example +*NOTE: * release coming soon! +Once a release is ready, you could just download one of the [binaries]https://github.com/nyanchor/ydl/releases() and run: ```bash -$ ./youtube-dl -format mp3 -id lWEbEtr_Vng +$ ydl -id url # -id lWEbEtr_Vng ``` -## Roadmap - -* Download youtube video with video id or link and converts to flv or mp3. -* Support HD Video download. -* Concurrent downloads. -* Web App(PWA, Basic JS Web UI). - - -## Contributing - -Follow the basic instruction in the [CONTRIBUTING](CONTRIBUTING.md) file. ## Licence -`youtube-dl` is licensed under [The MIT Licence](LICENSE.md). +`ydl` is licensed under [The MIT Licence](LICENSE.md). + +## License +The scripts and documentation in this project are released under the [MIT License](LICENSE.md) -## Support -This project was created and is maintained by [Nyah Check](https://twitter.com/ch3ck_) +## Author +- [Nyah Check](https://nyah.dev) \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100644 index c8c8b8d..0000000 --- a/build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#Script builds the program -#!/usr/bin/env bash - -p=`pwd` -for d in $(ls ./); do - echo "building main/$d" - cd $p/cm$d - env GOOS=linux GOARCH=386 go build -done -cd $p diff --git a/download.go b/download.go deleted file mode 100644 index 6440768..0000000 --- a/download.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import ( - "context" - "io" - "os" - "strings" - "unicode" - - "github.com/sirupsen/logrus" - "github.com/wader/goutubedl" -) - -// removeWhiteSpace removes white spaces from string -// removeWhiteSpace returns a filename without whitespaces -func removeWhiteSpace(str string) string { - return strings.Map(func(r rune) rune { - if unicode.IsSpace(r) { - return -1 - } - return r - }, str) -} - -// fixExtension is a helper function that -// fixes file the extension -func fixExtension(str string) string { - if strings.Contains(str, "mp3") { - str = ".mp3" - } else { - str = ".flv" - } - - return str -} - -// decodeVideoStream processes downloaded video stream and -// decodeVideoStream calls helper functions and writes the -// output in the required format -func decodeVideoStream(videoUrl, format string) error { - - // Get video data - res, err := goutubedl.New(context.Background(), videoUrl, goutubedl.Options{}) - if err != nil { - logrus.Errorf("Unable to create goutube object %s: %v", videoUrl, err) - return err - } - - file := removeWhiteSpace(res.Info.Title) + fixExtension(format) - videoStream, err := res.Download(context.Background(), "best") - if err != nil { - logrus.Errorf("Unable to download %s stream: %v", format, err) - return err - } - defer videoStream.Close() - - // Create output file - fp, err := os.OpenFile(file, os.O_CREATE, 0755) - if err != nil { - logrus.Errorf("Unable to create output file: %v", err) - return err - } - defer fp.Close() - - io.Copy(fp, videoStream) - - return nil -} \ No newline at end of file diff --git a/download_test.go b/download_test.go deleted file mode 100644 index de83282..0000000 --- a/download_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "testing" -) - -var tables = []struct { - url, id string // input -}{ - {"https://www.youtube.com/watch?v=lWEbEtr_Vng", "lWEbEtr_Vng"}, - {"https://www.youtube.com/watch?v=ALWmcO8S-dc", "ALWmcO8S-dc"}, - {"", ""}, - {"https://www.facebook.com/mark/videos?v=RDHpNluHOAJFA", ""}, - {"https://www.youtube.com/watch?v=ALWmcO8S-dc", "ALWmcO8S-dc"}, - {"https://www.wsj.com/articles/trump-administration-wont-withdraw-from-paris-climate-deal-1505593922", ""}, - {"https://vimeo.com/101522071", ""}, -} - -var vid []string - -func TestApi(t *testing.T) { - - // path := "test" - for i, table := range tables { - err := decodeVideoStream(table.url, "mp3") - if err != nil { - t.Errorf("videoId(%d): expected %q, actual %q", i, table.id, err) - } - } -} - -func BenchmarkVideoId(b *testing.B) { - for n := 0; n < b.N; n++ { - if err := decodeVideoStream(tables[0].url, "mp3"); err != nil { - b.Errorf("Error downloading video: %v", err) - } - } -} diff --git a/go.mod b/go.mod index 8ebf5bb..7b51fdf 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,3 @@ -module github.com/ch3ck/youtube-dl +module ydl -go 1.12 - -require ( - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect - github.com/sirupsen/logrus v1.5.0 - github.com/stretchr/objx v0.2.0 // indirect - github.com/stretchr/testify v1.4.0 // indirect - github.com/wader/goutubedl v0.0.0-20200327095909-c841a70bbbad - github.com/wader/osleaktest v0.0.0-20191111175233-f643b0fed071 // indirect - golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.2.7 // indirect -) +go 1.17 diff --git a/go.sum b/go.sum index 2f5e223..e69de29 100644 --- a/go.sum +++ b/go.sum @@ -1,31 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= -github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/wader/goutubedl v0.0.0-20200115162246-9eae90476a5d h1:+noQWJMxTu1ruNLG1KcA7tz0kHR7QYUBu+88Y2+w930= -github.com/wader/goutubedl v0.0.0-20200115162246-9eae90476a5d/go.mod h1:TEOvzRw4YvTeOdSvGyoZD9KbKK4xbx0EJoSqj4v6yAs= -github.com/wader/goutubedl v0.0.0-20200327095909-c841a70bbbad h1:16IhHxbHNcIjuAXABVcKczJlHRO7sRwLPCnSmogRPCA= -github.com/wader/goutubedl v0.0.0-20200327095909-c841a70bbbad/go.mod h1:TEOvzRw4YvTeOdSvGyoZD9KbKK4xbx0EJoSqj4v6yAs= -github.com/wader/osleaktest v0.0.0-20190723190525-c53af4cfc4a3/go.mod h1:XD6emOFPHVzb0+qQpiNOdPL2XZ0SRUM0N5JHuq6OmXo= -github.com/wader/osleaktest v0.0.0-20191111175233-f643b0fed071/go.mod h1:XD6emOFPHVzb0+qQpiNOdPL2XZ0SRUM0N5JHuq6OmXo= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c h1:gUYreENmqtjZb2brVfUas1sC6UivSY8XwKwPo8tloLs= -golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b h1:h03Ur1RlPrGTjua4koYdpGl8W0eYo8p1uI9w7RPlkdk= -golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 463d7f1..2c65c7c 100644 --- a/main.go +++ b/main.go @@ -1,28 +1,28 @@ -// main program entry package main +// #cgo LDFLAGS: ./pkg/libydl.a -ldl +// #include "./pkg/download.h" +import "C" + import ( "flag" - "fmt" - _ "net/http/pprof" + "log" "os" - "runtime" - "runtime/pprof" "strings" - "sync" - - "github.com/sirupsen/logrus" ) const ( // help Banner BANNER = `` + - `youtube-dl - Simple youtube video/audio DownloadStreams` + "\n\n" + - `Usage: youtube-dl [OPTIONS] [ARGS]` + "\n" + `ydl - simple youtube downloader` + "\n\n" + + `Usage: [OPTIONS] [ARGS]` + "\n" + + "\t" + `ydl -id video url or id` + "\n" + + "\t" + `ydl -path download path (defaults to '.')` + "\n" + + "\t" + `Example: ydl -id https://www.youtube.com/watch?v=lWEbEtr_Vng` + "\n\n\n" // current version - VERSION = "v0.2" + VERSION = "v1.0" // default maximum concurrent downloads MAXDOWNLOADS = 5 @@ -37,35 +37,19 @@ var ( bitrate uint ) -var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") - func init() { // parse flags flag.StringVar(&ids, "id", "", "video url or video id; separate multiple ids with a comma.") - flag.StringVar(&format, "format", "flv", "download file format(mp3 or flv)") flag.StringVar(&path, "path", ".", "download file path") - flag.BoolVar(&version, "version", false, "print version number") - flag.UintVar(&bitrate, "bitrate", 192, "audio bitrate") flag.Usage = func() { - fmt.Fprint(os.Stderr, fmt.Sprintf("%s \t %s", BANNER, VERSION)) + log.Fatalf("%s \t %s", BANNER, VERSION) flag.PrintDefaults() } } func main() { flag.Parse() - - if *cpuprofile != "" { - f, err := os.Create(*cpuprofile) - if err != nil { - logrus.Fatalf("%v", err) - } - pprof.StartCPUProfile(f) - defer pprof.StopCPUProfile() - } - runtime.SetBlockProfileRate(20) - args := os.Args if len(args) < 2 { usageAndExit(BANNER, 2) @@ -74,8 +58,13 @@ func main() { path, _ = os.Getwd() } + // parse urls urls := parseUrls(ids) - beginDownload(urls) + + // start download + if err := concurrentDownload(MAXDOWNLOADS, format, urls); err != nil { + log.Fatalf("Unable to download video(s): %v with errors => %v", urls, err) + } } // parseUrls for video download @@ -87,46 +76,24 @@ func parseUrls(urls string) []string { } } -func beginDownload(urls []string) { - if len(urls) < 2 { - if err := decodeVideoStream(urls[0], format); err != nil { - logrus.Errorf("Unable to beginDownload: %v", err) - } - } else { - if err := concurrentDownload(MAXDOWNLOADS, format, urls); err != nil { - logrus.Errorf("Unable to concurrently download videos: %v with errors => %v", urls, err) - } - } -} - //DownloadStreams download a batch of elements asynchronously -func concurrentDownload(maxOperations int, format string, urls []string) <-chan error { - - var wg sync.WaitGroup - wg.Add(len(urls)) - - ch := make(chan error, maxOperations) +func concurrentDownload(maxOperations int, format string, urls []string) error { for _, url := range urls { + // download video go func(url string) { - defer wg.Done() - ch <- decodeVideoStream(url, format) + cUrl := C.CString(url) + cPath := C.CString(path) + + C.download(cUrl, cPath) }(url) } - - go func() { - wg.Wait() - close(ch) - }() - - return ch + return nil } func usageAndExit(message string, exitCode int) { if message != "" { - fmt.Fprintf(os.Stderr, message) - fmt.Fprintf(os.Stderr, "\n\n") + log.Fatalf(message) } flag.Usage() - fmt.Fprintf(os.Stderr, "\n") os.Exit(exitCode) } diff --git a/pkg/download.h b/pkg/download.h new file mode 100644 index 0000000..f2a446b --- /dev/null +++ b/pkg/download.h @@ -0,0 +1,13 @@ +/** -*- mode: C; -*- + * download.h + * + * Copyright (C) 2021 Nyah Check + * + **/ + +#ifndef DOWNLOAD_H +#define DOWNLOAD_H + +char * download(char *url, char *path); + +#endif diff --git a/pkg/download/.gitignore b/pkg/download/.gitignore new file mode 100644 index 0000000..6351a5d --- /dev/null +++ b/pkg/download/.gitignore @@ -0,0 +1,12 @@ +# rs will have compiled files and executables +/target/ +Cargo.lock + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +# Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk +target +build diff --git a/pkg/download/.rustfmt.toml b/pkg/download/.rustfmt.toml new file mode 100644 index 0000000..a6a1306 --- /dev/null +++ b/pkg/download/.rustfmt.toml @@ -0,0 +1,5 @@ +max_width = 80 +newline_style = "Unix" +reorder_imports = true +use_small_heuristics = "Max" +use_field_init_shorthand = true diff --git a/pkg/download/Cargo.toml b/pkg/download/Cargo.toml new file mode 100644 index 0000000..806c4f2 --- /dev/null +++ b/pkg/download/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "ydl" +description = "download youtube videos to path" +version = "0.1.0" +authors = ["Nyah Check "] +license = "MIT" +homepage = "https://github.com/ch3ck" +repository = "https://github.com/ch3ck/ydl" +readme = "README.md" +categories = ["web-programming"] +keywords = [ "rust"] +edition = "2018" +documentation = "https://docs.rs/ydl" + + +[lib] +crate-type = ["staticlib"] + +[profile.release] +debug = true +overflow-checks = true +panic = "abort" + +[dependencies] +clippy = "0.0.302" +env_logger = "0.9.0" +log = "0.4.14" +rustube = "0.3.6" +tokio = { version = "1.15.0", features = ["macros", "io-util", "sync", "rt-multi-thread"] } diff --git a/pkg/download/src/lib.rs b/pkg/download/src/lib.rs new file mode 100644 index 0000000..79ba49d --- /dev/null +++ b/pkg/download/src/lib.rs @@ -0,0 +1,42 @@ +//! -*- mode: rust; -*- +//! +//! download - downloads youtube files +use rustube::{Id, Video}; +use std::ffi; + +#[no_mangle] +pub async extern "C" fn download<'a>( + url: &'a str, + path: &'a str, +) -> Result<(), Box> { + env_logger::init(); + + let id = Id::from_raw(&url)?; + let video = Video::from_id(id.into_owned()).await?; + + let _result = video + .streams() + .iter() + .filter(|stream| { + stream.includes_video_track && stream.includes_audio_track + }) + .max_by_key(|stream| stream.quality_label) + .unwrap() + .download_to_dir(&path) + .await + .unwrap(); + + Ok(()) +} + +#[cfg(test)] +pub mod tests { + use super::*; + + #[tokio::test] + async fn test_download() { + let url = String::from("https://www.youtube.com/watch?v=lWEbEtr_Vng"); + let fp = String::from("~/Downloads"); + download(url.as_str(), fp.as_str()).await.expect("expect an OK(_) response"); + } +}