manga-downloadr 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1b1a496ff7a0146d994b4e53490c869344a032da
4
+ data.tar.gz: c333cb615114dbde3956a162ea56d332829550c8
5
+ SHA512:
6
+ metadata.gz: 7eac6dc32977973b667c30935e31645946ac083b2de60fe5d1397539b8605158e0d43a745f742ffe7ce99c17d7793b983115b1a77b07ee3d380519a0c0bf5826
7
+ data.tar.gz: b3f7538dd773bdea1f308bf26b6274cf89255cbd56e9e9644f64ca3327ba677ff51be17a06e23aafa4abcfcbc7fc86ef9328cca99333e1a465b3e55252daa5f7
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Fabio Akita
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,32 @@
1
+ # Manga Downloadr
2
+
3
+ I just bought a new Kindle Paperwhite and so happens it's the perfect form factor
4
+ to read good old, black and white, mangas.
5
+
6
+ So I decided to automate the process of fetching manga images from MangaReader.net,
7
+ optimize and compile them into PDF files that fit the Kindle resolution.
8
+
9
+ ## Installation
10
+
11
+ Just install with:
12
+
13
+ ```
14
+ gem install manga-downloadr
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ And then execute:
20
+
21
+ $ manga-downloader -n berserk -u http://www.mangareader.net/96/berserk.html -d /MyDocuments/MyMangas
22
+
23
+ If there's any interruption (bad network) you can run the same command line again and it will resume from
24
+ where it was interrupted before.
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it ( https://github.com/akitaonrails/manga-downloadr/fork )
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'optparse'
4
+
5
+ options = {}
6
+ option_parser = OptionParser.new do |opts|
7
+ opts.banner = "Usage: manga-downloadr [options]"
8
+
9
+ opts.on("-u URL", "--url URL",
10
+ "Full MangaReader.net manga homepage URL - required") do |v|
11
+ options[:url] = v
12
+ end
13
+
14
+ opts.on("-n NAME", "--name NAME",
15
+ "slug to be used for the sub-folder to store all manga files - required") do |n|
16
+ options[:name] = n
17
+ end
18
+
19
+ opts.on("-d DIRECTORY", "--directory DIRECTORY",
20
+ "main folder where all mangas will be stored - required") do |d|
21
+ options[:directory] = d
22
+ end
23
+
24
+ opts.on("-h", "--help", "Show this message") do
25
+ puts opts
26
+ exit
27
+ end
28
+ end
29
+
30
+ begin
31
+ ARGV << "-h" if ARGV.empty?
32
+ option_parser.parse!(ARGV)
33
+ rescue OptionParser::ParseError
34
+ $stderr.print "Error: " + $! + "\n"
35
+ exit
36
+ end
37
+
38
+ require 'manga-downloadr'
39
+ generator = MangaDownloadr::Workflow.create(options[:url], options[:name], manga_root: options[:directory])
40
+ unless generator.state?(:chapter_urls)
41
+ puts "Massive parallel scanning of all chapters "
42
+ generator.fetch_chapter_urls!
43
+ end
44
+ unless generator.state?(:page_urls)
45
+ puts "\nMassive parallel scanning of all pages "
46
+ generator.fetch_page_urls!
47
+ end
48
+ unless generator.state?(:image_urls)
49
+ puts "\nMassive parallel scanning of all images "
50
+ generator.fetch_image_urls!
51
+ puts "\nTotal page links found: #{generator.chapter_pages_count}"
52
+ end
53
+ unless generator.state?(:images)
54
+ puts "\nMassive parallel download of all page images "
55
+ generator.fetch_images!
56
+ end
57
+ puts "\nCompiling all images into PDF volumes "
58
+ generator.compile_ebooks!
59
+ puts "\nProcess finished."
@@ -0,0 +1,177 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'nokogiri'
5
+ require 'typhoeus'
6
+ require 'fileutils'
7
+ require 'rmagick'
8
+ require 'prawn'
9
+ require 'fastimage'
10
+ require 'open-uri'
11
+ require 'yaml'
12
+
13
+ module MangaDownloadr
14
+ ImageData = Struct.new(:folder, :filename, :url)
15
+
16
+ class Workflow
17
+ attr_accessor :manga_root_url, :manga_root, :manga_root_folder, :manga_name, :hydra_concurrency
18
+ attr_accessor :chapter_list, :chapter_pages, :chapter_images, :download_links, :chapter_pages_count
19
+ attr_accessor :manga_title, :pages_per_volume, :page_size
20
+ attr_accessor :processing_state
21
+
22
+ def initialize(root_url, manga_name, options = {})
23
+ self.manga_root_url = root_url
24
+ self.manga_root = options[:manga_root] || "/vagrant/tmp/mangareader/"
25
+ self.manga_root_folder = File.join(manga_root, manga_name)
26
+ self.manga_name = manga_name
27
+
28
+ self.hydra_concurrency = options[:hydra_concurrency] || 100
29
+
30
+ self.chapter_pages = {}
31
+ self.chapter_images = {}
32
+
33
+ self.pages_per_volume = options[:pages_per_volume] || 250
34
+ self.page_size = options[:page_size] || [600, 800]
35
+
36
+ self.processing_state = []
37
+ end
38
+
39
+ def fetch_chapter_urls!
40
+ doc = Nokogiri::HTML(open(manga_root_url))
41
+
42
+ self.chapter_list = doc.css("#listing a").map { |l| l['href']}
43
+ self.manga_title = doc.css("#mangaproperties h1").first.text
44
+
45
+ current_state :chapter_urls
46
+ end
47
+
48
+ def fetch_page_urls!
49
+ hydra = Typhoeus::Hydra.new(max_concurrency: hydra_concurrency)
50
+ chapter_list.each do |chapter_link|
51
+ begin
52
+ request = Typhoeus::Request.new "http://www.mangareader.net#{chapter_link}"
53
+ request.on_complete do |response|
54
+ chapter_doc = Nokogiri::HTML(response.body)
55
+ pages = chapter_doc.css('#selectpage #pageMenu option')
56
+ chapter_pages.merge!(chapter_link => pages.map { |p| p['value'] })
57
+ print '.'
58
+ end
59
+ hydra.queue request
60
+ rescue => e
61
+ puts e
62
+ end
63
+ end
64
+ hydra.run
65
+
66
+ self.chapter_pages_count = chapter_pages.values.inject(0) { |total, list| total += list.size }
67
+ current_state :page_urls
68
+ end
69
+
70
+ def fetch_image_urls!
71
+ hydra = Typhoeus::Hydra.new(max_concurrency: hydra_concurrency)
72
+ chapter_list.each do |chapter_key|
73
+ chapter_pages[chapter_key].each do |page_link|
74
+ begin
75
+ request = Typhoeus::Request.new "http://www.mangareader.net#{page_link}"
76
+ request.on_complete do |response|
77
+ chapter_doc = Nokogiri::HTML(response.body)
78
+ image = chapter_doc.css('#img').first
79
+ tokens = image['alt'].match("^(.*?)\s\-\s(.*?)$")
80
+ extension = File.extname(URI.parse(image['src']).path)
81
+
82
+ chapter_images.merge!(chapter_key => []) if chapter_images[chapter_key].nil?
83
+ chapter_images[chapter_key] << ImageData.new( tokens[1], "#{tokens[2]}#{extension}", image['src'] )
84
+ print '.'
85
+ end
86
+ hydra.queue request
87
+ rescue => e
88
+ puts e
89
+ end
90
+ end
91
+ end
92
+ hydra.run
93
+
94
+ current_state :image_urls
95
+ end
96
+
97
+ def fetch_images!
98
+ hydra = Typhoeus::Hydra.new(max_concurrency: hydra_concurrency)
99
+ chapter_list.each_with_index do |chapter_key, chapter_index|
100
+ chapter_images[chapter_key].each do |file|
101
+ begin
102
+ downloaded_filename = File.join(manga_root_folder, file.folder, file.filename)
103
+ next if File.exists?(downloaded_filename) # effectively resumes the download list without re-downloading everything
104
+ request = Typhoeus::Request.new file.url
105
+ request.on_complete do |response|
106
+ # download
107
+ FileUtils.mkdir_p(File.join(manga_root_folder, file.folder))
108
+ File.open(downloaded_filename, "wb+") { |f| f.write response.body }
109
+
110
+ # resize
111
+ image = Magick::Image.read( downloaded_filename ).first
112
+ resized = image.resize_to_fit(600, 800)
113
+ resized.write( downloaded_filename ) { self.quality = 50 }
114
+
115
+ print '.'
116
+ GC.start # to avoid a leak too big (ImageMagick is notorious for that, specially on resizes)
117
+ end
118
+ hydra.queue request
119
+ rescue => e
120
+ puts e
121
+ end
122
+ end
123
+ end
124
+ hydra.run
125
+
126
+ current_state :images
127
+ end
128
+
129
+ def compile_ebooks!
130
+ folders = Dir[manga_root_folder + "/*/"].sort_by { |element| ary = element.split(" ").last.to_i }
131
+ self.download_links = folders.inject([]) do |list, folder|
132
+ list += Dir[folder + "*.*"].sort_by { |element| ary = element.split(" ").last.to_i }
133
+ end
134
+
135
+ # concatenating PDF files (250 pages per volume)
136
+ chapter_number = 0
137
+ while !download_links.empty?
138
+ chapter_number += 1
139
+ pdf_file = File.join(manga_root_folder, "#{manga_title} #{chapter_number}.pdf")
140
+ list = download_links.slice!(0..pages_per_volume)
141
+ Prawn::Document.generate(pdf_file, page_size: page_size) do |pdf|
142
+ list.each do |image_file|
143
+ begin
144
+ pdf.image image_file, position: :center, vposition: :center
145
+ rescue => e
146
+ puts "Error in #{image_file} - #{e}"
147
+ end
148
+ end
149
+ end
150
+ print '.'
151
+ end
152
+
153
+ current_state :ebooks
154
+ end
155
+
156
+ def state?(state)
157
+ self.processing_state.include?(state)
158
+ end
159
+
160
+ private def current_state(state)
161
+ self.processing_state << state
162
+ MangaDownloadr::Workflow.serialize(self)
163
+ end
164
+
165
+ class << self
166
+ def serialize(obj)
167
+ File.open("/tmp/#{obj.manga_name}.yaml", 'w') {|f| f.write(YAML::dump(obj)) }
168
+ end
169
+
170
+ def create(root_url, manga_name, options = {})
171
+ dump_file_name = "/tmp/#{manga_name}.yaml"
172
+ return YAML::load(File.read(dump_file_name)) if File.exists?(dump_file_name)
173
+ MangaDownloadr::Workflow.new(root_url, manga_name, options)
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,3 @@
1
+ module MangaDownloadr
2
+ VERSION = "1.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: manga-downloadr
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - AkitaOnRails
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: typhoeus
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rmagick
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.13'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.13'
55
+ - !ruby/object:Gem::Dependency
56
+ name: prawn
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: fastimage
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.6'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.6'
83
+ description: downloads any manga from MangaReader.net
84
+ email: boss@akitaonrails.com
85
+ executables:
86
+ - manga-downloadr
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE.txt
91
+ - README.md
92
+ - Rakefile
93
+ - bin/manga-downloadr
94
+ - lib/manga-downloadr.rb
95
+ - lib/manga-downloadr/version.rb
96
+ homepage: http://github.com/akitaonrails/manga-downloadr
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.4.3
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: downloads and compile to a Kindle optimized manga in PDF
120
+ test_files: []