-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit cab4627
Showing
6 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
language: go | ||
install: | ||
- go get github.com/tanaikech/goodls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
The MIT License (MIT) | ||
Copyright (c) 2018 Kanshi TANAIKE | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
goodls | ||
===== | ||
|
||
<a name="TOP"></a> | ||
[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENCE) | ||
|
||
<a name="Overview"></a> | ||
# Overview | ||
This is a CLI tool to download shared files from Google Drive. | ||
|
||
# Demo | ||
![](images/demo.gif) | ||
|
||
The image used for this demonstration was created by [k3-studio](https://k3-studio.deviantart.com/art/Chromatic-spiral-416032436) | ||
|
||
<a name="Description"></a> | ||
# Description | ||
We have already known that the shared files on Google Drive can be downloaded without the authorization. But when the size of file becomes large (about 40MB), it requires a little ingenuity to download the file. It requires to access 2 times to Google Drive. At 1st access, it retrieves a cookie and a code for downloading. At 2nd access, the file is downloaded using the cookie and code. I created this process as a CLI tool. This tool has the following features. | ||
|
||
- Use suitable process for size and type of file. | ||
- Retrieve filename and mimetype from response header. | ||
- Can download all shared files except for project files. | ||
|
||
# How to Install | ||
Download an executable file of goodls from [the release page](https://github.com/tanaikech/goodls/releases) and import to a directory with path. | ||
|
||
or | ||
|
||
Use go get. | ||
|
||
~~~bash | ||
$ go get -u github.com/tanaikech/goodls | ||
~~~ | ||
|
||
# Usage | ||
You can use this just after you download or install goodls. You are not required to do like OAuth2 process. | ||
|
||
~~~bash | ||
$ goodls -u [URL of shared file on Google Drive] | ||
~~~ | ||
|
||
- **Help** | ||
- ``$ goodls --help`` | ||
- **Options** | ||
- ``-e`` | ||
- Extension of output file. This is for only Google Docs (Spreadsheet, Document, Presentation). Default is ``pdf``. | ||
- Sample : | ||
- ``$ goodls -u https://docs.google.com/document/d/#####/edit?usp=sharing -e txt`` | ||
- ``-f`` | ||
- Filename of file which is output. When this was not used, the original filename on Google Drive is used. | ||
- Sample : | ||
- ``$ goodls -u https://docs.google.com/document/d/#####/edit?usp=sharing -e txt -f sample.txt`` | ||
- **URL is like below.** | ||
- In the case of Google Docs (Spreadsheet, Document, Slides) | ||
- ``https://docs.google.com/spreadsheets/d/#####/edit?usp=sharing`` | ||
- ``https://docs.google.com/document/d/#####/edit?usp=sharing`` | ||
- ``https://docs.google.com/presentation/d/#####/edit?usp=sharing`` | ||
- In the case of except for Google Docs | ||
- ``https://drive.google.com/file/d/#####/view?usp=sharing`` | ||
|
||
**When you download shared files from Google Drive, please confirm whether the files are shared.** | ||
|
||
# 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. | ||
|
||
----- | ||
|
||
<a name="Licence"></a> | ||
# Licence | ||
[MIT](LICENCE) | ||
|
||
<a name="Author"></a> | ||
# Author | ||
[Tanaike](https://tanaikech.github.io/about/) | ||
|
||
If you have any questions and commissions for me, feel free to tell me. | ||
|
||
<a name="Update_History"></a> | ||
# Update History | ||
* v1.0.0 (January 10, 2018) | ||
|
||
1. Initial release. | ||
|
||
|
||
[TOP](#TOP) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
Package main (doc.go) : | ||
This is a CLI tool to download shared files from Google Drive. | ||
We have already known that the shared files on Google Drive can be downloaded without the authorization. But when the size of file becomes large (about 40MB), it requires a little ingenuity to download the file. It requires to access 2 times to Google Drive. At 1st access, it retrieves a cookie and a code for downloading. At 2nd access, the file is downloaded using the cookie and code. I created this process as a CLI tool. This tool has the following features. | ||
- Use suitable process for size and type of file. | ||
- Retrieve filename and mimetype from response header. | ||
- Can download all shared files except for project files. | ||
--------------------------------------------------------------- | ||
# How to Install | ||
Download an executable file of goodls from https://github.com/tanaikech/goodls/releases | ||
or | ||
Use go get. | ||
$ go get -u github.com/tanaikech/goodls | ||
# Usage | ||
You can use this just after you download or install goodls. You are not required to do like OAuth2 process. | ||
$ goodls -u [URL of shared file on Google Drive] | ||
--------------------------------------------------------------- | ||
*/ | ||
package main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
// Package main (goodls.go) : | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
"mime" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/urfave/cli" | ||
) | ||
|
||
const ( | ||
appname = "goodls" | ||
anyurl = "https://drive.google.com/uc?export=download" | ||
docutl = "https://docs.google.com/" | ||
) | ||
|
||
// para : Structure for each parameter | ||
type para struct { | ||
Client *http.Client | ||
Code string | ||
ContentType string | ||
Ext string | ||
Filename string | ||
Id string | ||
Kind string | ||
Url string | ||
WorkDir string | ||
} | ||
|
||
// saveFile : Save retrieved data as a file. | ||
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 | ||
} | ||
body, err := ioutil.ReadAll(res.Body) | ||
if err != nil { | ||
return err | ||
} | ||
ioutil.WriteFile(filepath.Join(p.WorkDir, p.Filename), body, 0777) | ||
fmt.Printf("{\"Filename\": \"%s\", \"Type\": \"%s\", \"MimeType\": \"%s\", \"FileSize\": %d}\n", p.Filename, p.Kind, p.ContentType, len(body)) | ||
defer res.Body.Close() | ||
return nil | ||
} | ||
|
||
// getFilename : Retrieve filename from header. | ||
func (p *para) getFilename(s *http.Response) error { | ||
if len(s.Header["Content-Disposition"]) > 0 { | ||
_, para, err := mime.ParseMediaType(s.Header["Content-Disposition"][0]) | ||
if err != nil { | ||
return err | ||
} | ||
if p.Filename == "" { | ||
p.Filename = para["filename"] | ||
} | ||
} else { | ||
return errors.New(fmt.Sprintf("File ID [ %s ] is not shared, while the file is existing.\n", p.Id)) | ||
} | ||
return nil | ||
} | ||
|
||
// downloadLargeFile : When a large size of file is downloaded, this method is used. | ||
func (p *para) downloadLargeFile() error { | ||
fmt.Println("Now downloading.") | ||
res, err := p.fetch(p.Url + "&confirm=" + p.Code) | ||
if err != nil { | ||
return err | ||
} | ||
if res.StatusCode != 200 && p.Kind != "file" { | ||
return errors.New(fmt.Sprintf("Error: This error occurs when it downloads a large file of Google Docs.\nMessage: %+v", res)) | ||
} | ||
p.saveFile(res) | ||
return nil | ||
} | ||
|
||
// 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 | ||
} | ||
} | ||
} | ||
|
||
// fetch : Fetch data from Google Drive | ||
func (p *para) fetch(url string) (*http.Response, error) { | ||
req, err := http.NewRequest("get", url, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
res, err := p.Client.Do(req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return res, nil | ||
} | ||
|
||
// checkUrl : Parse inputted URL. | ||
func (p *para) checkUrl(s string) error { | ||
r := regexp.MustCompile(`google\.com\/(\w.+)\/d\/(\w.+)\/`) | ||
if r.MatchString(s) { | ||
res := r.FindAllStringSubmatch(s, -1) | ||
p.Kind = res[0][1] | ||
p.Id = res[0][2] | ||
if p.Kind == "file" { | ||
p.Url = anyurl + "&id=" + p.Id | ||
} else { | ||
if p.Kind == "presentation" { | ||
p.Url = docutl + p.Kind + "/d/" + p.Id + "/export/" + p.Ext | ||
} else { | ||
p.Url = docutl + p.Kind + "/d/" + p.Id + "/export?format=" + p.Ext | ||
} | ||
} | ||
} else { | ||
return errors.New("Error: URL is wrong.") | ||
} | ||
return nil | ||
} | ||
|
||
// download : Main method of download. | ||
func (p *para) download(url string) error { | ||
var err error | ||
err = p.checkUrl(url) | ||
if err != nil { | ||
return err | ||
} | ||
jar, err := cookiejar.New(nil) | ||
if err != nil { | ||
return err | ||
} | ||
p.Client = &http.Client{Jar: jar} | ||
res, err := p.fetch(p.Url) | ||
if err != nil { | ||
return err | ||
} | ||
if res.StatusCode == 200 { | ||
if len(res.Header["Set-Cookie"]) == 0 { | ||
return p.saveFile(res) | ||
} else { | ||
p.checkCookie(res.Header["Set-Cookie"][0]) | ||
if len(p.Code) == 0 && p.Kind == "file" { | ||
return errors.New(fmt.Sprintf("File ID [ %s ] is not shared, while the file is existing.\n", p.Id)) | ||
} else if len(p.Code) == 0 && p.Kind != "file" { | ||
return p.saveFile(res) | ||
} else { | ||
return p.downloadLargeFile() | ||
} | ||
} | ||
} else { | ||
return errors.New(fmt.Sprintf("File ID [ %s ] cannot be downloaded as [ %s ].\n", p.Id, p.Ext)) | ||
} | ||
return nil | ||
} | ||
|
||
// handler : Initialize of "para". | ||
func handler(c *cli.Context) { | ||
if c.String("url") == "" { | ||
createHelp().Run(os.Args) | ||
return | ||
} | ||
var err error | ||
workdir, err := filepath.Abs(".") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
p := ¶{ | ||
Ext: c.String("extension"), | ||
Filename: c.String("filename"), | ||
WorkDir: workdir, | ||
} | ||
err = p.download(c.String("url")) | ||
if err != nil { | ||
fmt.Printf("Error: %v\n", err) | ||
os.Exit(1) | ||
} | ||
return | ||
} | ||
|
||
// createHelp : Create help document. | ||
func createHelp() *cli.App { | ||
a := cli.NewApp() | ||
a.Name = appname | ||
a.Author = "tanaike [ https://github.com/tanaikech/" + appname + " ] " | ||
a.Email = "[email protected]" | ||
a.Usage = "Download shared files on Google Drive." | ||
a.Version = "1.0.0" | ||
a.Flags = []cli.Flag{ | ||
cli.StringFlag{ | ||
Name: "url, u", | ||
Usage: "URL of shared file on Google Drive. This is a required parameter.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "extension, e", | ||
Usage: "Extension of output file. This is for only Google Docs (Spreadsheet, Document, Presentation).", | ||
Value: "pdf", | ||
}, | ||
cli.StringFlag{ | ||
Name: "filename, f", | ||
Usage: "Filename of file which is output. When this was not used, the original filename on Google Drive is used.", | ||
}, | ||
} | ||
return a | ||
} | ||
|
||
// main : Main of this script | ||
func main() { | ||
a := createHelp() | ||
a.Action = handler | ||
a.Run(os.Args) | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.