manga-tools 0.1.1 → 0.1.2
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 +4 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +1 -5
- data/README.md +6 -5
- data/lib/manga/tools.rb +0 -1
- data/lib/manga/tools/cli.rb +7 -140
- data/lib/manga/tools/http.rb +7 -6
- data/lib/manga/tools/version.rb +1 -1
- metadata +2 -3
- data/lib/manga/tools/cache.rb +0 -113
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: da4ed03340c47790056a5ffeaa86a4f986010d8dfcb7c8fd87568300c9f66e30
|
|
4
|
+
data.tar.gz: a4f1644d7fca880a904463bf10376c86aaac49aac3a2233ec323ee8d0287bc42
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94e0f51017fd81f30360f03ec4c29cf82318401e507dc267cc354e6e4cba43278b5161e3b12f3af4f0ecfdc2e4dc21af6e8f0c10c0f0ea5b4fd22fb96f06e4dc
|
|
7
|
+
data.tar.gz: 45d686425259594ae0e45114f8e7d70970df515db271f3c3c6dee8c6316c83deaa8d844a6be9b199abe7fe1854032a127a56eed64e2da785245281b8dafbca15
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
manga-tools (0.1.
|
|
4
|
+
manga-tools (0.1.2)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
@@ -14,10 +14,7 @@ GEM
|
|
|
14
14
|
multipart-post (>= 1.2, < 3)
|
|
15
15
|
json (2.3.1)
|
|
16
16
|
method_source (1.0.0)
|
|
17
|
-
mini_portile2 (2.4.0)
|
|
18
17
|
multipart-post (2.1.1)
|
|
19
|
-
nokogiri (1.10.10)
|
|
20
|
-
mini_portile2 (~> 2.4.0)
|
|
21
18
|
parallel (1.19.2)
|
|
22
19
|
parser (2.7.1.4)
|
|
23
20
|
ast (~> 2.4.1)
|
|
@@ -67,7 +64,6 @@ PLATFORMS
|
|
|
67
64
|
DEPENDENCIES
|
|
68
65
|
faraday
|
|
69
66
|
manga-tools!
|
|
70
|
-
nokogiri
|
|
71
67
|
pry
|
|
72
68
|
rake
|
|
73
69
|
rspec
|
data/README.md
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
# Manga::Tools
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/rb/manga-tools)
|
|
3
4
|

|
|
4
5
|
[](https://codeclimate.com/github/yagihiro/manga-tools/maintainability)
|
|
5
6
|
[](https://codeclimate.com/github/yagihiro/manga-tools/test_coverage)
|
|
6
7
|
|
|
7
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/manga/tools`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
8
|
-
|
|
9
|
-
TODO: Delete this and the text above, and describe your gem
|
|
10
|
-
|
|
11
8
|
## Installation
|
|
12
9
|
|
|
13
10
|
Add this line to your application's Gemfile:
|
|
@@ -26,7 +23,11 @@ Or install it yourself as:
|
|
|
26
23
|
|
|
27
24
|
## Usage
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
* A search command to find out the release date of a manga
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
$ manga-tools search "ONE PIECE"
|
|
30
|
+
```
|
|
30
31
|
|
|
31
32
|
## Development
|
|
32
33
|
|
data/lib/manga/tools.rb
CHANGED
data/lib/manga/tools/cli.rb
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
|
-
require 'nokogiri'
|
|
5
|
-
require 'stringio'
|
|
6
4
|
require 'thor'
|
|
5
|
+
require_relative 'http'
|
|
7
6
|
|
|
8
7
|
module Manga
|
|
9
8
|
module Tools
|
|
@@ -13,150 +12,18 @@ module Manga
|
|
|
13
12
|
true
|
|
14
13
|
end
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
# def pub(name)
|
|
18
|
-
# doc = Nokogiri::HTML(
|
|
19
|
-
# URI.open('https://tsutaya.tsite.jp/feature/book/release/comic/index', 'User-Agent' => UserAgent)
|
|
20
|
-
# )
|
|
21
|
-
#
|
|
22
|
-
# File.open('.index.txt', 'w') {|f| f.write doc }
|
|
23
|
-
#
|
|
24
|
-
# results = {}
|
|
25
|
-
#
|
|
26
|
-
# current_date = ''
|
|
27
|
-
# state = :title
|
|
28
|
-
# current_data = {}
|
|
29
|
-
# doc.css('h3, div.comic_list div.c_cols-1of3 span').each do |element|
|
|
30
|
-
# case element.name
|
|
31
|
-
# when 'h3'
|
|
32
|
-
# results[element.content] = []
|
|
33
|
-
# current_date = element.content
|
|
34
|
-
# when 'span'
|
|
35
|
-
# current_data[state] = element.content
|
|
36
|
-
# case state
|
|
37
|
-
# when :title
|
|
38
|
-
# state = :author
|
|
39
|
-
# when :author
|
|
40
|
-
# state = :label
|
|
41
|
-
# when :label
|
|
42
|
-
# state = :title
|
|
43
|
-
# results[current_date] << current_data
|
|
44
|
-
# current_data = {}
|
|
45
|
-
# end
|
|
46
|
-
# end
|
|
47
|
-
# end
|
|
48
|
-
#
|
|
49
|
-
# puts JSON.pretty_generate(results)
|
|
50
|
-
# end
|
|
51
|
-
|
|
52
|
-
desc 'search WORD', 'Search for a given WORD'
|
|
15
|
+
desc 'search WORD', 'Search titles for a given WORD'
|
|
53
16
|
def search(word)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
url = "https://calendar.gameiroiro.com/manga.php?year=#{t.year}&month=#{t.month}"
|
|
58
|
-
data = Manga::Tools::Cache.fetch(key: url) do |cache|
|
|
59
|
-
res = Manga::Tools::Http.get(url)
|
|
60
|
-
cache.expires_in = Manga::Tools::Http.seconds_to_cache(res)
|
|
61
|
-
results = generate_internal_data(t, res.body)
|
|
62
|
-
results.to_json
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
hash = JSON.parse(data)
|
|
17
|
+
url = "https://manga-tools-server.herokuapp.com/publications?keyword=#{CGI.escape(word)}"
|
|
18
|
+
res = Manga::Tools::Http.get(url)
|
|
19
|
+
results = JSON.parse(res.body)
|
|
66
20
|
|
|
67
21
|
puts "Searching '#{word}' ..."
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
items.each do |item|
|
|
72
|
-
puts "Found: #{date}, #{item['title']}" if item['title'].index(word)
|
|
73
|
-
end
|
|
22
|
+
results.each do |item|
|
|
23
|
+
puts "#{item['published_at']}: #{item['title']}"
|
|
74
24
|
end
|
|
75
25
|
puts 'Finished.'
|
|
76
26
|
end
|
|
77
|
-
|
|
78
|
-
private
|
|
79
|
-
|
|
80
|
-
# @param time [Time] a target time
|
|
81
|
-
# @param data [String] a string of HTTP response body.
|
|
82
|
-
def generate_internal_data(time, data)
|
|
83
|
-
doc = Nokogiri::HTML(StringIO.open(data))
|
|
84
|
-
|
|
85
|
-
results = {}
|
|
86
|
-
current_date = nil
|
|
87
|
-
state = :genre
|
|
88
|
-
data = {}
|
|
89
|
-
doc.css(targets.join(', ')).each do |element|
|
|
90
|
-
value = rm_spaces(element.content)
|
|
91
|
-
case element.name
|
|
92
|
-
when 'td'
|
|
93
|
-
current_date = format('%<ym>s/%<day>02d', ym: time.strftime('%Y/%m'), day: value.to_i)
|
|
94
|
-
results[current_date] = []
|
|
95
|
-
when 'p', 'a'
|
|
96
|
-
case state
|
|
97
|
-
when :genre
|
|
98
|
-
guard_genre(element)
|
|
99
|
-
data[state] = value
|
|
100
|
-
state = :title
|
|
101
|
-
when :title
|
|
102
|
-
guard_title(element)
|
|
103
|
-
data[state] = value
|
|
104
|
-
data[:link] = element['href']
|
|
105
|
-
state = :company
|
|
106
|
-
when :company
|
|
107
|
-
guard_company(element)
|
|
108
|
-
data[state] = value
|
|
109
|
-
state = :authors
|
|
110
|
-
when :authors
|
|
111
|
-
guard_authors(element)
|
|
112
|
-
data[state] = value
|
|
113
|
-
state = :genre
|
|
114
|
-
results[current_date] << data
|
|
115
|
-
data = {}
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
rescue StandardError
|
|
119
|
-
# when authors, authors may not be present.
|
|
120
|
-
if state == :authors
|
|
121
|
-
data[state] = ''
|
|
122
|
-
state = :genre
|
|
123
|
-
results[current_date] << data
|
|
124
|
-
data = {}
|
|
125
|
-
end
|
|
126
|
-
retry
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
results
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def targets
|
|
133
|
-
@targets ||= [
|
|
134
|
-
'td.day-td',
|
|
135
|
-
'div.product-description-right p.p-genre',
|
|
136
|
-
'div.product-description-right a',
|
|
137
|
-
'div.product-description-right p.p-company'
|
|
138
|
-
]
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Remove HTML spaces ( ) and white spaces.
|
|
142
|
-
# @param str [String] a string
|
|
143
|
-
def rm_spaces(str)
|
|
144
|
-
str.gsub("\u00A0", '').strip
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def guard_genre(element)
|
|
148
|
-
raise 'invalid state' unless element.name == 'p' && element['class'] == 'p-genre'
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def guard_title(element)
|
|
152
|
-
raise 'invalid state' unless element.name == 'a'
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def guard_company(element)
|
|
156
|
-
raise 'invalid state' unless element.name == 'p' && element['class'] == 'p-company'
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
alias guard_authors guard_company
|
|
160
27
|
end
|
|
161
28
|
end
|
|
162
29
|
end
|
data/lib/manga/tools/http.rb
CHANGED
|
@@ -2,23 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
require 'faraday'
|
|
4
4
|
require 'uri'
|
|
5
|
+
require_relative 'version'
|
|
5
6
|
|
|
6
7
|
module Manga
|
|
7
8
|
module Tools
|
|
8
9
|
# HTTP client class
|
|
9
10
|
module Http
|
|
10
|
-
# rubocop:disable Layout/LineLength
|
|
11
|
-
UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
|
|
12
|
-
# rubocop:enable Layout/LineLength
|
|
13
|
-
|
|
14
11
|
# @param url [String] an url string, eg: `https://example.com`
|
|
15
12
|
# @return [Faraday::Connection] a connection object
|
|
16
13
|
def self.connection(url)
|
|
17
14
|
@connection ||= Faraday.new(
|
|
18
15
|
url: url,
|
|
19
|
-
headers: { 'User-Agent' =>
|
|
16
|
+
headers: { 'User-Agent' => user_agent }
|
|
20
17
|
) do |f|
|
|
21
|
-
f.response :logger
|
|
18
|
+
# f.response :logger
|
|
22
19
|
end
|
|
23
20
|
end
|
|
24
21
|
|
|
@@ -40,6 +37,10 @@ module Manga
|
|
|
40
37
|
|
|
41
38
|
Regexp.last_match(1).to_i
|
|
42
39
|
end
|
|
40
|
+
|
|
41
|
+
def self.user_agent
|
|
42
|
+
@user_agent ||= "manga-tools #{Manga::Tools::VERSION}"
|
|
43
|
+
end
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
end
|
data/lib/manga/tools/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: manga-tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hiroki Yagita
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-08-
|
|
11
|
+
date: 2020-08-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Tools to support the automation of daily manga-related activities.
|
|
14
14
|
email:
|
|
@@ -34,7 +34,6 @@ files:
|
|
|
34
34
|
- bin/setup
|
|
35
35
|
- exe/manga-tools
|
|
36
36
|
- lib/manga/tools.rb
|
|
37
|
-
- lib/manga/tools/cache.rb
|
|
38
37
|
- lib/manga/tools/cli.rb
|
|
39
38
|
- lib/manga/tools/http.rb
|
|
40
39
|
- lib/manga/tools/version.rb
|
data/lib/manga/tools/cache.rb
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'digest/md5'
|
|
4
|
-
require 'fileutils'
|
|
5
|
-
|
|
6
|
-
module Manga
|
|
7
|
-
module Tools
|
|
8
|
-
# Cache class
|
|
9
|
-
class Cache
|
|
10
|
-
class << self
|
|
11
|
-
# Initialize cache
|
|
12
|
-
def init
|
|
13
|
-
FileUtils.mkdir_p(cache_current_year_dir)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def root_dir
|
|
17
|
-
@root_dir ||= "#{Dir.home}/.manga-tools"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def cache_root_dir
|
|
21
|
-
@cache_root_dir ||= "#{root_dir}/cache"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def cache_current_year_dir
|
|
25
|
-
@cache_current_year_dir ||= "#{cache_root_dir}/#{Time.now.year}"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# @return [Integer] default expires in seconds (1day)
|
|
29
|
-
def default_expires_in
|
|
30
|
-
@default_expires_in ||= 1 * 24 * 60 * 60
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# @param key [String] A key to generate a key for the cache
|
|
34
|
-
# @return [String] string of cached data
|
|
35
|
-
def fetch(key:)
|
|
36
|
-
raise ArgumentError, 'mast pass a block' unless block_given?
|
|
37
|
-
|
|
38
|
-
cache = new(key)
|
|
39
|
-
# cache.clear
|
|
40
|
-
return cache.load_data if cache.available?
|
|
41
|
-
|
|
42
|
-
result = yield(cache)
|
|
43
|
-
cache.save_meta_data
|
|
44
|
-
cache.save_data(result)
|
|
45
|
-
result
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# @return [Integer|nil] A cache deadline in seconds.
|
|
50
|
-
attr_accessor :expires_in
|
|
51
|
-
# @return [String] A key to generate a key for the cache.
|
|
52
|
-
attr_reader :key
|
|
53
|
-
# @return [String] A key for the cache.
|
|
54
|
-
attr_reader :cache_key
|
|
55
|
-
# @return [String] A path to the cache metadata file.
|
|
56
|
-
attr_reader :meta_file_path
|
|
57
|
-
# @return [String] A path to the cached data file.
|
|
58
|
-
attr_reader :data_file_path
|
|
59
|
-
|
|
60
|
-
# @param key [String] A key to generate a key for the cache.
|
|
61
|
-
def initialize(key)
|
|
62
|
-
raise ArgumentError, 'mast pass the key param' unless key
|
|
63
|
-
|
|
64
|
-
@expires_in = self.class.default_expires_in
|
|
65
|
-
@key = key
|
|
66
|
-
@cache_key = Digest::MD5.hexdigest(key)
|
|
67
|
-
@meta_file_path = "#{self.class.cache_current_year_dir}/#{@cache_key}"
|
|
68
|
-
@data_file_path = "#{@meta_file_path}.data"
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# @return [Boolean] True if a cache is available, otherwise return false.
|
|
72
|
-
def available?
|
|
73
|
-
t = File.mtime(meta_file_path)
|
|
74
|
-
saved_expires_in = File.read(meta_file_path).strip.to_i
|
|
75
|
-
|
|
76
|
-
Time.now <= (t + saved_expires_in)
|
|
77
|
-
rescue StandardError
|
|
78
|
-
false
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# @return [String] Reads and returns the cache data.
|
|
82
|
-
def load_data
|
|
83
|
-
File.read(data_file_path)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Save the metadata for the cache.
|
|
87
|
-
def save_meta_data
|
|
88
|
-
@expires_in = self.class.default_expires_in unless expires_in
|
|
89
|
-
|
|
90
|
-
File.open(meta_file_path, 'w') do |f|
|
|
91
|
-
f.write(expires_in.to_s)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
self
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# @param str [String] Save the specified cache data
|
|
98
|
-
def save_data(str)
|
|
99
|
-
File.open(data_file_path, 'w') do |f|
|
|
100
|
-
f.write(str.force_encoding('utf-8'))
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
self
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# for debug
|
|
107
|
-
def clear
|
|
108
|
-
File.delete(meta_file_path) if File.exist?(meta_file_path)
|
|
109
|
-
File.delete(data_file_path) if File.exist?(data_file_path)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|