Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew-Morozko committed Nov 2, 2019
1 parent a4d8dbb commit 132ccd4
Show file tree
Hide file tree
Showing 9 changed files with 919 additions and 0 deletions.
28 changes: 28 additions & 0 deletions README.md
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
```
128 changes: 128 additions & 0 deletions auth.go
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
}
11 changes: 11 additions & 0 deletions go.mod
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
)
38 changes: 38 additions & 0 deletions go.sum
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=
81 changes: 81 additions & 0 deletions httptools.go
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
}
Loading

0 comments on commit 132ccd4

Please sign in to comment.