diff options
-rw-r--r-- | rhimport/fetcher.go | 62 | ||||
-rw-r--r-- | rhimport/youtubedl_responses.go | 51 |
2 files changed, 113 insertions, 0 deletions
diff --git a/rhimport/fetcher.go b/rhimport/fetcher.go index 469d36b..3933105 100644 --- a/rhimport/fetcher.go +++ b/rhimport/fetcher.go @@ -25,6 +25,7 @@ package rhimport import ( + "bytes" "fmt" "io" "io/ioutil" @@ -32,6 +33,7 @@ import ( "net/http" "net/url" "os" + "os/exec" "os/user" "path" "path/filepath" @@ -134,9 +136,51 @@ func curlProgressCallback(dltotal, dlnow, ultotal, ulnow float64, userdata inter return true } +func checkYoutubeDL(ctx *Context, res *Result, uri *url.URL) *YoutubeDLInfo { + cmd := exec.Command("youtube-dl", "--no-playlist", "-f", "bestaudio/best", "--prefer-free-formats", "-J", ctx.SourceUri) + var stderr, stdout bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + done := make(chan *YoutubeDLInfo) + go func() { + defer func() { + done <- nil + }() + if err := cmd.Run(); err != nil { + rhdl.Printf("youtube-dl: %v, stderr: %s", err, strings.TrimSpace(stderr.String())) + return + } + info, err := NewYoutubeDLInfoFromJSON(&stdout) + if err != nil { + rhdl.Printf("youtube-dl: %v, stderr: %s", err, strings.TrimSpace(stderr.String())) + return + } + rhl.Printf("youtube-dl: extractor: %s -> %s", info.Extractor, info.URL) + ctx.SourceUri = info.URL + done <- info + }() + + select { + case info := <-done: + return info + case <-ctx.Cancel: + cmd.Process.Kill() + res.ResponseCode = http.StatusNoContent + res.ErrorString = "canceled" + return nil + } +} + func fetchFileCurl(ctx *Context, res *Result, uri *url.URL) (err error) { rhl.Printf("curl-based fetcher called for '%s'", ctx.SourceUri) + info := checkYoutubeDL(ctx, res, uri) + if res.ResponseCode == http.StatusNoContent { + rhl.Printf("download of '%s' got canceled", ctx.SourceUri) + return nil + } + easy := curl.EasyInit() if easy == nil { err = fmt.Errorf("Error initializing libcurl") @@ -148,10 +192,28 @@ func fetchFileCurl(ctx *Context, res *Result, uri *url.URL) (err error) { easy.Setopt(curl.OPT_URL, ctx.SourceUri) easy.Setopt(curl.OPT_USERAGENT, "Radio Helsinki Import") + if info != nil && (info.Protocol == "http" || info.Protocol == "https") { + if len(info.HTTPHeaders) > 0 { + var h []string + for key, value := range info.HTTPHeaders { + h = append(h, key+": "+value) + } + easy.Setopt(curl.OPT_HTTPHEADER, h) + rhdl.Printf("added HTTP header: %q", h) + } + } + cbdata := &FetcherCurlCBData{ctx: ctx, res: res, remotename: path.Base(uri.Path)} if cbdata.basepath, err = ioutil.TempDir(ctx.conf.TempDir, "rhimportd-"); err != nil { return } + if info != nil { + if info.Title == "" { + cbdata.remotename = info.ID + "." + info.Ext + } else { + cbdata.remotename = info.Title + "." + info.Ext + } + } easy.Setopt(curl.OPT_HEADERFUNCTION, curlHeaderCallback) easy.Setopt(curl.OPT_HEADERDATA, cbdata) diff --git a/rhimport/youtubedl_responses.go b/rhimport/youtubedl_responses.go new file mode 100644 index 0000000..1a8c0ca --- /dev/null +++ b/rhimport/youtubedl_responses.go @@ -0,0 +1,51 @@ +// +// rhimportd +// +// The Radio Helsinki Rivendell Import Daemon +// +// +// Copyright (C) 2015-2016 Christian Pointner <equinox@helsinki.at> +// +// This file is part of rhimportd. +// +// rhimportd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// rhimportd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with rhimportd. If not, see <http://www.gnu.org/licenses/>. +// + +package rhimport + +import ( + "encoding/json" + "fmt" + "io" +) + +type YoutubeDLInfo struct { + ID string `json:"id"` + Title string `json:"title"` + URL string `json:"url"` + Extractor string `json:"extractor"` + Ext string `json:"ext"` + Protocol string `json:"protocol"` + HTTPHeaders map[string]string `json:"http_headers"` +} + +func NewYoutubeDLInfoFromJSON(data io.Reader) (res *YoutubeDLInfo, err error) { + decoder := json.NewDecoder(data) + res = &YoutubeDLInfo{} + if jsonerr := decoder.Decode(res); jsonerr != nil { + err = fmt.Errorf("Error parsing JSON response: %s", jsonerr) + return + } + return +} |