diff --git a/README.md b/README.md index 2083fb2..1c5f9ad 100644 --- a/README.md +++ b/README.md @@ -233,10 +233,12 @@ $ goodls -u https://drive.google.com/drive/folders/abcdefg?usp=sharing -key htjk # Q&A - I want to download **shared projects** from user's Google Drive. + - You can download **shared projects** using [ggsrun](https://github.com/tanaikech/ggsrun). - ggsrun can also download **shared files** from other user's Google Drive using Drive API which needs the access token. - I want to download all files including the standalone projects from the shared folder and own folder. + - You can achieve it using [ggsrun](https://github.com/tanaikech/ggsrun). - I want to use this with "Dockerfile build". @@ -347,4 +349,10 @@ If you have any questions and commissions for me, feel free to tell me. 1. As the URL for downloading the files, `webContentLink` was added. So from this version, the URL of `https://drive.google.com/uc?export=download&id=###` got to be able to be used. This is the request from [this thread](https://github.com/tanaikech/goodls/issues/13). + + +- v1.2.8 (February 17, 2022) + + 1. Recently, it seems that the specification of this flow has been changed. So I updated goodls for reflecting this upload. So, the usage of goodls is not changed. + [TOP](#top) diff --git a/goodls.go b/goodls.go index 9e5b0e1..0a3c479 100644 --- a/goodls.go +++ b/goodls.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "io/ioutil" "mime" "net/http" "net/http/cookiejar" @@ -80,7 +81,6 @@ func (c *chunks) Read(dat []byte) (int, error) { func (p *para) saveFile(res *http.Response) error { var err error p.ContentType = res.Header["Content-Type"][0] - err = p.getFilename(res) if err = p.getFilename(res); err != nil { return err } @@ -134,7 +134,13 @@ func (p *para) getFilename(s *http.Response) error { p.Filename = para["filename"] } } else { - return fmt.Errorf("file ID [ %s ] is not shared, while the file is existing", p.ID) + body, _ := ioutil.ReadAll(s.Body) + rFilename := regexp.MustCompile(`([\w\s\S]+?)<\/a>`) + matches := rFilename.FindAllStringSubmatch(string(body), -1) + if len(matches) == 0 { + return fmt.Errorf("file ID [ %s ] cannot be downloaded", p.ID) + } + p.Filename = matches[0][1] } return nil } @@ -159,18 +165,16 @@ func (p *para) downloadLargeFile() error { return p.saveFile(res) } -// checkCookie : When a large size of file is downloaded, a code for downloading is retrieved at here. -func (p *para) checkCookie(rawCookies string) { - header := http.Header{} - header.Add("Cookie", rawCookies) - request := http.Request{Header: header} - for _, e := range request.Cookies() { - if strings.Contains(e.Name, "download_warning_") { - cookie, _ := request.Cookie(e.Name) - p.Code = cookie.Value - break - } +// getDownloadCode : When a large size of file is downloaded, a code for downloading is retrieved at here. +func (p *para) getDownloadCode(res *http.Response) error { + body, _ := ioutil.ReadAll(res.Body) + rFilename := regexp.MustCompile(`confirm\=([\w\s\S]+?)"`) + matches := rFilename.FindAllStringSubmatch(string(body), -1) + if len(matches) == 0 { + return fmt.Errorf("file ID [ %s ] cannot be downloaded", p.ID) } + p.Code = matches[0][1] + return nil } // fetch : Fetch data from Google Drive @@ -268,7 +272,7 @@ func (p *para) download(url string) error { if p.APIKey != "" && p.ShowFileInf { return nil } else if p.APIKey == "" && p.ShowFileInf { - return errors.New("When you want to use the option '--fileinf', please use API key") + return errors.New("when you want to use the option '--fileinf', please use API key") } else if p.APIKey != "" && p.DlFolder { return nil } @@ -281,11 +285,17 @@ func (p *para) download(url string) error { if err != nil { return err } + if res.StatusCode == 200 { - if len(res.Header["Set-Cookie"]) == 0 { + // For a small file. + // After February, 2022, "Content-Disposition" is not included in the response header for a large file. + _, chk := res.Header["Content-Disposition"] + if chk { return p.saveFile(res) } - p.checkCookie(res.Header["Set-Cookie"][0]) + if err := p.getDownloadCode(res); err != nil { + return err + } if len(p.Code) == 0 && p.Kind == "file" { return fmt.Errorf("file ID [ %s ] is not shared, while the file is existing", p.ID) } else if len(p.Code) == 0 && p.Kind != "file" { @@ -360,7 +370,7 @@ func handler(c *cli.Context) error { return scanner.Err() } if len(urls) == 0 { - return fmt.Errorf("No URL data. Please check help\n\n $ %s --help", appname) + return fmt.Errorf("no URL data. Please check help\n\n $ %s --help", appname) } for _, url := range urls { err = p.download(url) @@ -381,7 +391,7 @@ func createHelp() *cli.App { {Name: "tanaike [ https://github.com/tanaikech/" + appname + " ] ", Email: "tanaike@hotmail.com"}, } a.UsageText = "Download shared files on Google Drive." - a.Version = "1.2.7" + a.Version = "1.2.8" a.Flags = []cli.Flag{ &cli.StringFlag{ Name: "url, u", diff --git a/resumabledownload.go b/resumabledownload.go index d8ea2b7..14324dc 100644 --- a/resumabledownload.go +++ b/resumabledownload.go @@ -187,7 +187,7 @@ func (v *valResumableDownload) chkResumeFile() (bool, bool, error) { if fs == v.DownloadFile.Size { return false, true, nil } else if fs > v.DownloadFile.Size { - return false, false, fmt.Errorf("Size of download file is larger than that of local file. Please confirm the file and URL. FileName is %s. Download URL is %s", v.Filename, v.URL) + return false, false, fmt.Errorf("size of download file is larger than that of local file. Please confirm the file and URL. FileName is %s. Download URL is %s", v.Filename, v.URL) } v.Start = fs v.End = func() int64 { @@ -289,7 +289,7 @@ func (v *valResumableDownload) getStatusMsg(fc, end bool) string { } return getMsg(setIndent(st, 0), " : ") default: - return fmt.Sprintf("Unknown error.") + return fmt.Sprintf("unknown error") } } @@ -305,7 +305,7 @@ func (p *para) resumableDownload() error { return err } if strings.Contains(v.DownloadFile.MimeType, "application/vnd.google-apps") { - return fmt.Errorf("Google Docs cannot be resumable downloaded") + return fmt.Errorf("a Google Docs file cannot be resumable downloaded") } fc, end, err := v.chkResumeFile() if err != nil {