my_manga 1.1.6

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: e8c579b52a739f5a0468d82ab037729cd63c45ef
4
+ data.tar.gz: 3650970a5fff6c2e24e8cc2549be74f8ef4482fd
5
+ SHA512:
6
+ metadata.gz: 7a87ba326ae963a5489b0708bce458b183fafcd75cba61fbbfe6cb53cc285131c23014d072ca3b30b2f078a703421645de061d67236ef91973033bc78ef40dcc
7
+ data.tar.gz: 94a60cd4dfd2a629edc8aea1d727ae39057fa3d5f61ad34e5d39bc6c0fa48a559a68151bbbfd0d9e399117d0b83d19f9dc571f3cf873e2dd844406b8f31f6fc6
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 John Hager
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,145 @@
1
+ TODO: Don't test cli with binary. Test the lib.
2
+ TODO: Check out http://piotrmurach.github.io/tty/gems/ to make this gem better
3
+
4
+ ```
5
+ my_manga find "assassination classroom"
6
+
7
+ Manga found for "assassination classroom"
8
+ =========================================
9
+ Name Url
10
+ Assasination Classroom http://www.mangareader.net/assassination-classroom
11
+
12
+ => nil
13
+
14
+ my_manga add http://www.mangareader.net/assassination-classroom
15
+
16
+ "Assassination Classroom" added to your library!
17
+
18
+ => nil
19
+
20
+ my_manga remove "Assassination Classroom"
21
+
22
+ "Assassination Classroom" removed from your library!
23
+
24
+ => nil
25
+
26
+ my_manga list
27
+
28
+ Manga list
29
+ =======================================
30
+ Name Chapters (read/total)
31
+ Assassination Classroom 0/161
32
+ Naruto 669/700
33
+ Naruto Movie 10/10
34
+
35
+ => nil
36
+
37
+ my_manga download
38
+
39
+ Downloading 161 Chapters from "Assassination Classroom"
40
+ Downloading 1 Chapters from "Naruto"
41
+ ...
42
+ Finished Download!
43
+
44
+ => nil
45
+
46
+ my_manga download "Assassination Classroom"
47
+
48
+ Downloading 0 Chapters from "Assassination Classroom"
49
+ Finished Download!
50
+
51
+ => nil
52
+
53
+ my_manga download "Assassination Classroom" --from=1 --to=10
54
+
55
+ Downloading 10 Chapters from "Assassination Classroom"
56
+ Finished Download!
57
+
58
+ => nil
59
+
60
+ my_manga download "Assassination Classroom" --list=[11,13,15]
61
+
62
+ Downloading 3 Chapters from "Assassination Classroom"
63
+ Finished Download!
64
+
65
+ => nil
66
+
67
+ my_manga update
68
+
69
+ Fetching Manga
70
+ ...
71
+ Updated "Assassination Classroom": 5 new Chapters.
72
+
73
+ => nil
74
+
75
+ my_manga update "Assassination Classroom"
76
+
77
+ Fetching Manga
78
+ ...
79
+ Nothing to Update
80
+
81
+ => nil
82
+
83
+ my_manga list "Assasination Classroom"
84
+
85
+ Manga details for "Assasination Classroom"
86
+ =========================================
87
+ Name Chapters (read/total)
88
+ Assassination Classroom 161/166
89
+
90
+ Chapters Read
91
+ -------------
92
+ Assasination Classroom 1
93
+ Assasination Classroom 2
94
+ Assasination Classroom 3
95
+ ...
96
+ Assasination Classroom 161
97
+
98
+ => nil
99
+
100
+ my_manga list -a "Naruto Movie"
101
+
102
+ Manga details for "Naruto Movie"
103
+ =========================================
104
+ Name Chapters (read/total)
105
+ Naruto Movie 161/166
106
+
107
+ Chapters Read
108
+ -------------
109
+ Naruto Movie 1
110
+ Naruto Movie 2
111
+ Naruto Movie 3
112
+ Naruto Movie 4
113
+ Naruto Movie 5
114
+ Naruto Movie 6
115
+ Naruto Movie 7
116
+ Naruto Movie 8
117
+ Naruto Movie 9
118
+ Naruto Movie 10
119
+
120
+ => nil
121
+
122
+ my_manga mark read "Assassination Classroom" --from=162 --to=165
123
+
124
+ Chapters 162-165 from "Assination Classroom" Marked as Read
125
+
126
+ => nil
127
+
128
+ my_manga mark unread "Naruto Movie" --list=[2,3,4,7,8]
129
+
130
+ Chapters 2, 3, 4, 7, 8 from "Naruto Movie" Marked as Unread
131
+
132
+ => nil
133
+
134
+ my_manga list
135
+
136
+ Manga list
137
+ =======================================
138
+ Name Chapters (read/total)
139
+ Assasination Classroom 165/166
140
+ Naruto 700/700
141
+ Naruto Movie 5/10
142
+
143
+ => nil
144
+
145
+ ````
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/my_manga'
5
+
6
+ Hanami::CLI.new(MyManga::CLI::Commands).call
@@ -0,0 +1,19 @@
1
+ default: &default
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ # For details on connection pooling, see rails configuration guide
5
+ # http://guides.rubyonrails.org/configuring.html#database-pooling
6
+ pool: 5
7
+ host: <%= ENV["PG_HOST"] %>
8
+ reconnect: false
9
+ # Set these in .bashrc or .bash_profile
10
+ username: <%= ENV["PG_USER"] %>
11
+ password: <%= ENV["PG_PASS"] %>
12
+
13
+ production:
14
+ <<: *default
15
+ database: my_manga_library_database
16
+
17
+ test:
18
+ <<: *default
19
+ database: my_manga_library_test_database
@@ -0,0 +1,44 @@
1
+ module MyManga
2
+ class DB
3
+ MIGRATION_PATH = File.expand_path('../migrate', __FILE__)
4
+ CONFIG_PATH = File.expand_path('../config.yml', __FILE__)
5
+ CONFIG = YAML.load(ERB.new(File.read(CONFIG_PATH)).result)[MyManga.env]
6
+
7
+ def self.establish_connection
8
+ ActiveRecord::Base.establish_connection(CONFIG)
9
+ end
10
+
11
+ def self.establish_base_connection
12
+ ActiveRecord::Base.establish_connection(
13
+ CONFIG.merge(
14
+ {
15
+ "database" => "postgres",
16
+ "schema_search_path" => "public",
17
+ }
18
+ )
19
+ )
20
+ end
21
+
22
+ def self.connection
23
+ ActiveRecord::Base.connection
24
+ end
25
+
26
+ def self.create_database
27
+ connection.create_database(CONFIG["database"], CONFIG)
28
+ end
29
+
30
+ def self.drop_database
31
+ connection.drop_database(CONFIG["database"])
32
+ end
33
+
34
+ def self.migrate
35
+ ActiveRecord::Migrator.migrate(MIGRATION_PATH, nil)
36
+ end
37
+
38
+ def self.dump
39
+ system("pg_dump -Fc #{CONFIG["database"]} > #{File.expand_path(__dir__)}/latest.dump")
40
+ end
41
+ end
42
+ end
43
+
44
+
@@ -0,0 +1,30 @@
1
+ require 'active_record'
2
+ require 'yaml'
3
+ require 'erb'
4
+
5
+ module MyManga
6
+ def self.env
7
+ ENV["MY_MANGA_ENV"] || "production"
8
+ end
9
+
10
+ def self.env=(environment)
11
+ ENV["MY_MANGA_ENV"] = environment
12
+ end
13
+
14
+ def self.download_dir
15
+ ENV["MY_MANGA_DOWNLOAD_DIR"] || Dir.home
16
+ end
17
+
18
+ def self.manga_download_dir=(dir)
19
+ ENV["MY_MANGA_DOWNLOAD_DIR"] = dir
20
+ end
21
+ end
22
+
23
+ require_relative '../db/connection.rb'
24
+
25
+ MODELS_DIR = File.expand_path("../../models", __FILE__)
26
+ Dir[MODELS_DIR + '/*.rb'].each do |file|
27
+ require file
28
+ end
29
+
30
+ MyManga::DB.establish_connection
@@ -0,0 +1,20 @@
1
+ class AddTables < ActiveRecord::Migration[4.2]
2
+ create_table :manga do |t|
3
+ t.string :name
4
+ t.string :uri
5
+ t.integer :read_count, default: 0
6
+ t.integer :total_count, default: 0
7
+
8
+ t.timestamps null: false
9
+ end
10
+
11
+ create_table :chapters do |t|
12
+ t.string :name
13
+ t.string :uri
14
+ t.integer :number
15
+ t.boolean :read, default: false
16
+ t.integer :manga_id
17
+
18
+ t.timestamps null: false
19
+ end
20
+ end
@@ -0,0 +1,116 @@
1
+ class MangaHere < Mangdown::Adapter::Base
2
+ site :manga_here
3
+
4
+ ROOT = 'http://www.mangahere.cc/'.freeze
5
+ CDNS = [
6
+ 'http://l.mhcdn.net/store/manga/',
7
+ 'https://mhcdn.secure.footprint.net/store/manga/'
8
+ ].freeze
9
+
10
+ attr_reader :root
11
+
12
+ def self.for?(uri_or_site)
13
+ uri_or_site.to_s.start_with?(ROOT) ||
14
+ cdn_uri?(uri_or_site) ||
15
+ uri_or_site.to_s == 'manga_here'
16
+ end
17
+
18
+ def self.cdn_uri?(uri)
19
+ CDNS.any? { |cdn| uri.to_s.start_with?(cdn) }
20
+ end
21
+
22
+ def initialize(uri, doc, name)
23
+ super
24
+
25
+ @root = ROOT
26
+ end
27
+
28
+ def hydra_opts
29
+ { max_concurrency: 1 }
30
+ end
31
+
32
+ def is_manga_list?(uri = @uri)
33
+ uri =~ /#{root}mangalist/
34
+ end
35
+
36
+ # Must return true/false if uri represents a manga for adapter
37
+ def is_manga?(uri = @uri)
38
+ uri =~ /#{root}manga\//
39
+ end
40
+
41
+ # Must return true/false if uri represents a chapter for adapter
42
+ def is_chapter?(uri = @uri)
43
+ uri =~ /#{root}.+\/c\d{3}\/?/
44
+ end
45
+
46
+ # Must return true/false if uri represents a page for adapter
47
+ def is_page?(uri = @uri)
48
+ self.class.cdn_uri?(uri)
49
+ end
50
+
51
+ # Return Array of Hash with keys: :uri, :name, :site
52
+ def manga_list
53
+ doc.css('.list_manga a.manga_info').map { |a|
54
+ uri = URI.join(root, a[:href]).to_s
55
+
56
+ { uri: uri, name: a.text.strip, site: site }}
57
+ end
58
+
59
+ # Return Hash with keys: :uri, :name, :site
60
+ def manga
61
+ name ||= doc.css('h1.title').first.text.strip
62
+
63
+ { uri: uri, name: name, site: site }
64
+ end
65
+
66
+ # Return Array of Hash with keys: :uri, :name, :site
67
+ def chapter_list
68
+ chapters = doc.css('.manga_detail .detail_list ul').first.css('li a')
69
+ chapters.reverse.map.with_index do |a, i|
70
+ uri = URI.join(root, a[:href]).to_s
71
+ name = a.text.strip
72
+
73
+ { uri: uri, name: name, chapter: i + 1, site: site }
74
+ end
75
+ end
76
+
77
+ # Return Hash with keys: :uri, :name, :chapter, :manga, :site
78
+ #
79
+ def chapter
80
+ name ||= chapter_name
81
+ chapter ||= name.slice(/[\d\.]+$/)
82
+ manga = name.sub(/ #{chapter}\Z/, '').strip
83
+
84
+ { uri: uri, name: name, chapter: chapter, manga: manga, site: site }
85
+ end
86
+
87
+ # Return Array of Hash with keys: :uri, :name, :site
88
+ def page_list
89
+ doc.css('.wid60').first.css('option').map do |option|
90
+ uri = URI.join(root, option[:value]).to_s
91
+ name = option.text
92
+
93
+ { uri: uri, name: name, site: site }
94
+ end
95
+ end
96
+
97
+ # Return Hash with keys: :uri, :name, :site
98
+ def page
99
+ if name.nil?
100
+ option = doc.css('.wid60 option[selected]').first
101
+ name = "#{chapter_name} - #{option.text.strip.rjust(3, '0')}"
102
+ end
103
+
104
+ image = doc.css('.read_img img')[1]
105
+ uri = image[:src]
106
+
107
+ { uri: uri, name: name, site: site }
108
+ end
109
+
110
+ private
111
+
112
+ def chapter_name
113
+ CGI.unescapeHTML(doc.css('.title h1').text.strip)
114
+ end
115
+ end
116
+
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../db/environment'
4
+
5
+ require 'hanami/cli'
6
+ require 'mangdown/client'
7
+
8
+ require_relative 'my_manga/cli'
9
+ require_relative 'mangdown/adapters/manga_here'
10
+
11
+ Mangdown.register_adapter(:manga_here, MangaHere)
12
+ M.manga_pages << 'http://www.mangahere.cc/mangalist/'
13
+
14
+ module MyManga
15
+ module_function
16
+
17
+ def [](name)
18
+ Manga.find_by(name: name)
19
+ end
20
+
21
+ def find(search)
22
+ M.find(Regexp.new(search, 'i'))
23
+ end
24
+
25
+ def package(dir)
26
+ Mangdown::CBZ.all(dir)
27
+ end
28
+
29
+ def names
30
+ Manga.pluck(:name)
31
+ end
32
+
33
+ def add(uri)
34
+ return if Manga.find_by_uri(uri)
35
+
36
+ md_manga = Mangdown::MDHash.new(uri: uri).to_manga
37
+ Manga.from_md(md_manga)
38
+ end
39
+
40
+ def remove(name)
41
+ Manga.find_by_name(name)&.destroy
42
+ end
43
+
44
+ def fetch_chapters(manga, numbers)
45
+ manga.chapters.where(number: numbers)
46
+ end
47
+
48
+ def read!(manga, numbers)
49
+ set_read(manga, numbers, true)
50
+ end
51
+
52
+ def unread!(manga, numbers)
53
+ set_read(manga, numbers, false)
54
+ end
55
+
56
+ def update(manga)
57
+ return if env == 'test'
58
+
59
+ md_manga = manga.to_md
60
+ chapters = md_manga.chapters
61
+ manga.update_chapters(chapters)
62
+ end
63
+
64
+ def download_chapter(chapter, download_dir)
65
+ return if env == 'test'
66
+
67
+ md_chapter = chapter.to_md
68
+ md_chapter.download_to(download_dir)
69
+ end
70
+
71
+ def download(manga, numbers)
72
+ unless env == 'test'
73
+ download_dir = create_manga_download_dir(manga)
74
+ chapters = fetch_chapters(manga, numbers)
75
+
76
+ chapters.each do |chapter|
77
+ download_chapter(chapter, download_dir)
78
+ end
79
+ package(download_dir)
80
+ end
81
+
82
+ read!(manga, numbers)
83
+ end
84
+
85
+ def mangdown_client_clean_up
86
+ M.clean_up
87
+ end
88
+
89
+ # @private
90
+ def set_read(manga, numbers, bool)
91
+ chapters = fetch_chapters(manga, numbers)
92
+ chapters.update_all(read: bool)
93
+ manga.reload
94
+ manga.read_count = manga.chapters_read.length
95
+ manga.save
96
+ end
97
+
98
+ # @private
99
+ def create_manga_download_dir(manga)
100
+ base = MyManga.download_dir
101
+ manga_dir = [base, manga.name].join('/')
102
+ Dir.mkdir(base) unless Dir.exist?(base)
103
+ Dir.mkdir(manga_dir) unless Dir.exist?(manga_dir)
104
+ manga_dir
105
+ end
106
+ end