Skip to content

Commit

Permalink
Auto update youtube-dl #119 #130
Browse files Browse the repository at this point in the history
  • Loading branch information
mxpv committed Apr 19, 2020
1 parent e764ab6 commit 0e4a7a6
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 22 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ any device in podcast client.
- One-click deployment for AWS.
- Runs on Windows, Mac OS, Linux, and Docker.
- Supports ARM.
- Automatic youtube-dl self update.

## Dependencies

If you're running the CLI as binary (e.g. not via Docker), you need to make sure that dependencies are available on
your system. Currently Podsync depends on `youtube-dl` and `ffmpeg`.
your system. Currently, Podsync depends on `youtube-dl` and `ffmpeg`.

On Mac you can install those with `brew`:
```
Expand Down Expand Up @@ -78,6 +79,9 @@ vimeo = "{VIMEO_API_TOKEN}"
[database]
badger = { truncate = true, file_io = true } # See https://github.com/dgraph-io/badger#memory-usage

[downloader]
self_update = true # Optional, auto update youtube-dl every 24 hours

# Optional log config. If not specified logs to the stdout
[log]
filename = "podsync.log"
Expand Down
2 changes: 1 addition & 1 deletion cmd/podsync/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func main() {
"date": date,
}).Info("running podsync")

downloader, err := ytdl.New(ctx)
downloader, err := ytdl.New(ctx, cfg.Downloader.SelfUpdate)
if err != nil {
log.WithError(err).Fatal("youtube-dl error")
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ type Log struct {
Compress bool `toml:"compress"`
}

// Downloader is a youtube-dl related configuration
type Downloader struct {
// SelfUpdate toggles self update every 24 hour
SelfUpdate bool `toml:"self_update"`
}

type Config struct {
// Server is the web server configuration
Server Server `toml:"server"`
Expand All @@ -113,6 +119,8 @@ type Config struct {
Feeds map[string]*Feed
// Tokens is API keys to use to access YouTube/Vimeo APIs.
Tokens Tokens `toml:"tokens"`
// Downloader (youtube-dl) configuration
Downloader Downloader `toml:"downloader"`
}

// LoadConfig loads TOML configuration from a file path
Expand Down
5 changes: 5 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ data_dir = "test/data/"
[database]
dir = "/home/user/db/"
[downloader]
self_update = true
[feeds]
[feeds.XYZ]
url = "https://youtube.com/watch?v=ygIUF678y40"
Expand Down Expand Up @@ -68,6 +71,8 @@ dir = "/home/user/db/"
assert.EqualValues(t, "en", feed.Custom.Language)

assert.Nil(t, config.Database.Badger)

assert.True(t, config.Downloader.SelfUpdate)
}

func TestApplyDefaults(t *testing.T) {
Expand Down
21 changes: 21 additions & 0 deletions pkg/ytdl/temp_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ytdl

import (
"os"

log "github.com/sirupsen/logrus"
)

type tempFile struct {
*os.File
dir string
}

func (f *tempFile) Close() error {
err := f.File.Close()
err1 := os.RemoveAll(f.dir)
if err1 != nil {
log.Errorf("could not remove temp dir: %v", err1)
}
return err
}
65 changes: 45 additions & 20 deletions pkg/ytdl/ytdl.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os/exec"
"path/filepath"
"strings"
"sync"
"time"

"github.com/pkg/errors"
Expand All @@ -19,22 +20,21 @@ import (
"github.com/mxpv/podsync/pkg/model"
)

const DownloadTimeout = 10 * time.Minute
const (
DownloadTimeout = 10 * time.Minute
UpdatePeriod = 24 * time.Hour
)

var (
ErrTooManyRequests = errors.New(http.StatusText(http.StatusTooManyRequests))
)

type YoutubeDl struct {
path string
}

type tempFile struct {
*os.File
dir string
path string
updateLock sync.Mutex // Don't call youtube-dl while self updating
}

func New(ctx context.Context) (*YoutubeDl, error) {
func New(ctx context.Context, update bool) (*YoutubeDl, error) {
path, err := exec.LookPath("youtube-dl")
if err != nil {
return nil, errors.Wrap(err, "youtube-dl binary not found")
Expand All @@ -58,10 +58,25 @@ func New(ctx context.Context) (*YoutubeDl, error) {
return nil, err
}

if update {
// Do initial update at launch
if err := ytdl.Update(ctx); err != nil {
log.WithError(err).Error("failed to update youtube-dl")
}

go func() {
for range time.After(UpdatePeriod) {

This comment has been minimized.

Copy link
@dop251

dop251 Apr 19, 2020

Contributor

This will only fire once after which the loop will hang. Proof: https://play.golang.org/p/wA4V38IRlo3

This comment has been minimized.

Copy link
@mxpv

mxpv Apr 19, 2020

Author Owner

Good catch, thanks. Fixed in df7dc56

if err := ytdl.Update(context.Background()); err != nil {
log.WithError(err).Error("update failed")
}
}
}()
}

return ytdl, nil
}

func (dl YoutubeDl) ensureDependencies(ctx context.Context) error {
func (dl *YoutubeDl) ensureDependencies(ctx context.Context) error {
found := false

if path, err := exec.LookPath("ffmpeg"); err == nil {
Expand Down Expand Up @@ -93,7 +108,22 @@ func (dl YoutubeDl) ensureDependencies(ctx context.Context) error {
return nil
}

func (dl YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episode *model.Episode) (r io.ReadCloser, err error) {
func (dl *YoutubeDl) Update(ctx context.Context) error {
dl.updateLock.Lock()
defer dl.updateLock.Unlock()

log.Info("updating youtube-dl")
output, err := dl.exec(ctx, "--update", "--verbose")
if err != nil {
log.WithError(err).Error(output)
return errors.Wrap(err, "failed to self update youtube-dl")
}

log.Info(output)
return nil
}

func (dl *YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episode *model.Episode) (r io.ReadCloser, err error) {
tmpDir, err := ioutil.TempDir("", "podsync-")
if err != nil {
return nil, errors.Wrap(err, "failed to get temp dir for download")
Expand All @@ -112,6 +142,10 @@ func (dl YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episo
filePath := filepath.Join(tmpDir, fmt.Sprintf("%s.%s", episode.ID, "%(ext)s"))

args := buildArgs(feedConfig, episode, filePath)

dl.updateLock.Lock()
defer dl.updateLock.Unlock()

output, err := dl.exec(ctx, args...)
if err != nil {
log.WithError(err).Errorf("youtube-dl error: %s", filePath)
Expand Down Expand Up @@ -140,7 +174,7 @@ func (dl YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episo
return &tempFile{File: f, dir: tmpDir}, nil
}

func (dl YoutubeDl) exec(ctx context.Context, args ...string) (string, error) {
func (dl *YoutubeDl) exec(ctx context.Context, args ...string) (string, error) {
ctx, cancel := context.WithTimeout(ctx, DownloadTimeout)
defer cancel()

Expand Down Expand Up @@ -181,12 +215,3 @@ func buildArgs(feedConfig *config.Feed, episode *model.Episode, outputFilePath s
args = append(args, "--output", outputFilePath, episode.VideoURL)
return args
}

func (f *tempFile) Close() error {
err := f.File.Close()
err1 := os.RemoveAll(f.dir)
if err1 != nil {
log.Errorf("could not remove temp dir: %v", err1)
}
return err
}

0 comments on commit 0e4a7a6

Please sign in to comment.