├── commiter.yml ├── download.bat ├── .gitignore ├── update.sh ├── templates.rb ├── config.json ├── page_template.html ├── downloader.rb ├── redownload.rb ├── mangaconfig.rb ├── LNDownloader.rb └── MangaDownloader.rb /commiter.yml: -------------------------------------------------------------------------------- 1 | convention: symphony 2 | -------------------------------------------------------------------------------- /download.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | downloader.rb %* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test.rb 2 | urls.txt 3 | input.txt 4 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | ruby downloader.rb 3 | -------------------------------------------------------------------------------- /templates.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | TEMPLATE_HTML = File.read("page_template.html") 4 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "manganelo.com": { 3 | "ln": false, 4 | "chapter":".row-content-chapter a","page":".container-chapter-reader img","page1": "img" 5 | }, 6 | "www.readlightnovel.org": { 7 | "ln": true, 8 | "chapter":"#accordion .tab-content li a","page":".chapter-content3 .desc" 9 | }, 10 | "jpmtl.com": { 11 | "ln": true, 12 | "chapter":".book-ccontent a","page":"article" 13 | }, 14 | "mangakakalots.com": { 15 | "ln": false, 16 | "chapter":"#chapter .chapter-list a","page":"img" 17 | } 18 | } -------------------------------------------------------------------------------- /page_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Chapter {{chapter_index}} · {{mange_title}} 8 | 9 | 70 | 71 | 72 | 73 | 74 | {{button}} 75 | 76 | {{body}} 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /downloader.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'optparse' 5 | require 'json' 6 | 7 | require_relative 'MangaConfig' 8 | require_relative 'MangaDownloader' 9 | require_relative 'LNDownloader' 10 | 11 | MANGA_DIR = 'z:/Books/Manga' 12 | SELECTOR_CONFIG = JSON.parse(File.read(File.join(__dir__, 'config.json'))) 13 | 14 | $messages = [] 15 | def update_selectors(config) 16 | config['selector'] = SELECTOR_CONFIG[URI(config['url']).host] 17 | config 18 | end 19 | 20 | def download(config, _config_path) 21 | config = update_selectors(config) 22 | 23 | if config['ln'] 24 | download = LNDownloader.new(config) 25 | else 26 | download = MangaDownloader.new(config) 27 | end 28 | system('rm urls.txt') if File.exist?('urls.txt') 29 | $messages << "Downloaded [#{config['title']}]" if download.download 30 | end 31 | 32 | def download_manga(title) 33 | [ 34 | File.join(MANGA_DIR, title, 'Manga', title + ' - Manga.json'), 35 | File.join( 36 | MANGA_DIR, 37 | title, 38 | 'Light Novel - WEB', 39 | title + ' - Light Novel.json' 40 | ) 41 | ].each do |config_path| 42 | if File.exist?(config_path) 43 | config = JSON.parse(File.read(config_path)) 44 | download(config, config_path) 45 | end 46 | end 47 | end 48 | 49 | if ARGV.empty? 50 | while true 51 | config = MangaConfig.new 52 | download(config.get, config.config_path) 53 | end 54 | elsif ARGV.include? '-u' 55 | manga = 56 | Dir.entries(MANGA_DIR)[2..-1].select do |e| 57 | File.directory?(File.join(MANGA_DIR, e)) 58 | end 59 | 60 | manga.each do |title| 61 | download_manga(title) 62 | end 63 | elsif ARGV.include? '-d' 64 | title = ARGV.last 65 | download_manga(title) 66 | end 67 | 68 | puts $messages unless $messages.empty? 69 | -------------------------------------------------------------------------------- /redownload.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'optparse' 5 | require 'json' 6 | 7 | def manga_dir 8 | 'z:/Books/Manga' 9 | end 10 | 11 | def pad_num(num) 12 | num.to_s.rjust(4, '0') 13 | end 14 | 15 | def save_links_aria2c(list) 16 | download = 17 | list.flatten.map! do |url| 18 | [ 19 | url['url'], 20 | ' dir=' + url['directory'], 21 | ' out=' + url['filename'].split('&')[0] 22 | ].join("\n") 23 | end 24 | 25 | out = File.open('urls.txt', 'w') 26 | out.puts download.join("\n") 27 | out.close 28 | 29 | unless list.empty? 30 | system( 31 | 'aria2c --check-certificate=false --auto-file-renaming=false --continue=true -i urls.txt' 32 | ) 33 | end 34 | end 35 | 36 | def download(config, _config_path) 37 | download_list = [] 38 | manga_name = config['name'] 39 | puts manga_name 40 | manga_type = config['ln'] ? 'Light Novel' : 'Manga' 41 | config['chapters']['items'].each do |chapter| 42 | chapter['items'].each do |image| 43 | download_list << 44 | { 45 | 'directory' => 46 | "#{manga_dir}/#{manga_name}/#{manga_type}/Chapter #{pad_num(chapter["chapter"])}", 47 | 'filename' => "Page #{image["number"]}.jpg", 48 | 'url' => image["url"] 49 | } 50 | end 51 | end 52 | save_links_aria2c(download_list) 53 | exit 54 | end 55 | 56 | manga = 57 | Dir.entries(manga_dir)[2..-1].select do |e| 58 | File.directory?(File.join(manga_dir, e)) 59 | end 60 | 61 | manga.each do |m| 62 | config_path = File.join(manga_dir, m, 'Manga', m + ' - Manga.json') 63 | 64 | next if !File.exist?(config_path) 65 | config = JSON.parse(File.read(config_path)) 66 | 67 | if !config['ln'] 68 | download(config, config_path) 69 | system('rm urls.txt') if File.exist?('urls.txt') 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /mangaconfig.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'json' 5 | require 'fileutils' 6 | 7 | class MangaConfig 8 | def initialize 9 | @prefix = 'z:/Books/Manga' 10 | @selectors = JSON.parse(File.read(File.join(__dir__, 'config.json'))) 11 | @manga_config = config_create_new 12 | config_save 13 | end 14 | 15 | def get 16 | @manga_config 17 | end 18 | 19 | def config_save 20 | FileUtils.mkpath(config_dir) unless Dir.exist?(config_dir) 21 | out = File.open(config_path, 'w') 22 | out.puts @manga_config.to_json 23 | out.close 24 | end 25 | 26 | def normalize(title) 27 | title.gsub(%r{[\\\/\:\*\?\"\<\>\|]+}, ' - ').gsub(/\r\n/im, ' ').gsub( 28 | /[ ]{2,}/im, 29 | ' ' 30 | ) 31 | end 32 | 33 | def manga_name 34 | @manga_config['name'] 35 | end 36 | 37 | def manga_type 38 | @manga_config['ln'] ? 'Light Novel' : 'Manga' 39 | end 40 | 41 | def manga_folder 42 | @manga_config['ln'] ? 'Light Novel - WEB' : 'Manga' 43 | end 44 | 45 | def config_filename 46 | manga_name + " - #{manga_type}.json" 47 | end 48 | 49 | def config_dir 50 | File.join(@prefix, manga_name, manga_folder) 51 | end 52 | 53 | def manga_dir 54 | config_dir 55 | end 56 | 57 | def config_path 58 | File.join(config_dir, config_filename) 59 | end 60 | 61 | def config_create_new 62 | puts 'Enter title of manga: ' 63 | title = STDIN.gets.chomp 64 | while title.empty? 65 | puts 'Enter title of manga: ' 66 | title = STDIN.gets.chomp 67 | end 68 | 69 | puts 'Enter url of manga: ' 70 | url = STDIN.gets.chomp 71 | while url.empty? 72 | puts 'Enter url of manga: ' 73 | url = STDIN.gets.chomp 74 | end 75 | 76 | if @selectors.key?(URI(url).host) 77 | ln = @selectors[URI(url).host]['ln'] 78 | else 79 | puts 'Is this Light Novel? [y/n, default = n]: ' 80 | ln = STDIN.gets.chomp 81 | ln = !ln.empty? ? ln.downcase.include?('y') : false 82 | end 83 | puts "This is a #{ln ? 'Light Novel' : 'Manga'}" 84 | 85 | puts 'Enter index_start[default = 1]: ' 86 | input = STDIN.gets.chomp 87 | index_start = input.empty? ? 1 : input.to_i 88 | 89 | pre_selected = @selectors[URI(url).host]['chapter'] 90 | puts "Enter Selector for chapter[default = '#{pre_selected}']: " 91 | input = STDIN.gets.chomp 92 | chapter_selector = input.empty? ? pre_selected : input 93 | 94 | pre_selected = @selectors[URI(url).host]['page'] 95 | puts "Enter Selector for page[default = '#{pre_selected}']: " 96 | input = STDIN.gets.chomp 97 | page_selector = input.empty? ? pre_selected : input 98 | 99 | # puts "Enter Selector for cover[default = '.manga-info-pic img']: " 100 | # input = STDIN.gets.chomp 101 | # cover_selector = input.empty? ? ".manga-info-pic img" : input 102 | 103 | { 104 | 'host' => URI(url).host, 105 | 'title' => title.gsub(/\r\n/im, ' ').gsub(/[ ]{2,}/im, ' '), 106 | 'url' => url, 107 | 'ln' => ln, 108 | 'name' => normalize(title), 109 | 'chapters' => { 110 | 'index_start' => index_start, 111 | 'index_end' => 0, 112 | 'count' => 0, 113 | 'items' => [] 114 | }, 115 | 'selector' => { 'chapter' => chapter_selector, 'page' => page_selector } 116 | } 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /LNDownloader.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'mechanize' 4 | require 'nokogiri' 5 | require 'open-uri' 6 | require 'json' 7 | require 'thread' 8 | 9 | require 'http' 10 | require 'to_duration' 11 | require 'tty-logger' 12 | 13 | require_relative 'templates' 14 | 15 | class LNDownloader 16 | def initialize(options) 17 | @start = Time.now 18 | @logger = TTY::Logger.new { |config| config.level = :debug } # or "INFO" or TTY::Logger::INFO_LEVEL 19 | @prefix = 'z:/Books/Manga' 20 | @manga_config = options 21 | @download_list = [] 22 | @queue = Queue.new 23 | @threads = [] 24 | end 25 | 26 | def manga_title 27 | @manga_config['title'] 28 | end 29 | 30 | def manga_name 31 | @manga_config['name'] 32 | end 33 | 34 | def manga_type 35 | @manga_config['ln'] ? 'Light Novel' : 'Manga' 36 | end 37 | 38 | def config_filename 39 | manga_name + " - #{manga_type}.json" 40 | end 41 | 42 | def manga_folder 43 | @manga_config['ln'] ? 'Light Novel - WEB' : 'Manga' 44 | end 45 | 46 | def config_dir 47 | File.join(@prefix, manga_name, manga_folder) 48 | end 49 | 50 | def config_path 51 | File.join(config_dir, config_filename) 52 | end 53 | 54 | def config_save 55 | Dir.mkdir(config_dir) unless Dir.exist?(config_dir) 56 | out = File.open(config_path, 'w') 57 | out.puts @manga_config.to_json 58 | out.close 59 | end 60 | 61 | def log_i(txt) 62 | @logger.info(manga_name, txt) 63 | end 64 | 65 | def log(txt) 66 | @logger.debug(manga_name, txt) 67 | end 68 | 69 | def page_fetch(url) 70 | log(url) 71 | Nokogiri::HTML.parse(HTTP.headers('User-Agent' => 'Curl').get(url).to_s) 72 | end 73 | 74 | def chapter_exist(chapter) 75 | itexist = 76 | @manga_config['chapters']['items'].select do |e| 77 | e['url'] == chapter[:url] 78 | end 79 | 80 | itexist.length > 0 81 | end 82 | 83 | def config_push_chapter(chapter) 84 | if !chapter_exist(chapter) 85 | @manga_config['chapters']['items'] << chapter 86 | @manga_config['chapters']['count'] = 87 | @manga_config['chapters']['items'].length 88 | 89 | config_save 90 | end 91 | end 92 | 93 | def fetch_manga_page 94 | doc = page_fetch(@manga_config['url']) 95 | chapters = doc.css(@manga_config['selector']['chapter']).to_a 96 | log_i("Found #{chapters.length} chapters") 97 | _index = @manga_config['chapters']['index_start'].to_i 98 | chapters.map! do |e| 99 | url = e['href'] 100 | if @manga_config['host'].include?('jpmtl.com') 101 | url = 'https://' + @manga_config['host'] + e['href'] 102 | end 103 | data = { chapter: _index, title: e.text, url: url } 104 | _index = _index + 1 105 | data 106 | end 107 | end 108 | 109 | def fetch_chapter_page(url) 110 | doc = page_fetch(url) 111 | page = doc.css(@manga_config['selector']['page']).to_a 112 | 113 | page.each { |elm| elm.search('.//script').remove } 114 | 115 | page.map!(&:to_s) 116 | end 117 | 118 | def save_links_aria2c(list) 119 | download = 120 | list.flatten.map! do |url| 121 | [ 122 | url['url'], 123 | ' dir=' + url['directory'], 124 | ' out=' + url['filename'].split('&')[0] 125 | ].join("\n") 126 | end 127 | 128 | out = File.open('urls.txt', 'w') 129 | out.puts download.join("\n") 130 | out.close 131 | 132 | log('Downloading images') 133 | if !list.empty? 134 | system('aria2c --auto-file-renaming=false --continue=true -q -i urls.txt') 135 | end 136 | end 137 | 138 | def file_flush_data(filepath, data, write_type = 'w') 139 | out = File.open(filepath, write_type) 140 | out.puts data 141 | out.close 142 | end 143 | 144 | def pad_num(num) 145 | num.to_s.rjust(4, '0') 146 | end 147 | 148 | def page_html_generate(content, chapter, chapter_total) 149 | chapter_num = chapter['chapter'] 150 | 151 | buttons = [] 152 | if chapter_num > @manga_config['chapters']['index_start'] 153 | buttons << 154 | "" 157 | end 158 | buttons << 159 | "" 162 | 163 | body = "
#{content.join("\n")}
" 164 | 165 | document = TEMPLATE_HTML.gsub('{{chapter_index}}', chapter_num.to_s) 166 | document = document.gsub('{{mange_title}}', manga_title) 167 | document = document.gsub('{{button}}', buttons.join("\n")) 168 | unless manga_title.include?('Overgeared') && 169 | body.match?(/Chapter\s+\d+\-\d+/) 170 | document = document.gsub('{{body}}', body) 171 | end 172 | 173 | Dir.mkdir(config_dir) unless Dir.exist?(config_dir) 174 | 175 | html_page = "#{config_dir}/Chapter #{pad_num(chapter_num)}.html" 176 | file_flush_data(html_page, document, 'w') unless File.exist?(html_page) 177 | end 178 | 179 | def page_json_generate(content, chapter, _chapter_total) 180 | chapter_num = chapter['chapter'] 181 | 182 | document = content.to_json 183 | 184 | Dir.mkdir(config_dir) unless Dir.exist?(config_dir) 185 | 186 | html_page = "#{config_dir}/Chapter #{pad_num(chapter_num)}.json" 187 | file_flush_data(html_page, document, 'w') unless File.exist?(html_page) 188 | end 189 | 190 | def parallel 191 | 2.times do 192 | @threads << 193 | Thread.new do 194 | # loop until there are no more things to do 195 | until @queue.empty? 196 | # pop with the non-blocking flag set, this raises 197 | # an exception if the queue is empty, in which case 198 | # work_unit will be set to nil 199 | work_unit = @queue.pop 200 | if !work_unit.nil? 201 | chapter = work_unit[:data] 202 | total = work_unit[:total] 203 | chapter_url = chapter[:url] 204 | chapter_content = fetch_chapter_page(chapter_url) 205 | 206 | chapter_item = { 207 | 'url' => chapter[:url], 208 | 'chapter' => chapter[:chapter], 209 | 'title' => chapter[:title] 210 | } 211 | chapter_item_json = { 212 | 'url' => chapter[:url], 213 | 'chapter' => chapter[:chapter], 214 | 'title' => chapter[:title], 215 | 'items' => chapter_content 216 | } 217 | 218 | config_push_chapter(chapter_item) 219 | 220 | page_html_generate(chapter_content, chapter_item, total) 221 | page_json_generate(chapter_item_json, chapter_item, total) 222 | end 223 | end # when there is no more work, the thread will stop 224 | end 225 | end 226 | 227 | # wait until all threads have completed processing 228 | @threads.each(&:join) 229 | end 230 | 231 | def download 232 | log('Getting manga page') 233 | 234 | chapters = fetch_manga_page 235 | 236 | @manga_config['chapters']['index_end'] = chapters.length 237 | 238 | if chapters.length > @manga_config['chapters']['count'] 239 | 240 | else 241 | log('No new chapters found') 242 | return false 243 | end 244 | 245 | chapters.select! { |e| !chapter_exist(e) } 246 | chapters.each { |e| @queue << { data: e, total: chapters.length } } 247 | 248 | parallel 249 | 250 | config_save 251 | log("Finish downloading in #{(Time.now - @start)}s") 252 | 253 | return true 254 | end 255 | end 256 | -------------------------------------------------------------------------------- /MangaDownloader.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'mechanize' 4 | require 'nokogiri' 5 | require 'open-uri' 6 | require 'json' 7 | 8 | require 'http' 9 | require 'to_duration' 10 | require 'tty-logger' 11 | 12 | require_relative 'templates' 13 | 14 | class MangaDownloader 15 | def initialize(config) 16 | @start = Time.now 17 | @logger = TTY::Logger.new do |config| 18 | config.level = :debug # or "INFO" or TTY::Logger::INFO_LEVEL 19 | end 20 | @prefix = 'z:/Books/Manga' 21 | @manga_config = config 22 | @download_list = [] 23 | @queue = Queue.new 24 | @threads = [] 25 | end 26 | 27 | def manga_title 28 | @manga_config['title'] 29 | end 30 | 31 | def manga_name 32 | @manga_config['name'] 33 | end 34 | 35 | def manga_type 36 | @manga_config['ln'] ? 'Light Novel' : 'Manga' 37 | end 38 | 39 | def config_filename 40 | manga_name + " - #{manga_type}.json" 41 | end 42 | 43 | def config_dir 44 | File.join(@prefix, manga_name, manga_type) 45 | end 46 | 47 | def manga_dir 48 | config_dir 49 | end 50 | 51 | def config_path 52 | File.join(config_dir, config_filename) 53 | end 54 | 55 | def config_save 56 | Dir.mkdir(config_dir) if !Dir.exist?(config_dir) 57 | out = File.open(config_path, 'w') 58 | out.puts @manga_config.to_json 59 | out.close 60 | end 61 | 62 | def log_i(txt) 63 | @logger.info(manga_name, txt) 64 | end 65 | 66 | def log(txt) 67 | @logger.debug(manga_name, txt) 68 | end 69 | 70 | def page_fetch(url) 71 | html = HTTP.get(url, { 72 | headers: {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'} 73 | }).to_s 74 | Nokogiri::HTML( 75 | html 76 | .gsub('', '') 78 | .gsub('data-src=', 'src=') 79 | ) 80 | end 81 | 82 | def chapter_exist(chapter) 83 | itexist = 84 | @manga_config['chapters']['items'].select do |e| 85 | e['url'] == chapter[:url] 86 | end 87 | 88 | itexist.length > 0 89 | end 90 | 91 | def config_push_chapter(chapter) 92 | if !chapter_exist(chapter) 93 | @manga_config['chapters']['items'] << chapter 94 | @manga_config['chapters']['count'] = 95 | @manga_config['chapters']['items'].length 96 | end 97 | end 98 | 99 | def fetch_manga_page 100 | doc = page_fetch(@manga_config['url']) 101 | chapters = doc.css(@manga_config['selector']['chapter']).to_a.reverse 102 | log_i("Found #{chapters.length} chapters") 103 | index = @manga_config['chapters']['index_start'].to_i 104 | chapters.map! do |e| 105 | data = { 106 | chapter: index, 107 | title: e['title'], 108 | url: e['href'].gsub('?style=paged', '?style=list') 109 | } 110 | index += 1 111 | data 112 | end 113 | end 114 | 115 | def fetch_chapter_page(url) 116 | doc = page_fetch(url) 117 | img = doc.css(@manga_config['selector']['page']).to_a 118 | img = doc.css(@manga_config['selector']['page1']).to_a if img.length < 3 119 | 120 | index = 0 121 | images = [] 122 | img.each do |e| 123 | index += 1 124 | 125 | images << { number: index, url: e['src'] } 126 | end 127 | 128 | images 129 | end 130 | 131 | def save_links_aria2c(list) 132 | download = 133 | list.flatten.map! do |url| 134 | [ 135 | url['url'], 136 | ' dir=' + url['directory'], 137 | ' out=' + url['filename'].split('&')[0] 138 | ].join("\n") 139 | end 140 | 141 | out = File.open('urls.txt', 'a') 142 | out.puts download.join("\n") 143 | out.close 144 | 145 | log('Downloading images') 146 | if !list.empty? 147 | system( 148 | 'aria2c --check-certificate=false --auto-file-renaming=false --continue=true -j8 -x16 -i urls.txt' 149 | ) 150 | end 151 | end 152 | 153 | def file_flush_data(filename, data, write_type = 'w') 154 | out = File.open(File.join(@prefix, filename), write_type) 155 | out.puts data 156 | out.close 157 | end 158 | 159 | def pad_num(num) 160 | num.to_s.rjust(4, '0') 161 | end 162 | 163 | def page_html_generate(images, chapter, chapter_total) 164 | chapter_num = chapter['chapter'] 165 | 166 | buttons = [] 167 | if chapter_num > @manga_config['chapters']['index_start'] 168 | buttons << 169 | "" 172 | end 173 | buttons << 174 | "" 177 | 178 | img = 179 | images.map do |e| 180 | "" 183 | end 184 | 185 | document = TEMPLATE_HTML.gsub('{{chapter_index}}', chapter_num.to_s) 186 | document = document.gsub('{{mange_title}}', manga_title) 187 | document = document.gsub('{{button}}', buttons.join("\n")) 188 | document = document.gsub('{{body}}', img.join("\n")) 189 | 190 | Dir.mkdir(manga_dir) unless Dir.exist?(manga_dir) 191 | 192 | html_page = 193 | "#{manga_name}/#{manga_type}/Chapter #{pad_num(chapter_num)}.html" 194 | file_flush_data(html_page, document, 'w') unless File.exist?(html_page) 195 | end 196 | 197 | def parallel 198 | 1.times do 199 | @threads << 200 | Thread.new do 201 | # loop until there are no more things to do 202 | until @queue.empty? 203 | # pop with the non-blocking flag set, this raises 204 | # an exception if the queue is empty, in which case 205 | # work_unit will be set to nil 206 | work_unit = 207 | begin 208 | @queue.pop(true) 209 | rescue StandardError 210 | nil 211 | end 212 | if work_unit 213 | chapter = work_unit[:data] 214 | total = work_unit[:total] 215 | chapter_url = chapter[:url] 216 | chapter_images = fetch_chapter_page(chapter_url) 217 | log( 218 | "Chapter #{chapter[:chapter]} has #{ 219 | chapter_images.length 220 | } pages" 221 | ) 222 | 223 | chapter_item = { 224 | 'url' => chapter[:url], 225 | 'chapter' => chapter[:chapter], 226 | 'title' => chapter[:title], 227 | 'items' => chapter_images, 228 | 'count' => chapter_images.length 229 | } 230 | 231 | config_push_chapter(chapter_item) 232 | 233 | chapter_images.each do |image| 234 | @download_list << 235 | { 236 | 'directory' => 237 | "#{@prefix}/#{manga_name}/#{manga_type}/Chapter #{ 238 | pad_num(chapter[:chapter]) 239 | }", 240 | 'filename' => "Page #{image[:number]}.jpg", 241 | 'url' => image[:url] 242 | } 243 | end 244 | 245 | page_html_generate(chapter_images, chapter_item, total) 246 | end 247 | end # when there is no more work, the thread will stop 248 | end 249 | end 250 | 251 | # wait until all threads have completed processing 252 | @threads.each(&:join) 253 | end 254 | 255 | def download 256 | log('Getting manga page') 257 | 258 | chapters = fetch_manga_page 259 | 260 | @manga_config['chapters']['index_end'] = chapters.length 261 | 262 | if chapters.length > @manga_config['chapters']['count'] 263 | 264 | else 265 | log('No new chapters found') 266 | return false 267 | end 268 | 269 | chapters.reject! { |e| chapter_exist(e) } 270 | chapters.each { |e| @queue << { data: e, total: chapters.length } } 271 | 272 | parallel 273 | 274 | log("Found #{@download_list.length} images for download") 275 | 276 | config_save 277 | save_links_aria2c(@download_list) 278 | 279 | log("Finish downloading in #{(Time.now - @start)}s") 280 | 281 | true 282 | end 283 | end 284 | --------------------------------------------------------------------------------