Skip to content

Instantly share code, notes, and snippets.

@kidoman
Created November 6, 2013 00:06
Show Gist options
  • Save kidoman/7328620 to your computer and use it in GitHub Desktop.
Save kidoman/7328620 to your computer and use it in GitHub Desktop.
A snatcher of sorts :)
package main
import (
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path"
"regexp"
"time"
)
const (
urlFmt = "http://embeds.uploadcrazy.net/ucc.php?file=ts%v%02v"
rexp = "http://[a-z0-9]+\\.uploadcrazy.net.+&e=[0-9]+"
maxTries = 5
waitTime = 10
)
var (
re = regexp.MustCompile(rexp)
)
var (
folder = flag.String("folder", ".", "where to put the downloaded files")
show = flag.String("show", "ts", "show short string")
season = flag.Int("s", 1, "season")
estart = flag.Int("es", 1, "which episode to start downloading")
eend = flag.Int("ee", 1, "which episode to end downloading")
)
func main() {
flag.Parse()
if *estart > *eend {
log.Fatal("End episode has to be > start episode")
}
for _, e := range listEpisodes() {
if e.done() {
log.Printf("Skipping episode %v as it is already done (file %v)", e, e.outputFilename())
continue
}
if err := e.download(); err != nil {
log.Fatalf("Could not download episode %v", e)
}
}
}
func listEpisodes() []episode {
es := make([]episode, 0)
for i := *estart; i <= *eend; i++ {
es = append(es, episode(i))
}
return es
}
type episode int
func (e episode) String() string {
return fmt.Sprintf("%v", int(e))
}
func (e episode) done() bool {
o := e.outputFilename()
fp := path.Join(*folder, o)
if _, err := os.Stat(fp); os.IsNotExist(err) {
return false
}
return true
}
func (e episode) download() error {
tries := 0
var durl string
for {
var err error
durl, err = e.downloadUrl()
if err != nil {
log.Fatal(err)
}
log.Printf("Validating url %q", durl)
if validateDownloadUrl(durl) {
log.Printf("Validated %q successfully", durl)
break
}
log.Printf("Could not validate %q", durl)
tries++
if tries > maxTries {
log.Printf("Failed at getting a valid download URL for episode %v", e)
return nil
}
}
o := e.outputFilename()
cmd := exec.Command("axel", "-o", o, "-a", durl)
cmd.Dir = *folder
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Printf("About to start downloading %q", durl)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
log.Printf("Completed downloading from %q", durl)
return nil
}
func validateDownloadUrl(url string) bool {
res := make(chan bool)
go func() {
resp, err := http.Head(url)
if err != nil {
res <- false
}
if resp.StatusCode != 200 {
res <- false
}
res <- true
}()
timeout := time.After(waitTime * time.Second)
select {
case result := <-res:
return result
case <-timeout:
return false
}
}
func (e episode) outputFilename() string {
return fmt.Sprintf("%v.s%02ve%02v.mp4", *show, *season, e)
}
func (e episode) downloadUrl() (string, error) {
url := e.url()
log.Printf("Scrapping page %q", url)
res, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
return "", fmt.Errorf("Invalid intermediate page (%v), cannot scrap", url)
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
durl := re.Find(body)
if durl == nil {
return "", errors.New("Cannot find download url")
}
log.Printf("Found url %q for episode %v", durl, e)
return string(durl), nil
}
func (e episode) url() string {
return fmt.Sprintf(urlFmt, *season, e)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment