<file_sep>package providers

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"golang.org/x/net/html"
)

var header = map[string]string{
	"Content-Type": "application/x-www-form-urlencoded",
	"User-Agent":   "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)",
}

var params = map[string]string{
	"name":        "",
	"search":      "",
	"description": "",
	"file":        "",
	"user":        "",
	"cat":         "404",
	//"subcat":      "409", // mangas
	"subcat": "406", // BD
	"submit": "Recherche",
	"order":  "added",
	"type":   "desc",
}

var cookies = []http.Cookie{
	{Name: "uid", Value: "7531997"},
	{Name: "pass", Value: "<PASSWORD>"},
	{Name: "authKey", Value: "<KEY>"},
}

// Init the provider execution
func Init(search string) *url.URL {

	params["name"] = search
	params["search"] = "@name " + search

	// Build URL
	var urlp *url.URL
	urlp, err := url.Parse("http://www.t411.in")
	if err != nil {
		panic("Unable to parse 'T411' provider URL")
	}
	urlp.Path += "/torrents/search/"

	// Add parameters
	parameters := url.Values{}
	for k, p := range params {
		parameters.Add(k, p)
	}
	urlp.RawQuery = parameters.Encode()

	fmt.Printf("Encoded URL is %q\n", urlp.String())

	return urlp
}

// Search execute search to the provider
func Search(urlp *url.URL) io.Reader {
	fmt.Printf("Execute %q\n", urlp.String())

	// Create HTTP request
	client := http.Client{}
	request, err := http.NewRequest("GET", urlp.String(), nil)
	if err != nil {
		log.Fatal(err)
	}
	// Add headers
	for k, h := range header {
		request.Header.Add(k, h)
	}
	// Add Cookies
	for _, c := range cookies {
		request.AddCookie(&c)
	}
	// Execute request
	resp, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}

	//defer resp.Body.Close()
	//fmt.Println("response Status:", resp.Status)
	//fmt.Println("response Headers:", resp.Header)

	return resp.Body
}

// ParseSearch body of the provider search request
func ParseSearch(body io.Reader) *[]Result {
	var results = make([]Result, 0)

	doc, err := html.Parse(body)
	if err != nil {
		log.Fatal(err)
	}
	var f func(*html.Node)
	f = func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "a" {
			var found = false
			var result = Result{}
			for _, a := range n.Attr {

				if a.Key == "href" && strings.Contains(a.Val, "www.t411.in/torrents") {
					result.DetailURL = a.Val
					found = true
				}
				if a.Key == "title" && found {
					result.Name = strings.TrimSpace(a.Val)
					results = append(results, result)
				}

			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)
	return &results
}

// AnalyzeSearch make an string analysis to identify results
func AnalyzeSearch(results []Result, typeSearch string, number int) []Result {
	var newResults = make([]Result, 0)
	for i := range results {
		result := &results[i]
		for _, analyzer := range Analyzers {
			analyzer.analyze(result)
			if (typeSearch == "volume" && result.IsVolume) || (typeSearch == "chapter" && result.IsChapter) {
				newResults = append(newResults, *result)
			}
		}
	}
	return newResults
}

// GetDetails execute search to get details
func GetDetails(urlp *url.URL) io.Reader {
	fmt.Printf("Execute %q\n", urlp.String())

	// Create HTTP request
	client := http.Client{}
	request, err := http.NewRequest("GET", urlp.String(), nil)
	if err != nil {
		log.Fatal(err)
	}
	// Add headers
	for k, h := range header {
		request.Header.Add(k, h)
	}
	// Add Cookies
	for _, c := range cookies {
		request.AddCookie(&c)
	}
	// Execute request
	resp, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}

	//defer resp.Body.Close()
	//fmt.Println("response Status:", resp.Status)
	//fmt.Println("response Headers:", resp.Header)

	return resp.Body
}

// ParseDetails body of the provider search request
func ParseDetails(body io.Reader) string {
	doc, err := html.Parse(body)

	if err != nil {
		log.Fatal(err)
	}
	var f func(*html.Node) string
	f = func(n *html.Node) string {
		if n.Type == html.ElementNode && n.Data == "a" {
			for _, a := range n.Attr {
				if a.Key == "href" && strings.Contains(a.Val, "/torrents/download/") {
					return a.Val
				}
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			var val = f(c)
			if val != "" {
				return val
			}
		}
		return ""
	}
	return f(doc)
}

// DownloadTorrentFile execute search to get details
func DownloadTorrentFile(urlp *url.URL, dest string) error {
	fmt.Printf("Download Torrent File %q\n", urlp.String())

	// Create HTTP request
	timeout := time.Duration(5 * time.Second)
	client := http.Client{
		Timeout: timeout,
	}
	request, err := http.NewRequest("GET", urlp.String(), nil)
	if err != nil {
		return err
	}
	// Add headers
	for k, h := range header {
		request.Header.Add(k, h)
	}
	// Add Cookies
	for _, c := range cookies {
		request.AddCookie(&c)
	}
	// Execute request
	resp, err := client.Do(request)
	if err != nil {
		return err
	}

	//fmt.Println("response Status:", resp.Status)
	//fmt.Println("response Headers:", resp.Header)

	var filename = resp.Header.Get("Content-Disposition")
	filename = strings.Replace(filename, "attachment; filename=", "", 1)
	filename = strings.Replace(filename, "\"", "", 2)

	defer resp.Body.Close()
	d, err := ioutil.ReadAll(resp.Body)

	if writeFile(dest+filename, d) == nil {
		fmt.Printf("saved %s as %s\n", urlp.String(), dest+filename)
	}

	return nil
}

func writeFile(dst string, d []byte) error {
	err := ioutil.WriteFile(dst, d, 0444)
	if err != nil {
		log.Fatal(err)
	}
	return err
}
<file_sep>package providers

import "regexp"

// Analyzers available analyzers
var Analyzers = []Analyzer{
	VolumeAnalyzer{},
}

// Analyzer Analyzer interface
type Analyzer interface {
	analyze(Analyzed *Result) *Result
}

// VolumeAnalyzer Analyze volumes
type VolumeAnalyzer struct {
}

var volumesWords = "(Volume|Volumes|volume|volumes|Tome|Tomes|tome|tomes|TOME|TOMES|T[0-9]+|V[0-9]+|Vol[0-9]+|vol[0-9]+)"

func (v VolumeAnalyzer) analyze(analyzed *Result) *Result {
	matchVolume, _ := regexp.MatchString(volumesWords, analyzed.Name)
	if matchVolume {
		analyzed.IsVolume = true
		re, _ := regexp.Compile(`([0-9]+)`) // want to know what is in front of 'at'
		res := re.FindAllStringSubmatch(analyzed.Name, -1)
		var elements = make([]string, 0)
		for _, r := range res {
			elements = append(elements, r[0])
		}
		analyzed.RefNumbers = elements
	} else {
		analyzed.IsChapter = true
		re, _ := regexp.Compile(`([0-9]+)`) // want to know what is in front of 'at'
		res := re.FindAllStringSubmatch(analyzed.Name, -1)
		var elements = make([]string, 0)
		for _, r := range res {
			elements = append(elements, r[0])
		}
		analyzed.RefNumbers = elements
	}
	return analyzed
}
<file_sep># gobookie

GoBookie
<file_sep>package main

import (
	"fmt"
	"regexp"
)

func main() {

	re, _ := regexp.Compile(`(Volume|Tome|volume|tome|T[0-9]+|V[0-9]+|Vol[0-9]+|vol[0-9]+)`)
	r := re.FindStringSubmatch("[MFT] One Piece Scan T01-65 fr")
	fmt.Printf("%s\n", r)

	re, _ = regexp.Compile(`([0-9]+)`) // want to know what is in front of 'at'
	res := re.FindAllStringSubmatch("[MFT] One Piece Scan T01-T65 fr", -1)
	fmt.Printf("%v\n", res)

	matchVolume, _ := regexp.MatchString(`(Volume|Tome|volume|tome|T[0-9]+|V[0-9]+)`, "[MFT] One Piece Tome 01-65 fr")
	fmt.Println(matchVolume)
	// [MFT] One Piece Scan 740 fr

}
<file_sep>package providers

import "fmt"

// Providers available
var Providers = map[string]Provider{
	"T411": {"http://www.t411.io", "html"},
}

// Provider contains informations and options about providers
type Provider struct {
	URL  string
	Type string
}

// Result of provider search
type Result struct {
	Name         string
	DetailURL    string
	TorrentURL   string
	IsVolume     bool
	IsChapter    bool
	RefNumbers   []string
	IsMultifiles bool
	Format       string
}

// Display display the result
func (r *Result) Display() {
	fmt.Println(r.Name)
	if r.IsVolume {
		fmt.Printf(" - Volume : %v\n", r.RefNumbers)
	} else if r.IsChapter {
		fmt.Printf(" - Chapter : %v\n", r.RefNumbers)
	}
	fmt.Println(" - URL D : ", r.DetailURL)
	fmt.Println(" - URL T : ", r.TorrentURL)
}

// IsAvailableProvider provider
func IsAvailableProvider(provider string) bool {
	_, exists := Providers[provider]
	return exists
}

// DisplayProvidersList liste all available providers in the output
func DisplayProvidersList() {
	fmt.Println("Available providers are :")
	for k := range Providers {
		fmt.Printf("- %s \n", k)
	}
}
<file_sep>package main

import (
	"flag"
	"fmt"
	"net/url"

	p "github.com/bpetetot/bookie/providers"
)

func main() {

	// Parsing command line arguments
	var provider = flag.String("provider", "t411", "Provider name")
	var search = flag.String("search", "", "Searched book or manga")
	var typeSearch = flag.String("type", "volume", "Set volume or chapter")
	var number = flag.Int("number", 1, "Volume or Chapter number")
	flag.Parse()

	if *provider == "" || *search == "" {
		flag.PrintDefaults()
	}

	// Search values into providers
	if p.IsAvailableProvider(*provider) {
		fmt.Printf("Search '%s' with '%s' provider. \n", *search, *provider)
		var urlp = p.Init(*search)
		var body = p.Search(urlp)
		var results = p.ParseSearch(body)
		var uResults = p.AnalyzeSearch(*results, *typeSearch, *number)

		for _, r := range uResults {
			// Get details
			urlp, err := url.Parse("http:" + r.DetailURL)
			if err != nil {
				panic("Unable to parse 'T411' provider URL")
			}
			body = p.GetDetails(urlp)
			r.TorrentURL = p.ParseDetails(body)
			r.Display()

			// Download torrent file
			urlp, err = url.Parse("http://www.t411.in" + r.TorrentURL)
			if err != nil {
				panic("Unable to parse 'T411' provider URL")
			}
			err = p.DownloadTorrentFile(urlp, "D:\\temp\\")
			if err != nil {
				fmt.Printf("Error downloading torrent %s\n", err)
			}
		}

	} else {
		fmt.Printf("Provider '%s' not found. \n", *provider)
		p.DisplayProvidersList()
	}

}
