Commits

Jason Moiron committed c9a5cd8

rearrange zip name, simplify get chapters, add overwrite option

Comments (0)

Files changed (5)

 		c.Number = strings.TrimLeft(FindNumber(numRe.FindString(c.Url)), " \t0")
 		chapters = append(chapters, c)
 	}
-	return chapters
+	reversed := make([]*Chapter, 0, len(chapters))
+	for i := len(chapters) - 1; i >= 0; i-- {
+		reversed = append(reversed, chapters[i])
+	}
+	return reversed
 }
 
 func (b *Batoto) GetPageUrls(doc *goquery.Document) []string {
 		fragment := Attr(o, "value")
 		urls = append(urls, BaseJoin(base, fragment))
 	}
-
 	return urls
 }
 
 	"os"
 	"path/filepath"
 	"runtime"
+	"strings"
 	"time"
 
 	"github.com/jmoiron/sqlx"
 	return fmt.Sprintf("%#v", c)
 }
 
+func (c Chapter) SeriesString() string {
+	if config.UseUnderscores {
+		return strings.Replace(c.Series, " ", "_", -1)
+	}
+	return c.Series
+}
+
 type Log struct {
 	Timestamp int64
 	Message   string
 
 // Return a list of all series matching terms.  If group is true, series are
 // grouped by their names and repeating fields are concatted w/ csv
-func FindSeries(site string, terms ...string) ([]*Series, error) {
+func FindSeriesSQL(site string, terms ...string) ([]*Series, error) {
 	series := make([]*Series, 0, 10)
 	term := fmt.Sprintf("%%%s%%", strings.Join(terms, "%"))
 	var err error
 	return series, err
 }
 
-// ActiveSearchSeries uses the provided site updater to search for the terms.
+// FindSeriesHttp uses the provided site updater to search for the terms.
 // For now, this is only valid when the site is provided, as the result lists
 // will clash with the sql results in a way that isn't really fixable.
-func ActiveSearchSeries(site string, terms ...string) ([]*Series, error) {
+func FindSeriesHttp(site string, terms ...string) ([]*Series, error) {
 	if len(site) == 0 {
 		log.Fatalf("-c requires --site as well.")
 	}
 	return u.Search(terms...), nil
 }
 
+func FindSeries(site string, terms ...string) ([]*Series, error) {
+	if opts.UseActiveSearches {
+		vprintf("Using active search as requested.")
+		return FindSeriesHttp(site, terms...)
+	}
+	return FindSeriesSQL(site, terms...)
+}
+
 // Like the series above, but attempts to find an exact match for the
 // terms being searched for;  if the name or the key is a case insensitive
 // full string match to the terms, the returned array will contain only that
+// If the final term is an integer, it's assumed to be the index in the
+// series search to select.
 func FindOneSeries(site string, terms ...string) ([]*Series, error) {
+	// see if the final term is an index;  if it is, save it but remove it from
+	// the terms that we actually use to search.
+	index, err := strconv.Atoi(terms[len(terms)-1])
+	if err != nil {
+		index = -1
+	} else {
+		terms = terms[:len(terms)-1]
+	}
+
 	series, err := FindSeries(site, terms...)
 	if len(series) < 2 {
 		return series, err
 	}
-	var lname, lkey string
+
+	if index >= 0 {
+		vprintf("Index of %d detected, returning series %s", index, series[index])
+		return []*Series{series[index]}, nil
+	}
 
 	exact := strings.ToLower(strings.Join(terms, " "))
+	var lname, lkey string
 
 	for _, s := range series {
 		lname = strings.ToLower(s.Name)
 	Force                bool
 	SetDownloadPath      string
 	Download             bool
+	Overwrite            bool
 	Site                 string
 	ListSites            bool
 	AddSite              string
 
 func Search(terms ...string) {
 	UpdateSites(false)
-	var series []*Series
-	var err error
-	if opts.UseActiveSearches {
-		series, err = ActiveSearchSeries(opts.Site, terms...)
-	} else {
-		series, err = FindSeries(opts.Site, terms...)
-	}
+
+	series, err := FindSeries(opts.Site, terms...)
+
 	if err != nil {
 		log.Printf("Search: %s\n", err)
 		return
 
 func Show(terms ...string) {
 	UpdateSites()
+
 	series, err := FindOneSeries(opts.Site, terms...)
+
 	if err != nil {
 		fmt.Printf("Error searching for terms: %s\n", err)
 	}
 		for _, s := range series {
 			fmt.Printf(" * %s (%s)\n", s.Name, s.Site)
 		}
-		fmt.Printf("Try using the exact name (or key) of a series.\n")
+		fmt.Printf("Try using the exact name (or key), or using the index (from zero) in the list.\n")
 		return
 	}
 
-	chapters := UpdateChapters(series[0].Name)
+	chapters := GetChapters(series[0])
 	for _, c := range chapters {
 		if opts.Filter.Match(c.Number) {
 			fmt.Printf(" * %s %s (%s)\n", c.Series, c.Number, c.Site)
 		return
 	}
 
-	chapters := UpdateChapters(series[0].Name)
+	chapters := GetChapters(series[0])
 	for _, c := range chapters {
 		if opts.Filter.Match(c.Number) {
 			DownloadChapter(c)
 	optarg.Add("", "sync", "Sync series info with what is on disk.", false)
 	optarg.Add("d", "download", "Download new chapters from series.", false)
 	optarg.Add("", "force", "Use with --update to force updating.", false)
+	optarg.Add("o", "overwrite", "Overwrite previous downloads.", false)
 	optarg.Add("", "set-download-path",
 		fmt.Sprintf("Change destination for sync and downloads. (Current: %s)", config.DownloadPath), "")
 	optarg.Add("", "toggle-use-underscores",
 			opts.Log = opt.Bool()
 		case "clear":
 			opts.Clear = opt.Bool()
+		case "overwrite":
+			opts.Overwrite = opt.Bool()
 		// sites
 		case "sites":
 			opts.ListSites = opt.Bool()
 			UpdaterRegistry[key] = &Batoto{val}
 		}
 	}
+
+	// If the Site is set to something which is search-only, use active searching
+	// is forced to true.
+	if len(opts.Site) > 0 {
+		if u, ok := UpdaterRegistry[opts.Site]; ok && len(u.SeriesListPath()) == 0 {
+			opts.UseActiveSearches = true
+		}
+	}
 }
 
 func UpdateSites(force ...bool) {
 	tx.Commit()
 }
 
+func GetChapters(series *Series) []*Chapter {
+	if opts.UseActiveSearches {
+		return UpdaterRegistry[opts.Site].GetChapters(series)
+	}
+	return UpdateChapters(series.Name)
+}
+
 // update chapters for a series.  searches the db for all sites that have
 // the series, and updates any which are too old for the chapters threshold
 func UpdateChapters(name string, force ...bool) []*Chapter {
 	return "", ""
 }
 
+// zipfile returns the zipfile name for a chapter, with the given version number.
+// If the version is 0, then it is not used.
+func zipfile(chapter *Chapter, version int) string {
+	number := chapter.Number
+	// for some reason, sometimes chaps are numbered '3.1', '3.5', etc
+	if chapter.Numberf != 0 {
+		fnum := fmt.Sprintf("%03.1f", chapter.Numberf)
+		if fnum[len(fnum)-1] != '0' {
+			number = fnum
+		} else {
+			number = fmt.Sprintf("%03d", int(chapter.Numberf))
+		}
+	}
+	var verstr string
+	if version > 0 {
+		verstr = fmt.Sprintf("-v%d", version)
+	}
+	return fmt.Sprintf("%s-c%s%s.zip", chapter.SeriesString(), number, verstr)
+}
+
 func DownloadChapter(chapter *Chapter) error {
 	site, url := SelectUrl(chapter)
 	vprintf(" %s %s (%s, %s)\n", chapter.Series, chapter.Number, site, url)
 		fmt.Printf("Error fetching `%s`: %s\n", url, err)
 		return err
 	}
-	var destzip string
-	series := chapter.Series
-	if config.UseUnderscores {
-		series = strings.Replace(series, " ", "_", -1)
-	}
-	destpath := filepath.Join(config.DownloadPath, series, chapter.Number)
-	if chapter.Numberf != 0 {
-		var path string
-		fnum := fmt.Sprintf("%0.1f", chapter.Numberf)
-		if fnum[len(fnum)-1] != '0' {
-			path = fmt.Sprintf("%s-c%03.1f.zip", series, chapter.Numberf)
-		} else {
-			path = fmt.Sprintf("%s-c%03d.zip", series, int(chapter.Numberf))
+
+	destpath := filepath.Join(config.DownloadPath, chapter.SeriesString(), chapter.Number)
+	destzip := filepath.Join(config.DownloadPath, chapter.SeriesString(), zipfile(chapter, 0))
+	// Bump the version number until the file doesn't exist.
+	if !opts.Overwrite {
+		_, err = os.Stat(destzip)
+		for v := 1; err == nil; v++ {
+			destzip = filepath.Join(config.DownloadPath, chapter.SeriesString(), zipfile(chapter, v))
+			_, err = os.Stat(destzip)
 		}
-		destzip = filepath.Join(config.DownloadPath, series, path)
-	} else {
-		destzip = filepath.Join(config.DownloadPath, series,
-			fmt.Sprintf("%s-c%s.zip", series, chapter.Number))
 	}
 
 	page_urls := updater.GetPageUrls(doc)