Skip to content

Commit 0e4a7a6

Browse files
committed
Auto update youtube-dl #119 #130
1 parent e764ab6 commit 0e4a7a6

File tree

6 files changed

+85
-22
lines changed

6 files changed

+85
-22
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ any device in podcast client.
2929
- One-click deployment for AWS.
3030
- Runs on Windows, Mac OS, Linux, and Docker.
3131
- Supports ARM.
32+
- Automatic youtube-dl self update.
3233

3334
## Dependencies
3435

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

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

82+
[downloader]
83+
self_update = true # Optional, auto update youtube-dl every 24 hours
84+
8185
# Optional log config. If not specified logs to the stdout
8286
[log]
8387
filename = "podsync.log"

cmd/podsync/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func main() {
9797
"date": date,
9898
}).Info("running podsync")
9999

100-
downloader, err := ytdl.New(ctx)
100+
downloader, err := ytdl.New(ctx, cfg.Downloader.SelfUpdate)
101101
if err != nil {
102102
log.WithError(err).Fatal("youtube-dl error")
103103
}

pkg/config/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ type Log struct {
101101
Compress bool `toml:"compress"`
102102
}
103103

104+
// Downloader is a youtube-dl related configuration
105+
type Downloader struct {
106+
// SelfUpdate toggles self update every 24 hour
107+
SelfUpdate bool `toml:"self_update"`
108+
}
109+
104110
type Config struct {
105111
// Server is the web server configuration
106112
Server Server `toml:"server"`
@@ -113,6 +119,8 @@ type Config struct {
113119
Feeds map[string]*Feed
114120
// Tokens is API keys to use to access YouTube/Vimeo APIs.
115121
Tokens Tokens `toml:"tokens"`
122+
// Downloader (youtube-dl) configuration
123+
Downloader Downloader `toml:"downloader"`
116124
}
117125

118126
// LoadConfig loads TOML configuration from a file path

pkg/config/config_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ data_dir = "test/data/"
2525
[database]
2626
dir = "/home/user/db/"
2727
28+
[downloader]
29+
self_update = true
30+
2831
[feeds]
2932
[feeds.XYZ]
3033
url = "https://youtube.com/watch?v=ygIUF678y40"
@@ -68,6 +71,8 @@ dir = "/home/user/db/"
6871
assert.EqualValues(t, "en", feed.Custom.Language)
6972

7073
assert.Nil(t, config.Database.Badger)
74+
75+
assert.True(t, config.Downloader.SelfUpdate)
7176
}
7277

7378
func TestApplyDefaults(t *testing.T) {

pkg/ytdl/temp_file.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package ytdl
2+
3+
import (
4+
"os"
5+
6+
log "github.com/sirupsen/logrus"
7+
)
8+
9+
type tempFile struct {
10+
*os.File
11+
dir string
12+
}
13+
14+
func (f *tempFile) Close() error {
15+
err := f.File.Close()
16+
err1 := os.RemoveAll(f.dir)
17+
if err1 != nil {
18+
log.Errorf("could not remove temp dir: %v", err1)
19+
}
20+
return err
21+
}

pkg/ytdl/ytdl.go

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"os/exec"
1111
"path/filepath"
1212
"strings"
13+
"sync"
1314
"time"
1415

1516
"github.com/pkg/errors"
@@ -19,22 +20,21 @@ import (
1920
"github.com/mxpv/podsync/pkg/model"
2021
)
2122

22-
const DownloadTimeout = 10 * time.Minute
23+
const (
24+
DownloadTimeout = 10 * time.Minute
25+
UpdatePeriod = 24 * time.Hour
26+
)
2327

2428
var (
2529
ErrTooManyRequests = errors.New(http.StatusText(http.StatusTooManyRequests))
2630
)
2731

2832
type YoutubeDl struct {
29-
path string
30-
}
31-
32-
type tempFile struct {
33-
*os.File
34-
dir string
33+
path string
34+
updateLock sync.Mutex // Don't call youtube-dl while self updating
3535
}
3636

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

61+
if update {
62+
// Do initial update at launch
63+
if err := ytdl.Update(ctx); err != nil {
64+
log.WithError(err).Error("failed to update youtube-dl")
65+
}
66+
67+
go func() {
68+
for range time.After(UpdatePeriod) {
69+
if err := ytdl.Update(context.Background()); err != nil {
70+
log.WithError(err).Error("update failed")
71+
}
72+
}
73+
}()
74+
}
75+
6176
return ytdl, nil
6277
}
6378

64-
func (dl YoutubeDl) ensureDependencies(ctx context.Context) error {
79+
func (dl *YoutubeDl) ensureDependencies(ctx context.Context) error {
6580
found := false
6681

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

96-
func (dl YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episode *model.Episode) (r io.ReadCloser, err error) {
111+
func (dl *YoutubeDl) Update(ctx context.Context) error {
112+
dl.updateLock.Lock()
113+
defer dl.updateLock.Unlock()
114+
115+
log.Info("updating youtube-dl")
116+
output, err := dl.exec(ctx, "--update", "--verbose")
117+
if err != nil {
118+
log.WithError(err).Error(output)
119+
return errors.Wrap(err, "failed to self update youtube-dl")
120+
}
121+
122+
log.Info(output)
123+
return nil
124+
}
125+
126+
func (dl *YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episode *model.Episode) (r io.ReadCloser, err error) {
97127
tmpDir, err := ioutil.TempDir("", "podsync-")
98128
if err != nil {
99129
return nil, errors.Wrap(err, "failed to get temp dir for download")
@@ -112,6 +142,10 @@ func (dl YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episo
112142
filePath := filepath.Join(tmpDir, fmt.Sprintf("%s.%s", episode.ID, "%(ext)s"))
113143

114144
args := buildArgs(feedConfig, episode, filePath)
145+
146+
dl.updateLock.Lock()
147+
defer dl.updateLock.Unlock()
148+
115149
output, err := dl.exec(ctx, args...)
116150
if err != nil {
117151
log.WithError(err).Errorf("youtube-dl error: %s", filePath)
@@ -140,7 +174,7 @@ func (dl YoutubeDl) Download(ctx context.Context, feedConfig *config.Feed, episo
140174
return &tempFile{File: f, dir: tmpDir}, nil
141175
}
142176

143-
func (dl YoutubeDl) exec(ctx context.Context, args ...string) (string, error) {
177+
func (dl *YoutubeDl) exec(ctx context.Context, args ...string) (string, error) {
144178
ctx, cancel := context.WithTimeout(ctx, DownloadTimeout)
145179
defer cancel()
146180

@@ -181,12 +215,3 @@ func buildArgs(feedConfig *config.Feed, episode *model.Episode, outputFilePath s
181215
args = append(args, "--output", outputFilePath, episode.VideoURL)
182216
return args
183217
}
184-
185-
func (f *tempFile) Close() error {
186-
err := f.File.Close()
187-
err1 := os.RemoveAll(f.dir)
188-
if err1 != nil {
189-
log.Errorf("could not remove temp dir: %v", err1)
190-
}
191-
return err
192-
}

0 commit comments

Comments
 (0)