-
Notifications
You must be signed in to change notification settings - Fork 1
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
1 parent
a4d8dbb
commit 132ccd4
Showing
9 changed files
with
919 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,28 @@ | ||
# Cloudy Uploader | ||
*Unofficial CLI client for [Overcast](https://overcast.fm/)® Premium upload feature* | ||
|
||
It's just a wrapper around upload a form at [https://overcast.fm/uploads](https://overcast.fm/uploads). | ||
|
||
I wanted to automate the uploading of my mp3s to my favorite podcast player and decided to share it here. | ||
Sorry for the code quality, this is also my first go project. It works, whatever;) Issues and pull requests are welcome. | ||
|
||
This project shouldn't cause any trouble, but I (of course) will shut it down if Marco isn't ok with it. | ||
|
||
``` | ||
Usage: cloudyuploader [--parallel-uploads PARALLEL-UPLOADS] [--login LOGIN] | ||
[--password PASSWORD] [--store-cookie] | ||
[--store-password] [--silent] FILE [FILE ...] | ||
Positional arguments: | ||
FILE files to be uploaded | ||
Options: | ||
--parallel-uploads PARALLEL-UPLOADS, -j PARALLEL-UPLOADS | ||
maximum number of concurrent upload jobs [default: 4] | ||
--login LOGIN email for Overcast account | ||
--password PASSWORD password for Overcast account | ||
--store-cookie store cookie to skip authorization [default: true] | ||
--store-password store (unencrypted) email/password [default: false] | ||
--silent, -s disable user interaction | ||
--help, -h display this help and exit | ||
``` |
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,128 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"os" | ||
"strings" | ||
"syscall" | ||
|
||
"github.com/pkg/errors" | ||
"golang.org/x/crypto/ssh/terminal" | ||
) | ||
|
||
type Creds struct { | ||
Email string | ||
Password string | ||
} | ||
|
||
func login(creds *Creds) (err error) { | ||
postdata := url.Values{ | ||
"then": {"uploads"}, | ||
"email": {creds.Email}, | ||
"password": {creds.Password}, | ||
} | ||
|
||
resp, err := client.PostForm("https://overcast.fm/login", postdata) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != 200 { | ||
return errors.Errorf("Unexpected HTTP response on login: %d", resp.StatusCode) | ||
} | ||
|
||
if strings.HasSuffix(resp.Request.URL.Path, "login") { | ||
return errors.New("Failed to login: wrong password") | ||
} | ||
|
||
if !strings.HasSuffix(resp.Request.URL.Path, "uploads") { | ||
return errors.Errorf("Failed to login: Request in unknown place %s", resp.Request.URL.String()) | ||
} | ||
|
||
return parseUploadsPage(resp.Body) | ||
} | ||
|
||
func requestPassword() (err error) { | ||
creds := &Creds{} | ||
creds.Email, creds.Password, err = inputCreds() | ||
if err != nil { | ||
return | ||
} | ||
|
||
err = login(creds) | ||
if err != nil { | ||
return | ||
} | ||
|
||
if args.StorePassword { | ||
config.Creds = creds | ||
} | ||
|
||
return | ||
} | ||
|
||
func auth() (err error) { | ||
if args.StorePassword && !args.Silent { | ||
return requestPassword() | ||
} | ||
|
||
if len(config.Cookies) != 0 { | ||
// attempting to access with saved cookies | ||
var resp *http.Response | ||
resp, err = client.Get("https://overcast.fm/uploads") | ||
if err == nil { | ||
defer resp.Body.Close() | ||
|
||
if strings.HasSuffix(resp.Request.URL.Path, "uploads") { | ||
// Wasn't redirected to /login, so cookies are valid | ||
return parseUploadsPage(resp.Body) | ||
} | ||
} | ||
printf("[WARN] Failed to log in with stored cookies\n") | ||
} | ||
|
||
if config.Creds != nil { | ||
// Got stored credentials, using them to login | ||
err = login(config.Creds) | ||
if err != nil { | ||
printf("[WARN] Failed to log in with stored credentials (%s)\n", err) | ||
} | ||
} | ||
if !args.Silent { | ||
return requestPassword() | ||
} | ||
return errors.New("Failed to login") | ||
} | ||
|
||
func inputCreds() (username, password string, err error) { | ||
reader := bufio.NewReader(os.Stdin) | ||
if args.Login != "" { | ||
fmt.Print("Email: ") | ||
username, err = reader.ReadString('\n') | ||
if err != nil { | ||
return | ||
} | ||
} else { | ||
username = args.Login | ||
} | ||
if args.Password != "" { | ||
fmt.Print("Password: ") | ||
var bytePassword []byte | ||
bytePassword, err = terminal.ReadPassword(int(syscall.Stdin)) | ||
if err != nil { | ||
err = errors.Wrap(err, "Failed to enter the password") | ||
return | ||
} | ||
fmt.Print("\n") | ||
password = string(bytePassword) | ||
} else { | ||
password = args.Password | ||
} | ||
|
||
return | ||
} |
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,11 @@ | ||
module github.com/Andrew-Morozko/cloudy-uploader | ||
|
||
require ( | ||
github.com/PuerkitoBio/goquery v1.5.0 | ||
github.com/alexflint/go-arg v1.2.0 | ||
github.com/pkg/errors v0.8.1 | ||
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 | ||
github.com/vbauerster/mpb/v4 v4.11.0 | ||
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf | ||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e | ||
) |
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,38 @@ | ||
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk= | ||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= | ||
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= | ||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= | ||
github.com/alexflint/go-arg v1.2.0 h1:TOFkN8/Cn1VvbHhsCuYq+P6ol3P5FAmjKIqsk7D2U/Q= | ||
github.com/alexflint/go-arg v1.2.0/go.mod h1:3Rj4baqzWaGGmZA2+bVTV8zQOZEjBQAPBnL5xLT+ftY= | ||
github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70= | ||
github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= | ||
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o= | ||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | ||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= | ||
github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= | ||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= | ||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||
github.com/vbauerster/mpb/v4 v4.11.0 h1:QdSmlc4dUap9XugHWx84yi7ABstYHW1rC5slzDwfXnw= | ||
github.com/vbauerster/mpb/v4 v4.11.0/go.mod h1:2d50DYyCBW+8eE9ZgdMCDEB+7S+ELz4YenPtQ+nKOts= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss= | ||
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= | ||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= | ||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= | ||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339 h1:zSqWKgm/o7HAnlAzBQ+aetp9fpuyytsXnKA8eiLHYQM= | ||
golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
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,81 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/tls" | ||
"net/http" | ||
"net/http/cookiejar" | ||
"net/url" | ||
) | ||
|
||
type Config struct { | ||
Creds *Creds | ||
Cookies []*BasicCookie | ||
} | ||
|
||
type BasicCookie struct { | ||
Name string | ||
Value string | ||
} | ||
|
||
func (c *Config) SetCookies(cookies []*http.Cookie) { | ||
c.Cookies = make([]*BasicCookie, len(cookies)) | ||
for i, cookie := range cookies { | ||
c.Cookies[i] = &BasicCookie{ | ||
Name: cookie.Name, | ||
Value: cookie.Value, | ||
} | ||
} | ||
} | ||
|
||
func (c *Config) GetCookies() []*http.Cookie { | ||
res := make([]*http.Cookie, len(c.Cookies)) | ||
|
||
for i, cookie := range c.Cookies { | ||
res[i] = &http.Cookie{ | ||
Name: cookie.Name, | ||
Value: cookie.Value, | ||
} | ||
} | ||
|
||
return res | ||
} | ||
|
||
type MyTransport struct { | ||
*http.Transport | ||
Agent string | ||
} | ||
|
||
func (mt *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) { | ||
r.Header.Set("User-Agent", mt.Agent) | ||
return mt.Transport.RoundTrip(r) | ||
} | ||
|
||
var client = NewHTTPClient() | ||
|
||
func NewHTTPClient() *http.Client { | ||
transport := &MyTransport{ | ||
Transport: http.DefaultTransport.(*http.Transport), | ||
Agent: userAgent, | ||
} | ||
|
||
if debug { | ||
proxyURL, err := url.Parse("http://10.10.10.10:8080") | ||
if err != nil { | ||
panic(err) | ||
} | ||
transport.Proxy = http.ProxyURL(proxyURL) | ||
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} | ||
} | ||
|
||
cookies, err := cookiejar.New(nil) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
res := &http.Client{ | ||
Transport: transport, | ||
Jar: cookies, | ||
} | ||
|
||
return res | ||
} |
Oops, something went wrong.