From c8078842238856b9a2573d93d523e4e29685f5e3 Mon Sep 17 00:00:00 2001 From: Nyah Check Date: Tue, 26 Sep 2017 16:38:17 -0600 Subject: [PATCH] api: Fixes to download API. --- api/apiconv.go | 78 ++++++++++++++++++------------------ api/apidata.go | 106 +++++++++++++++++++++++++++++++++---------------- ytd.go | 2 +- 3 files changed, 111 insertions(+), 75 deletions(-) diff --git a/api/apiconv.go b/api/apiconv.go index 2864711..1ce7ed8 100644 --- a/api/apiconv.go +++ b/api/apiconv.go @@ -12,72 +12,72 @@ import ( "errors" "fmt" "io" + "log" "net/http" "os" "os/exec" "path/filepath" - - "github.com/Sirupsen/logrus" ) //Converts Decoded Video file to mp3 by default with 123 bitrate or to //flv if otherwise specified and downloads to system -func APIConvertVideo(file, id, format string, bitrate uint, decVideo []string) error { +func ApiConvertVideo(file, id, format string, bitrate uint, decVideo []string) error { cmd := exec.Command("ffmpeg", "-i", "-", "-ab", fmt.Sprintf("%dk", bitrate), file) - stdin, err := cmd.StdinPipe() + err = os.MkdirAll(filepath.Dir(file), 666) if err != nil { return err } - if filepath.Ext(file) != ".mp3" && filepath.Ext(file) != ".flv" { - file = file[:len(file)-4] + ".mp3" + out, err := os.Create(file) + if err != nil { + return err } - //logrus.Infof("Converting video to %q format", filepath.Ext(file)) - if filepath.Ext(file) == ".mp3" { - /* NOTE: To modify to use Go ffmpeg bindings or cgo */ - - buf := &bytes.Buffer{} - gob.NewEncoder(buf).Encode(decVideo) - _, err = exec.LookPath("ffmpeg") - if err != nil { - return errors.New("ffmpeg not found on system") - } - - cmd.Start() - //logrus.Infof("Downloading mp3 file to disk %s", file) - stdin.Write(buf.Bytes()) //download file. - - } else { - out, err := os.Create(file) - if err != nil { - logrus.Errorf("Unable to download video file: %v", err) - return err - } - err = apiDownloadVideo(id, out) + stdin, err := cmd.StdinPipe() + if err != nil { return err } + buf := &bytes.Buffer{} + gob.NewEncoder(buf).Encode(decVideo) + _, err = exec.LookPath("ffmpeg") + if err != nil { + return errors.New("ffmpeg not found on system") + } + + cmd.Start() + //logrus.Infof("Downloading mp3 file to disk %s", file) + stdin.Write(buf.Bytes()) //download file. + return nil } //Downloads decoded video stream. -func apiDownloadVideo(url string, out io.Writer) error { - //logrus.Infof("Downloading file stream") - - resp, err := http.Get(videoExtractor + url) +func ApiDownloadVideo(file string, url string, video *RawVideoData) error { + resp, err := http.Get(url) if err != nil { - return fmt.Errorf("requesting stream: %s", err) + log.Printf("Http.Get\nerror: %s\nURL: %s\n", err, url) + return err } defer resp.Body.Close() + video.Vlength = float64(resp.ContentLength) + if resp.StatusCode != 200 { - return fmt.Errorf("reading answer: non 200 status code received: '%s'", err) + log.Printf("Reading Output: status code : '%v'", resp.StatusCode, err) + return errors.New("non 200 status code received") } - length, err := io.Copy(out, resp.Body) + err = os.MkdirAll(filepath.Dir(file), 666) if err != nil { - return fmt.Errorf("saving file: %s (%d bytes copied)", err, length) + return err + } + out, err := os.Create(file) + if err != nil { + return err + } + mw := io.MultiWriter(out, video) + _, err = io.Copy(mw, resp.Body) + if err != nil { + log.Println("Download Error: ", err) + return err } - - //logrus.Infof("Downloaded %d bytes", length) - return nil } diff --git a/api/apidata.go b/api/apidata.go index 055681c..4604fad 100644 --- a/api/apidata.go +++ b/api/apidata.go @@ -7,8 +7,8 @@ package api import ( - "encoding/json" "errors" + "fmt" "io/ioutil" "net/http" "net/url" @@ -25,12 +25,17 @@ const ( videoExtractor = "https://youtube.com/get_video_info?video_id=" ) +type stream map[string]string + //Youtube Downloader Data file. type RawVideoData struct { - Title string `json:"title"` - Author string `json:"author"` - Status string `json:"status"` - URLEncodedFmtStreamMap string `json:"url_encoded_fmt_stream_map"` + Title string `json:"title"` + Author string `json:"author"` + Status string `json:"status"` + URLEncodedFmtStreamMap []stream `json:"url_encoded_fmt_stream_map"` + VideoId string + VideoInfo string + Vlength float64 } //gets the Video ID from youtube url @@ -54,7 +59,9 @@ func APIGetVideoStream(format, id, path string, bitrate uint) (err error) { var decodedVideo []string //decoded video data //Get Video Data stream + video.VideoId = id videoUrl := videoExtractor + id + video.VideoInfo = videoUrl resp, er := http.Get(videoUrl) if er != nil { logrus.Errorf("Error in GET request: %v", er) @@ -72,50 +79,79 @@ func APIGetVideoStream(format, id, path string, bitrate uint) (err error) { return nil } - //Process Video stream - video.URLEncodedFmtStreamMap = output.Get("url_encoded_fmt_stream_map") - video.Author = output.Get("author") - video.Title = output.Get("title") - video.Status = output.Get("status") - - //Decode Video - outputStreams := strings.Split(video.URLEncodedFmtStreamMap, ",") - for cur, raw_data := range outputStreams { - //decoding raw data stream - dec_data, err := url.ParseQuery(raw_data) + status, ok := output["status"] + if !ok { + err = fmt.Errorf("No response status in server") + return err + } + if status[0] == "fail" { + reason, ok := answer["reason"] + if ok { + err = fmt.Errorf("'fail' response status found in the server, reason: '%s'", reason[0]) + } else { + err = errors.New(fmt.Sprint("'fail' response status found in the server, no reason given")) + } + return err + } + if status[0] != "ok" { + err = fmt.Errorf("non-success response status found in the server (status: '%s')", status) + return err + } + + // read the streams map + video.Author = output["author"][0] + video.Title = output["title"][0] + video.URLEncodedFmtStreamMap, ok = output.Get("url_encoded_fmt_stream_map") + if !ok { + err = errors.New(fmt.Sprint("no stream map found in the server")) + return err + } + + // read and decode streams. + streamsList := strings.Split(video.URLEncodedFmtStreamMap[0], ",") + var streams []stream + for streamPos, streamRaw := range streamsList { + streamQry, err := url.ParseQuery(streamRaw) if err != nil { - logrus.Errorf("Error Decoding Video data: %d, %v", cur, err) + logrus.Infof("An error occured while decoding one of the video's streams: stream %d: %s\n", streamPos, err) continue } - - data := map[string]string{ - "quality": dec_data.Get("quality"), - "type": dec_data.Get("type"), - "url": dec_data.Get("url"), - "sig": dec_data.Get("sig"), - "title": video.Title, - "author": video.Author, - "format": dec_data.Get("format"), + var sig string + if _, exist := streamQry["sig"]; exist { + sig = streamQry["sig"][0] } - str, _ := json.Marshal(data) - decodedVideo = append(decodedVideo, string(str)) - //logrus.Infof("\nDecoded %d bytes of %q, in %q format", len(decodedVideo), dec_data.Get("quality"), dec_data.Get("format")) + streams = append(streams, stream{ + "quality": streamQry["quality"][0], + "type": streamQry["type"][0], + "url": streamQry["url"][0], + "sig": sig, + "title": output["title"][0], + "author": output["author"][0], + }) + logrus.Infof("Stream found: quality '%s', format '%s'", streamQry["quality"][0], streamQry["type"][0]) } - //Convert and Download video data + video.URLEncodedFmtStreamMap = streams + //Download Video stream to file + vstream := streams[0] + url := vstream["url"] + "&signature" + vstream["sig"] + logrus.Infof("Downloading file to %s", file) + //create output file name and set path properly. file := path + video.Title + video.Author if format == "mp3" { file = file + ".mp3" + err = ApiConvertVideo(file, id, format, bitrate, decodedVideo) + if err != nil { + logrus.Errorf("Error downloading audio: %v", err) + } } else { //defaults to flv format for video files.) file = file + ".flv" - } - - err = APIConvertVideo(file, id, format, bitrate, decodedVideo) - if err != nil { - logrus.Errorf("Error downloading video: %v", err) + if err := ApiDownloadVideo(file, url, video); err != nil { + logrus.Errorf("Error downloading video: %v", v) + } } return nil diff --git a/ytd.go b/ytd.go index 5d213b5..11b8f14 100644 --- a/ytd.go +++ b/ytd.go @@ -67,7 +67,7 @@ func main() { defer pprof.StopCPUProfile() } runtime.SetBlockProfileRate(20) - + if path == "" { path, _ = os.Getwd() }