Skip to content

Commit 048e31d

Browse files
committed
Added --bundle arg
By using it you can bundle/package the entire defined range into a single file
1 parent 1fd7088 commit 048e31d

File tree

16 files changed

+213
-85
lines changed

16 files changed

+213
-85
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
manga-downloader
22
*.cbz
3+
demos/*.yml

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Manga Downloader
99
This app downloads mangas from websites like mangadex and stores them into cbz files, so you can read them with your
1010
favorite ereader or reading app.
1111

12-
![demo img]
12+
![download img]
1313

1414
Supported sites
1515
---------------
@@ -47,12 +47,27 @@ manga-downloader --language es https://mangadex.org/title/a1c7c817-4e59-43b7-936
4747
# would download One Piece chapters 1 to 10 in spanish
4848
~~~
4949

50+
### Bundling
51+
52+
You can bundle all the downloaded chapters into a single file by using the `--bundle` arg:
53+
54+
~~~bash
55+
manga-downloader https://inmanga.com/ver/manga/One-Piece/dfc7ecb5-e9b3-4aa5-a61b-a498993cd935 1-8 --bundle
56+
# would download one piece chapters 1 to 8 and bundle them into a single file
57+
~~~
58+
59+
![bundle img]
60+
61+
### Help
62+
5063
Use the `help` command to see all the available options:
5164

5265
~~~bash
5366
manga-downloader help
5467
~~~
5568

69+
![help img]
70+
5671
Installation
5772
------------
5873

@@ -117,4 +132,6 @@ All the code contained in this repo is licensed under the [GNU Affero General Pu
117132
[license badge]: https://img.shields.io/github/license/elboletaire/manga-downloader?color=green
118133
[releases]: https://github.com/elboletaire/manga-downloader/releases
119134
[issues]: https://github.com/elboletaire/manga-downloader/issues
120-
[demo img]: https://raw.githubusercontent.com/elboletaire/manga-downloader/master/demo.gif
135+
[download img]: https://raw.githubusercontent.com/elboletaire/manga-downloader/master/demos/download.gif
136+
[bundle img]: https://raw.githubusercontent.com/elboletaire/manga-downloader/master/demos/bundle.gif
137+
[help img]: https://raw.githubusercontent.com/elboletaire/manga-downloader/master/demos/help.gif

cmd/root.go

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
cc "github.com/ivanpirog/coloredcobra"
2020
)
2121

22+
var settings grabber.Settings
23+
2224
// rootCmd represents the base command when called without any subcommands
2325
var rootCmd = &cobra.Command{
2426
Use: "manga-downloader [flags] [url] [ranges]",
@@ -48,7 +50,7 @@ Would download chapters 10 to 20 of Black Clover from
4850
mangadex.org in Spanish`, "manga-downloader", color.YellowString("manga-downloader")),
4951
Args: cobra.ExactArgs(2),
5052
Run: func(cmd *cobra.Command, args []string) {
51-
s, errs := grabber.NewSite(args[0])
53+
s, errs := grabber.NewSite(args[0], &settings)
5254
if len(errs) > 0 {
5355
color.Red("Errors testing site (a site may be down):")
5456
for _, err := range errs {
@@ -62,7 +64,8 @@ mangadex.org in Spanish`, "manga-downloader", color.YellowString("manga-download
6264
s.InitFlags(cmd)
6365

6466
// ranges parsing
65-
rngs, err := ranges.Parse(args[1])
67+
settings.Range = args[1]
68+
rngs, err := ranges.Parse(settings.Range)
6669
cerr(err, "Error parsing ranges: %s")
6770

6871
// fetch series title
@@ -83,13 +86,15 @@ mangadex.org in Spanish`, "manga-downloader", color.YellowString("manga-download
8386
chapters = chapters.FilterRanges(rngs)
8487

8588
if len(chapters) == 0 {
86-
warn("No chapters found for the specified ranges")
89+
fmt.Println(color.YellowString("No chapters found for the specified ranges"))
90+
os.Exit(1)
8791
}
8892

93+
// download chapters
8994
wg := sync.WaitGroup{}
9095
g := make(chan struct{}, s.GetMaxConcurrency().Chapters)
96+
downloaded := grabber.Filterables{}
9197

92-
// loop chapters to retrieve pages
9398
for _, chap := range chapters {
9499
g <- struct{}{}
95100
wg.Add(1)
@@ -110,26 +115,51 @@ mangadex.org in Spanish`, "manga-downloader", color.YellowString("manga-download
110115
return
111116
}
112117

113-
filename, err := packer.NewFilenameFromTemplate(title, chapter, s.GetFilenameTemplate())
114-
if err != nil {
115-
color.Red("- error creating filename for chapter %s: %s", chapter.GetTitle(), err.Error())
116-
<-g
117-
return
118+
d := &packer.DownloadedChapter{
119+
Chapter: chapter,
120+
Files: files,
118121
}
119122

120-
filename += ".cbz"
121-
122-
if err = packer.ArchiveCBZ(filename, files); err != nil {
123-
color.Red("- error saving file %s: %s", filename, err.Error())
123+
if !settings.Bundle {
124+
filename, err := packer.PackSingle(s, d)
125+
if err == nil {
126+
fmt.Printf("- %s %s\n", color.GreenString("saved file"), color.HiBlackString(filename))
127+
} else {
128+
color.Red(err.Error())
129+
}
124130
} else {
125-
fmt.Printf("- %s %s\n", color.GreenString("saved file"), color.HiBlackString(filename))
131+
// avoid adding it to memory if we're not gonna use it
132+
downloaded = append(downloaded, d)
126133
}
127134

128135
// release guard
129136
<-g
130137
}(chap)
131138
}
132139
wg.Wait()
140+
close(g)
141+
142+
if !settings.Bundle {
143+
// if we're not bundling, just finish it
144+
os.Exit(0)
145+
}
146+
147+
// resort downloaded
148+
downloaded = downloaded.SortByNumber()
149+
150+
dc := []*packer.DownloadedChapter{}
151+
// convert slice back to DownloadedChapter
152+
for _, d := range downloaded {
153+
dc = append(dc, d.(*packer.DownloadedChapter))
154+
}
155+
156+
filename, err := packer.PackBundle(s, dc, settings.Range)
157+
if err != nil {
158+
color.Red(err.Error())
159+
os.Exit(1)
160+
}
161+
162+
fmt.Printf("- %s %s\n", color.GreenString("saved file"), color.HiBlackString(filename))
133163
},
134164
}
135165

@@ -156,10 +186,11 @@ func Execute() {
156186

157187
// init sets the flags for the root command
158188
func init() {
159-
rootCmd.Flags().Uint8P("concurrency", "c", 5, "number of concurrent chapter downloads, hard-limited to 5")
160-
rootCmd.Flags().Uint8P("concurrency-pages", "C", 10, "number of concurrent page downloads, hard-limited to 10")
161-
rootCmd.Flags().StringP("language", "l", "", "only download the specified language")
162-
rootCmd.Flags().StringP("filename-template", "t", packer.FilenameTemplateDefault, "template for the resulting filename")
189+
rootCmd.Flags().BoolVarP(&settings.Bundle, "bundle", "b", false, "bundle all specified chapters into a single file")
190+
rootCmd.Flags().Uint8VarP(&settings.MaxConcurrency.Chapters, "concurrency", "c", 5, "number of concurrent chapter downloads, hard-limited to 5")
191+
rootCmd.Flags().Uint8VarP(&settings.MaxConcurrency.Pages, "concurrency-pages", "C", 10, "number of concurrent page downloads, hard-limited to 10")
192+
rootCmd.Flags().StringVarP(&settings.Language, "language", "l", "", "only download the specified language")
193+
rootCmd.Flags().StringVarP(&settings.FilenameTemplate, "filename-template", "t", packer.FilenameTemplateDefault, "template for the resulting filename")
163194
}
164195

165196
func cerr(err error, prefix string) {
@@ -168,8 +199,3 @@ func cerr(err error, prefix string) {
168199
os.Exit(1)
169200
}
170201
}
171-
172-
func warn(err string) {
173-
fmt.Println(color.YellowString(err))
174-
os.Exit(1)
175-
}

demo.gif

-2.47 MB
Binary file not shown.

demos/bundle.gif

756 KB
Loading

demos/download.gif

512 KB
Loading

demos/help.gif

302 KB
Loading

downloader/fetch.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@ package downloader
22

33
import (
44
"bytes"
5-
"fmt"
65
"io"
6+
"sort"
77
"sync"
88

99
"github.com/elboletaire/manga-downloader/grabber"
1010
"github.com/elboletaire/manga-downloader/http"
1111
"github.com/fatih/color"
1212
)
1313

14+
// File represents a downloaded file
1415
type File struct {
1516
Data []byte
16-
Name string
17+
Page uint
1718
}
1819

1920
// FetchChapter downloads all the pages of a chapter
@@ -29,14 +30,13 @@ func FetchChapter(site grabber.Site, chapter *grabber.Chapter) (files []*File, e
2930
go func(page grabber.Page) {
3031
defer wg.Done()
3132

32-
filename := fmt.Sprintf("%03d.jpg", page.Number)
3333
file, err := FetchFile(http.RequestParams{
3434
URL: page.URL,
35-
Referer: site.GetBaseUrl(),
36-
}, filename)
35+
Referer: site.BaseUrl(),
36+
}, uint(page.Number))
3737

3838
if err != nil {
39-
color.Red("- error downloading page %s", filename)
39+
color.Red("- error downloading page %d of %s", page.Number, chapter.GetTitle())
4040
return
4141
}
4242

@@ -46,14 +46,19 @@ func FetchChapter(site grabber.Site, chapter *grabber.Chapter) (files []*File, e
4646
<-guard
4747
}(page)
4848
}
49-
5049
wg.Wait()
50+
close(guard)
51+
52+
// sort files by page number
53+
sort.SliceStable(files, func(i, j int) bool {
54+
return files[i].Page < files[j].Page
55+
})
5156

5257
return
5358
}
5459

5560
// FetchFile gets an online file returning a new *File with its contents
56-
func FetchFile(params http.RequestParams, filename string) (file *File, err error) {
61+
func FetchFile(params http.RequestParams, page uint) (file *File, err error) {
5762
body, err := http.Get(params)
5863
if err != nil {
5964
return
@@ -67,7 +72,7 @@ func FetchFile(params http.RequestParams, filename string) (file *File, err erro
6772

6873
file = &File{
6974
Data: data.Bytes(),
70-
Name: filename,
75+
Page: page,
7176
}
7277

7378
return

grabber/inmanga.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
// Inmanga is a grabber for inmanga.com
1414
type Inmanga struct {
15-
Grabber
15+
*Grabber
1616
title string
1717
}
1818

@@ -54,7 +54,7 @@ func (i *Inmanga) FetchTitle() (string, error) {
5454

5555
// FetchChapters returns the chapters of the manga
5656
func (i Inmanga) FetchChapters() (Filterables, []error) {
57-
id := GetUUID(i.URL)
57+
id := getUuid(i.URL)
5858

5959
// retrieve chapters json list
6060
body, err := http.GetText(http.RequestParams{

grabber/mangadex.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313

1414
// Mangadex is a grabber for mangadex.org
1515
type Mangadex struct {
16-
Grabber
16+
*Grabber
1717
title string
1818
}
1919

@@ -35,11 +35,11 @@ func (m *Mangadex) FetchTitle() (string, error) {
3535
return m.title, nil
3636
}
3737

38-
id := GetUUID(m.URL)
38+
id := getUuid(m.URL)
3939

4040
rbody, err := http.Get(http.RequestParams{
4141
URL: "https://api.mangadex.org/manga/" + id,
42-
Referer: m.GetBaseUrl(),
42+
Referer: m.BaseUrl(),
4343
})
4444
if err != nil {
4545
return "", err
@@ -53,8 +53,8 @@ func (m *Mangadex) FetchTitle() (string, error) {
5353
}
5454

5555
// fetch the title in the requested language
56-
if m.PreferredLanguage != "" {
57-
trans := body.Data.Attributes.AltTitles.GetTitleByLang(m.PreferredLanguage)
56+
if m.Settings.Language != "" {
57+
trans := body.Data.Attributes.AltTitles.GetTitleByLang(m.Settings.Language)
5858

5959
if trans != "" {
6060
m.title = trans
@@ -70,7 +70,7 @@ func (m *Mangadex) FetchTitle() (string, error) {
7070

7171
// FetchChapters returns the chapters of the manga
7272
func (m Mangadex) FetchChapters() (chapters Filterables, errs []error) {
73-
id := GetUUID(m.URL)
73+
id := getUuid(m.URL)
7474

7575
baseOffset := 500
7676
var fetchChaps func(int)
@@ -86,8 +86,8 @@ func (m Mangadex) FetchChapters() (chapters Filterables, errs []error) {
8686
params.Add("order[volume]", "asc")
8787
params.Add("order[chapter]", "asc")
8888
params.Add("offset", fmt.Sprint(offset))
89-
if m.PreferredLanguage != "" {
90-
params.Add("translatedLanguage[]", m.PreferredLanguage)
89+
if m.Settings.Language != "" {
90+
params.Add("translatedLanguage[]", m.Settings.Language)
9191
}
9292
uri = fmt.Sprintf("%s?%s", uri, params.Encode())
9393

@@ -108,9 +108,10 @@ func (m Mangadex) FetchChapters() (chapters Filterables, errs []error) {
108108
num, _ := strconv.ParseFloat(c.Attributes.Chapter, 64)
109109
chapters = append(chapters, &MangadexChapter{
110110
Chapter{
111-
Number: num,
112-
Title: c.Attributes.Title,
113-
Language: c.Attributes.TranslatedLanguage,
111+
Number: num,
112+
Title: c.Attributes.Title,
113+
Language: c.Attributes.TranslatedLanguage,
114+
PagesCount: c.Attributes.Pages,
114115
},
115116
c.Id,
116117
})
@@ -194,6 +195,7 @@ type mangadexFeed struct {
194195
Chapter string
195196
Title string
196197
TranslatedLanguage string
198+
Pages int64
197199
}
198200
}
199201
}

0 commit comments

Comments
 (0)