Better errors and error handling

Fix issue with ignore errors for playlists, now ignore
errors if we get info JSON with a non empty ID.
This commit is contained in:
Mattias Wadman
2019-11-05 00:45:14 +01:00
parent 77a906a487
commit 3b67f6a80c
2 changed files with 61 additions and 28 deletions

View File

@ -6,6 +6,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -29,13 +30,19 @@ type nopPrinter struct{}
func (nopPrinter) Print(v ...interface{}) {} func (nopPrinter) Print(v ...interface{}) {}
// Error youtube-dl specific error // YoutubedlError is a error from youtube-dl
type Error string type YoutubedlError string
func (e Error) Error() string { func (e YoutubedlError) Error() string {
return string(e) return string(e)
} }
// ErrNotAPlaylist error when single entry when expected a playlist
var ErrNotAPlaylist = errors.New("single entry when expected a playlist")
// ErrNotASingleEntry error when playlist when expected a single entry
var ErrNotASingleEntry = errors.New("playlist when expected a single entry")
// Info youtube-dl info // Info youtube-dl info
type Info struct { type Info struct {
// Generated from youtube-dl README using: // Generated from youtube-dl README using:
@ -241,9 +248,10 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
cmd := exec.CommandContext( cmd := exec.CommandContext(
ctx, ctx,
Path, Path,
// see comment below about ignoring errors for playlists
"--ignore-errors",
"--no-call-home", "--no-call-home",
"--no-cache-dir", "--no-cache-dir",
"--ignore-errors",
"--skip-download", "--skip-download",
"--restrict-filenames", "--restrict-filenames",
// provide URL via stdin for security, youtube-dl has some run command args // provide URL via stdin for security, youtube-dl has some run command args
@ -302,28 +310,35 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
} }
} }
// HACK: --ignore-errors still return error message and exit code != 0 infoSeemsOk := false
// so workaround is to assume things went ok if we get some json on stdout if len(stdoutBuf.Bytes()) > 0 {
if len(stdoutBuf.Bytes()) == 0 { if infoErr := json.Unmarshal(stdoutBuf.Bytes(), &info); infoErr != nil {
return Info{}, nil, infoErr
}
isPlaylist := info.Type == "playlist" || info.Type == "multi_video"
switch {
case options.Type == TypePlaylist && !isPlaylist:
return Info{}, nil, ErrNotAPlaylist
case options.Type == TypeSingle && isPlaylist:
return Info{}, nil, ErrNotASingleEntry
default:
// any type
}
// HACK: --ignore-errors still return error message and exit code != 0
// so workaround is to assume things went ok if we get some ok json on stdout
infoSeemsOk = info.ID != ""
}
if !infoSeemsOk {
if errMessage != "" { if errMessage != "" {
return Info{}, nil, Error(errMessage) return Info{}, nil, YoutubedlError(errMessage)
} else if cmdErr != nil { } else if cmdErr != nil {
return Info{}, nil, cmdErr return Info{}, nil, cmdErr
} }
}
if infoErr := json.Unmarshal(stdoutBuf.Bytes(), &info); infoErr != nil { return Info{}, nil, fmt.Errorf("unknown error")
return Info{}, nil, infoErr
}
isPlaylist := info.Type == "playlist" || info.Type == "multi_video"
switch {
case options.Type == TypePlaylist && !isPlaylist:
return Info{}, nil, fmt.Errorf("is not a playlist")
case options.Type == TypeSingle && isPlaylist:
return Info{}, nil, fmt.Errorf("is not a single entry")
default:
// any type
} }
get := func(url string) (*http.Response, error) { get := func(url string) (*http.Response, error) {
@ -371,7 +386,7 @@ func infoFromURL(ctx context.Context, rawURL string, options Options) (info Info
} }
} }
// as we ignore errors some entries might show up as null // as we ignore errors for playlists some entries might show up as null
if options.Type == TypePlaylist { if options.Type == TypePlaylist {
var filteredEntrise []Info var filteredEntrise []Info
for _, e := range info.Entries { for _, e := range info.Entries {

View File

@ -181,6 +181,20 @@ func TestPlaylist(t *testing.T) {
} }
} }
func TestTestUnsupportedURL(t *testing.T) {
defer leaktest.Check(t)()
_, ydlResultErr := New(context.Background(), "https://www.google.com", Options{})
if ydlResultErr == nil {
t.Errorf("expected unsupported url")
}
expectedErr := "Unsupported URL: https://www.google.com"
if ydlResultErr != nil && ydlResultErr.Error() != expectedErr {
t.Errorf("expected error %q got %q", expectedErr, ydlResultErr.Error())
}
}
func TestPlaylistWithPrivateVideo(t *testing.T) { func TestPlaylistWithPrivateVideo(t *testing.T) {
defer leaktest.Check(t)() defer leaktest.Check(t)()
@ -233,22 +247,26 @@ func TestSubtitles(t *testing.T) {
} }
} }
func TestErrorIsNotAPlaylist(t *testing.T) { func TestErrorNotAPlaylist(t *testing.T) {
defer leakChecks(t)()
_, ydlResultErr := New(context.Background(), testVideoRawURL, Options{ _, ydlResultErr := New(context.Background(), testVideoRawURL, Options{
Type: TypePlaylist, Type: TypePlaylist,
DownloadThumbnail: false, DownloadThumbnail: false,
}) })
if ydlResultErr.Error() != "is not a playlist" { if ydlResultErr != ErrNotAPlaylist {
t.Errorf("expected is playlist error") t.Errorf("expected is playlist error, got %s", ydlResultErr)
} }
} }
func TestErrorIsNotASingleEntry(t *testing.T) { func TestErrorNotASingleEntry(t *testing.T) {
defer leakChecks(t)()
_, ydlResultErr := New(context.Background(), playlistRawURL, Options{ _, ydlResultErr := New(context.Background(), playlistRawURL, Options{
Type: TypeSingle, Type: TypeSingle,
DownloadThumbnail: false, DownloadThumbnail: false,
}) })
if ydlResultErr.Error() != "is not a single entry" { if ydlResultErr != ErrNotASingleEntry {
t.Errorf("expected is not a playlist error") t.Errorf("expected is single entry error, got %s", ydlResultErr)
} }
} }