mangdown 0.13.4 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b46ede1b55f92c1ff03e43c64890bcb77f98838
4
- data.tar.gz: ce4927534522cc6bf0ab39deece40bb58d7524d8
3
+ metadata.gz: ceaa6642dfcb70513d084ec3b636d2c0855a9928
4
+ data.tar.gz: e6f2920b7f48afb586aeb038543a2f32ac6e1267
5
5
  SHA512:
6
- metadata.gz: 669578148a673bdd314a4f49cb541947e473d9286e907f51effc28e298d877ac56f68c44830c4c6b123732deacb289395d96bf13245d5f2db28d8e61305748f1
7
- data.tar.gz: c4e48d29330badd3ebd2166262b6fa62783b0ce614ccac4907ec7b7e2c4259618aa16c8535d0498770451ee507bc5f8f33f599757235573063eb0c5145e7a0c9
6
+ metadata.gz: e6ef238870bac0cd688769574919aea5ab8dffcd59b52f17ce1cbe315310179394c572972ffa34045a036a4f1d7eb200700a12ff9fb2e00756bb8b3afceddc8a
7
+ data.tar.gz: f9c15ca24c70f1abd1eabbe33af7a90ed68f300fe5397877a8a76ab3ee1966d9cd2e2b4337871a637c0a0dcd3bbbbc6c86b6df7bd1feefe2dc3933849ae3f606
data/README.md CHANGED
@@ -2,9 +2,16 @@
2
2
 
3
3
  There is a simple built-in client, "M", that you can use for finding manga:
4
4
 
5
- ```
5
+ ```ruby
6
+ require 'mangdown/client'
7
+
8
+ # Search for an exact match
6
9
  results = M.find("Dragon Ball")
7
10
 
11
+ # Or if you need more flexibilty when searching for a manga,
12
+ # use are Regex
13
+ results = M.find(/dragon ball(\ssd)?$/i)
14
+
8
15
  # Get a Mangdown::Manga object
9
16
  manga = results.first.to_manga
10
17
 
@@ -1,54 +1,118 @@
1
1
  module Mangdown
2
2
  class Mangareader < Adapter::Base
3
- Mangdown::ADAPTERS << self
3
+
4
+ site :mangareader
5
+
6
+ attr_reader :root
4
7
 
5
8
  def initialize(uri, doc, name)
6
9
  super
7
- @root ||= 'http://www.mangareader.net'
8
- @manga_list_css = 'ul.series_alpha li a'
9
- @manga_name_css = 'h2.aname'
10
- @chapter_list_css = 'div#chapterlist td a'
11
- @manga_list_uri = "#{@root}/alphabetical"
12
- @manga_link_prefix = @root
13
- @reverse_chapters = false
14
- @manga_uri_regex =
15
- /#{@root}(\/\d+)?(\/[^\/]+)(\.html)?/i
16
- @chapter_uri_regex =
17
- /#{@root}(\/[^\/]+){1,2}\/(\d+|chapter-\d+\.html)/i
18
- @page_uri_regex = /.+\.(png|jpg|jpeg)$/i
10
+
11
+ @root = 'http://www.mangareader.net'
19
12
  end
20
13
 
21
- def manga_name
22
- CGI.unescapeHTML(super)
14
+ def is_manga_list?(uri = @uri)
15
+ uri == "#{root}/alphabetical"
23
16
  end
24
17
 
25
- def build_page_uri(uri, manga, chapter, page_num)
26
- slug = slug(manga)
27
- "#{root}/#{slug}/#{chapter}/#{page_num}"
18
+ def is_manga?(uri = @uri)
19
+ uri.slice(/#{root}(\/\d+)?(\/[^\/]+)(\.html)?/i) == uri
28
20
  end
29
21
 
30
- def num_pages
31
- doc.css('select')[1].css('option').length
22
+ def is_chapter?(uri = @uri)
23
+ uri.slice(/#{root}(\/[^\/]+){1,2}\/(\d+|chapter-\d+\.html)/i) == uri
32
24
  end
33
25
 
34
- def page_image_src
35
- page_image[:src]
26
+ def is_page?(uri = @uri)
27
+ uri.slice(/.+\.(png|jpg|jpeg)$/i) == uri
28
+ end
29
+
30
+ # Only valid mangas should be returned (using is_manga?(uri))
31
+ def manga_list
32
+ doc.css('ul.series_alpha li a').map { |a|
33
+ uri = "#{root}#{a[:href]}"
34
+ manga = { uri: uri, name: a.text.strip, site: site }
35
+
36
+ manga if is_manga?(uri)
37
+ }.compact
36
38
  end
37
39
 
38
- def page_image_name
39
- page_image[:alt].sub(/([^\d]*)(\d+)(\.\w+)?$/) {
40
- "#{Regexp.last_match[1]}" +
41
- "#{Regexp.last_match[2].to_s.rjust(3, '0')}"
40
+ def manga
41
+ { uri: uri, name: manga_name, site: site }
42
+ end
43
+
44
+ # Only valid chapters should be returned (using is_chapter?(uri))
45
+ def chapter_list
46
+ doc.css('div#chapterlist td a').map { |a|
47
+ uri = root + a[:href].sub(root, '')
48
+ chapter = { uri: uri, name: a.text.strip, site: site }
49
+
50
+ chapter if is_chapter?(uri)
51
+ }.compact
52
+ end
53
+
54
+ def chapter
55
+ { uri: uri,
56
+ manga: manga_name,
57
+ name: chapter_name,
58
+ chapter: chapter_number,
59
+ site: site }
60
+ end
61
+
62
+ def page_list
63
+ last_page = doc.css('select')[1].css('option').length
64
+ (1..last_page).map { |page|
65
+ slug = manga_name.gsub(' ', '-').gsub(/[:,]/, '')
66
+ uri = "#{root}/#{slug}/#{chapter_number}/#{page}"
67
+ uri = Mangdown::Uri.new(uri).downcase
68
+
69
+ { uri: uri, name: page, site: site } }
70
+ end
71
+
72
+ def page
73
+ page_image = doc.css('img')[0]
74
+ uri = page_image[:src]
75
+ name = page_image[:alt].sub(/([^\d]*)(\d+)(\.\w+)?$/) {
76
+ Regexp.last_match[1].to_s + Regexp.last_match[2].to_s.rjust(3, '0')
42
77
  }
78
+
79
+ { uri: uri, name: name, site: site }
43
80
  end
44
81
 
45
82
  private
46
- def page_image
47
- doc.css('img')[0]
83
+
84
+ def manga_name
85
+ if is_manga?
86
+ name = doc.css('h2.aname').text
87
+ elsif is_chapter?
88
+ name = chapter_manga_name
89
+ end
90
+
91
+ CGI.unescapeHTML(name) if name
92
+ end
93
+
94
+ def chapter_name
95
+ if @name
96
+ @name.sub(/\s(\d+)$/) { |num| ' ' + num.to_i.to_s.rjust(5, '0') }
97
+ else
98
+ doc.css("").text # Not implimented
99
+ end
100
+ end
101
+
102
+ def chapter_manga_name
103
+ if @name
104
+ @name.slice(/(^.+)\s/, 1)
105
+ else
106
+ doc.css("").text # Not implimented
107
+ end
48
108
  end
49
109
 
50
- def slug(string)
51
- string.gsub(' ', '-').gsub(/[:,]/, '')
110
+ def chapter_number
111
+ if @name
112
+ @name.slice(/\d+\z/).to_i
113
+ else
114
+ doc.css("").text # Not implimented
115
+ end
52
116
  end
53
117
  end
54
118
  end
@@ -2,7 +2,7 @@ module Mangdown
2
2
  module Adapter
3
3
  class NoAdapterError < StandardError
4
4
  def initialize(site)
5
- super("Bad Site: No Properties Specified for Site: #{site.inspect}")
5
+ super("Bad Site: No Adapter Specified for Site: #{site.inspect}")
6
6
  end
7
7
  end
8
8
  end
@@ -1,143 +1,81 @@
1
1
  module Mangdown
2
2
  module Adapter
3
3
  class Base
4
-
5
- attr_reader :root
6
- def initialize(uri, doc, name)
7
- @uri, @doc, @name = uri, doc, name
8
- #@root = ''
9
- #@manga_list_css = ''
10
- #@chapter_list_css = ''
11
- #@manga_name_css = ''
12
- #@chapter_name_css = ''
13
- #@chapter_manga_name_css = ''
14
- #@chapter_number_css = ''
15
- #@manga_list_uri = ''
16
- #@manga_link_prefix = ''
17
- #@reverse_chapters = true || false
18
- #@manga_uri_regex = /.*/i
19
- #@chapter_uri_regex = /.*/i
20
- #@page_uri_regex = /.*/i
21
- end
22
4
 
23
- def self.type
24
- name.split('::').last.downcase.to_sym
5
+ # Returns something truthy if this adapter should be used for the
6
+ # given url or adapter name
7
+ def self.for?(url_or_adapter_name)
8
+ url_or_adapter_name[site.to_s]
25
9
  end
26
10
 
27
- def type
28
- self.class.type
11
+ def self.site(site = nil)
12
+ site ? @_site = site : @_site
29
13
  end
30
-
31
- # Override this if you want to use an adapter name for a site that is
32
- # not matched in the site's url
33
- # e.g. CoolAdapterName < Adapter::Base
34
- # def site
35
- # "mangareader"
36
- # end
37
- def self.site
38
- type.to_s
14
+
15
+ attr_reader :uri, :doc, :name
16
+ def initialize(uri, doc, name)
17
+ @uri = uri
18
+ @doc = doc
19
+ @name = name
39
20
  end
40
21
 
41
22
  def site
42
23
  self.class.site
43
24
  end
44
25
 
26
+ # Overwrite if you want to check the uri if it belongs to a manga list
27
+ def is_manga_list?(uri = @uri)
28
+ raise NotImplementedError
29
+ end
30
+
45
31
  # Must return true/false if uri represents a manga for adapter
46
32
  def is_manga?(uri = @uri)
47
- uri.slice(@manga_uri_regex) == uri
33
+ raise NotImplementedError
48
34
  end
49
35
 
50
36
  # Must return true/false if uri represents a chapter for adapter
51
37
  def is_chapter?(uri = @uri)
52
- uri.slice(@chapter_uri_regex) == uri
38
+ raise NotImplementedError
53
39
  end
54
40
 
55
41
  # Must return true/false if uri represents a page for adapter
56
42
  def is_page?(uri = @uri)
57
- uri.slice(@page_uri_regex) == uri
58
- end
59
-
60
- # Must return a string
61
- def manga_name
62
- if is_manga?
63
- doc.css(@manga_name_css).text
64
- elsif is_chapter?
65
- chapter_manga_name
66
- end
67
- end
68
-
69
- def chapter_name
70
- if @name
71
- @name.sub(/\s(\d+)$/) { |num| ' ' + num.to_i.to_s.rjust(5, '0') }
72
- else
73
- doc.css(@chapter_name_css).text
74
- end
75
- end
76
-
77
- def chapter_manga_name
78
- if @name
79
- @name.slice(/(^.+)\s/, 1)
80
- else
81
- doc.css(@chapter_manga_name_css).text
82
- end
83
- end
84
-
85
- def chapter_number
86
- raise NoMethodError, "Not a chapter" unless is_chapter?
87
- if @name
88
- @name.slice(/\d+\z/).to_i
89
- else
90
- doc.css(@chapter_number_css).text
91
- end
43
+ raise NotImplementedError
92
44
  end
93
45
 
94
- # Must return a uri for a page given the arguments
95
- def build_page_uri(uri, manga, chapter, page_num)
46
+ # Return Array of Hash with keys: :uri, :name, :site
47
+ def manga_list
48
+ raise NotImplementedError
96
49
  end
97
50
 
98
- # Must return the number of pages for a chapter
99
- def num_pages
51
+ # Return Hash with keys: :uri, :name, :site
52
+ def manga
53
+ raise NotImplementedError
100
54
  end
101
55
 
102
- # Must return the src for the page image
103
- def page_image_src
56
+ # Return Array of Hash with keys: :uri, :name, :site
57
+ def chapter_list
58
+ raise NotImplementedError
104
59
  end
105
60
 
106
- # Must return the name of the page image
107
- def page_image_name
61
+ # Return Hash with keys: :uri, :name, :chapter, :manga, :site
62
+ def chapter
63
+ raise NotImplementedError
108
64
  end
109
65
 
110
- # If no block given, must return an array arrays
111
- # [manga_uri, manga_name, adapter_type]
112
- # If block given, then the block may alter this array
113
- # Only valid mangas should be returned (using is_manga?(uri))
114
- def collect_manga_list
115
- doc.css(@manga_list_css).map { |a|
116
- manga = ["#{@manga_link_prefix}#{a[:href]}", a.text.strip, type]
117
- if is_manga?(manga.first)
118
- block_given? ? yield(manga) : manga
119
- end
120
- }.compact
66
+ # Return Array of Hash with keys: :uri, :name, :site
67
+ def page_list
68
+ raise NotImplementedError
121
69
  end
122
70
 
123
- # If no block given, must return an array arrays
124
- # [chapter_uri, chapter_name, adapter_type]
125
- # If block given, then the block may alter this array
126
- # Only valid chapters should be returned (using is_chapter?(uri))
127
- def manga_chapters
128
- chapters = doc.css(@chapter_list_css).map { |a|
129
- link = root + a[:href].sub(root, '')
130
- chapter = [link, a.text.strip, type]
131
- if is_chapter?(chapter.first)
132
- block_given? ? yield(chapter) : chapter
133
- end
134
- }.compact
135
- @reverse_chapters ? chapters.reverse : chapters
71
+ # Return Hash with keys: :uri, :name, :site
72
+ def page
73
+ raise NotImplementedError
136
74
  end
137
75
 
138
76
  private
139
77
  def doc
140
- @doc ||= Tools.get_doc(@uri)
78
+ @doc ||= Tools.get_doc(uri)
141
79
  end
142
80
  end
143
81
  end
@@ -4,35 +4,21 @@ module Mangdown
4
4
  include Equality
5
5
  include Enumerable
6
6
 
7
- attr_reader :uri, :pages
7
+ attr_reader :uri, :pages, :name, :manga, :chapter
8
+ attr_accessor :adapter
8
9
 
9
- def initialize(uri, name = nil, manga = nil, chapter = nil)
10
- # use a valid name
10
+ def initialize(uri, name, manga, chapter = nil)
11
11
  @name = name
12
12
  @manga = manga
13
13
  @chapter = chapter
14
14
  @uri = Mangdown::Uri.new(uri)
15
- @properties = Properties.new(@uri, nil, nil, name)
16
-
17
- load_pages
18
- end
19
-
20
- def name
21
- @name ||= @properties.chapter_name
22
- end
23
-
24
- def manga
25
- @manga ||= @properties.manga_name
26
- end
27
-
28
- def chapter
29
- @chapter ||= @properties.chapter_number
15
+ @pages = []
30
16
  end
31
17
 
32
18
  def inspect
33
19
  "#<#{self.class} @name=#{name} @uri=#{uri} " +
34
- "@pages=[#{pages.first(10).join(',')}" +
35
- "#{",..." if pages.length > 10}]>"
20
+ "@pages=[#{pages.first(3).join(',')}" +
21
+ "#{",..." if pages.length > 3}]>"
36
22
  end
37
23
  alias_method :to_s, :inspect
38
24
 
@@ -94,19 +80,19 @@ module Mangdown
94
80
  { failed: failed, succeeded: succeeded, skipped: skipped }
95
81
  end
96
82
 
97
- private
98
- def setup_download_dir!(dir)
99
- set_path(dir)
100
- FileUtils.mkdir_p(to_path) unless Dir.exists?(to_path)
101
- end
102
-
103
83
  def load_pages
104
- @pages ||= []
84
+ return @pages if @pages.any?
105
85
 
106
86
  fetch_each_page do |page| @pages << page end
107
87
  @pages.sort_by!(&:name)
108
88
  end
109
89
 
90
+ private
91
+ def setup_download_dir!(dir)
92
+ set_path(dir)
93
+ FileUtils.mkdir_p(to_path) unless Dir.exists?(to_path)
94
+ end
95
+
110
96
  # get page objects for all pages in a chapter
111
97
  def fetch_each_page
112
98
  pages = build_page_hashes
@@ -118,21 +104,19 @@ module Mangdown
118
104
 
119
105
  # get the docs for number of pages
120
106
  def build_page_hashes
121
- (1..@properties.num_pages).map { |num|
122
- uri_str = @properties.build_page_uri(uri, manga, chapter, num)
123
- uri = Mangdown::Uri.new(uri_str).downcase
124
- MDHash.new(uri: uri, name: num, chapter: name, manga: manga)
125
- }
107
+ adapter.page_list.map { |page|
108
+ page.merge!(chapter: name, manga: manga)
109
+ MDHash.new(page) }
126
110
  end
127
111
 
128
112
  # get the page name and uri
129
113
  def get_page(uri, doc)
130
- properties = Properties.new(uri, nil, doc)
131
- uri = properties.page_image_src
132
- page = properties.page_image_name
133
- site = properties.type
114
+ # Local binding for adapter
115
+ adapter = Mangdown.adapter!(uri, nil, doc)
116
+ page = adapter.page
117
+ page.merge!(chapter: name, manga: manga)
134
118
 
135
- MDHash.new(uri: uri, name: page, chapter: name, manga: manga, site: site)
119
+ MDHash.new(page)
136
120
  end
137
121
  end
138
122
  end
@@ -1,35 +1,25 @@
1
+ require_relative '../mangdown'
2
+
1
3
  module M
2
4
  extend self
3
5
 
4
- include ::Mangdown
5
-
6
6
  DATA_FILE_PATH = Dir.home + '/.manga_list.yml'
7
7
  HELP_FILE_PATH = File.expand_path(
8
8
  '../../README.md', File.dirname(__FILE__)
9
9
  )
10
- =begin
11
- MANGA_PAGES = (1..9).map { |p|
12
- "http://www.wiemanga.com/search/?name_sel=contain" +
13
- "&author_sel=contain&completed_series=either&page=#{p}.html"
14
- } +
15
- [
16
- 'http://www.mangareader.net/alphabetical',
17
- 'http://mangafox.me/manga/'
18
- ]
19
- =end
10
+
20
11
  MANGA_PAGES = ['http://www.mangareader.net/alphabetical']
21
12
 
22
13
  # return a list of hash with :uri and :name of mangas found in list
23
14
  def find(search)
24
- validate_search(search)
25
- current_manga_list.mangas.select { |manga|
26
- manga[:name].downcase.include?(search.downcase)
27
- }
15
+ search = Regexp.new(/^#{search}$/) if search.respond_to?(:to_str)
16
+
17
+ current_manga_list.select { |manga| manga[:name] =~ search }
28
18
  end
29
19
 
30
20
  # cbz all subdirectories in a directory
31
21
  def cbz(dir)
32
- Dir.exist?(dir) ? CBZ.all(dir) : raise(Errno::ENOENT, dir)
22
+ Dir.exist?(dir) ? Mangdown::CBZ.all(dir) : raise(Errno::ENOENT, dir)
33
23
  end
34
24
 
35
25
  # display help file
@@ -62,20 +52,15 @@ module M
62
52
  # otherwise fetch new data and write it to the data file
63
53
  def current_manga_list
64
54
  data = data_from_file
65
- return MangaList.from_data(data) if data
55
+ return Mangdown::MangaList.from_data(data) if data
66
56
 
67
- MangaList.new(*MANGA_PAGES).tap { |list|
68
- File.open(path, 'w+') { |f| f.write(list.to_yaml) }
69
- }
57
+ list = MANGA_PAGES.inject([]) { |manga, uri|
58
+ list = Mangdown::MDHash.new(uri: uri).to_manga_list
59
+ list.merge(manga) }
60
+ File.open(path, 'w+') { |f| f.write(list.to_yaml) }
61
+ list
70
62
  rescue Object => error
71
63
  puts "#{path} may be corrupt: #{error.message}"
72
64
  raise
73
65
  end
74
-
75
- # check if the search key contains letters or numbers
76
- def validate_search(search)
77
- unless search =~ /\w/
78
- raise ArgumentError, "Searches must contain letters and numbers"
79
- end
80
- end
81
66
  end
@@ -7,25 +7,19 @@ module Mangdown
7
7
  include Equality
8
8
  include Enumerable
9
9
 
10
- attr_reader :uri, :chapters
10
+ attr_reader :uri, :chapters, :name
11
+ attr_accessor :adapter
11
12
 
12
- def initialize(uri, name = nil)
13
+ def initialize(uri, name)
13
14
  @name = name
14
15
  @uri = Mangdown::Uri.new(uri)
15
16
  @chapters = []
16
- @properties = Properties.new(uri)
17
-
18
- get_chapters
19
- end
20
-
21
- def name
22
- @name || @properties.manga_name
23
17
  end
24
18
 
25
19
  def inspect
26
20
  "#<#{self.class} @name=#{name} @uri=#{uri} " +
27
- "@chapters=[#{chapters.first(10).join(',')}" +
28
- "#{",..." if chapters.length > 10}]>"
21
+ "@chapters=[#{chapters.first(3).join(',')}" +
22
+ "#{",..." if chapters.length > 3}]>"
29
23
  end
30
24
  alias_method :to_s, :inspect
31
25
 
@@ -42,17 +36,29 @@ module Mangdown
42
36
  def download_to(dir, start = 0, stop = -1, opts = { force_download: false })
43
37
  start, stop = validate_indeces!(start, stop)
44
38
  setup_download_dir!(dir)
39
+ failed = []
40
+ succeeded = []
41
+ skipped = []
45
42
 
46
43
  bar = progress_bar(start, stop)
47
44
  chapters[start..stop].each do |md_hash|
48
45
  chapter = md_hash.to_chapter
49
-
50
- if chapter.download_to(to_path, opts)
51
- bar.increment!
46
+ chapter_result = chapter.download_to(to_path, opts)
47
+
48
+ if chapter_result[:failed].any?
49
+ failed << chapter
50
+ elsif chapter_result[:succeeded].any?
51
+ succeeded << chapter
52
+ elsif chapter_result[:skipped].any?
53
+ skipped << chapter
54
+ end
55
+ if chapter_result[:failed].any?
56
+ STDERR.puts("error: #{chapter.name} was not fully downloaded")
52
57
  else
53
- STDERR.puts("error: #{chapter.name} was not downloaded")
58
+ bar.increment!
54
59
  end
55
60
  end
61
+ { failed: failed, succeeded: succeeded, skipped: skipped }
56
62
  end
57
63
 
58
64
  # explicit conversion to manga
@@ -75,14 +81,14 @@ module Mangdown
75
81
  @chapters.each(&block)
76
82
  end
77
83
 
78
- private
79
84
  # push MDHashes of manga chapters to @chapters
80
- def get_chapters
81
- @chapters += @properties.manga_chapters do |uri, chapter, site|
82
- MDHash.new(uri: uri, name: chapter, manga: name, site: site)
83
- end
85
+ def load_chapters
86
+ @chapters += adapter.chapter_list.map { |chapter|
87
+ chapter.merge!(manga: name)
88
+ MDHash.new(chapter) }
84
89
  end
85
90
 
91
+ private
86
92
  def chapter_indeces(start, stop)
87
93
  length = chapters.length
88
94
  [start, stop].map { |i| i < 0 ? length + i : i }
@@ -1,32 +1,41 @@
1
1
  module Mangdown
2
-
3
- # a list of manga
4
2
  class MangaList
3
+ include Enumerable
5
4
 
6
- def self.from_data(mangas)
7
- new(nil, mangas: mangas)
5
+ def self.from_data(manga)
6
+ new(manga)
8
7
  end
9
8
 
10
- attr_reader :mangas
9
+ attr_reader :manga
11
10
 
12
- def initialize(*uris, mangas: [])
13
- @mangas = mangas.map! { |hash| MDHash.new(hash) }
14
- if uris.any?
15
- uris.each { |uri| get_mangas(uri) }
16
- end
11
+ def initialize(manga = [])
12
+ @manga = manga.map! { |hash| MDHash.new(hash) }
17
13
  end
18
14
 
15
+ def each(&block)
16
+ manga.each(&block)
17
+ end
18
+
19
19
  def to_yaml
20
- @mangas.map(&:to_hash).to_yaml
20
+ @manga.map(&:to_hash).to_yaml
21
21
  end
22
22
 
23
- private
24
- # get a list of mangas from the uri
25
- def get_mangas(uri)
23
+ def load_manga(uri)
24
+ adapter = Mangdown.adapter!(uri)
25
+
26
+ manga = adapter.manga_list.map { |manga| MDHash.new(manga) }
26
27
 
27
- @mangas += Properties.new(uri).collect_manga_list { |uri, name, site|
28
- MDHash.new(uri: uri, name: name, site: site)
29
- }
28
+ merge(manga)
29
+ end
30
+
31
+ def to_a
32
+ manga
33
+ end
34
+
35
+ def merge(other)
36
+ @manga += other.to_a
37
+
38
+ return self
30
39
  end
31
40
  end
32
41
  end