narou 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of narou might be problematic. Click here for more details.
- data/ChangeLog.md +17 -1
- data/README.md +18 -12
- data/bin/narou +1 -1
- data/lib/color.rb +89 -0
- data/lib/command.rb +1 -1
- data/lib/command/alias.rb +5 -5
- data/lib/command/browser.rb +4 -2
- data/lib/command/convert.rb +14 -12
- data/lib/command/diff.rb +8 -8
- data/lib/command/download.rb +1 -1
- data/lib/command/folder.rb +2 -2
- data/lib/command/freeze.rb +1 -1
- data/lib/command/help.rb +8 -6
- data/lib/command/init.rb +8 -8
- data/lib/command/inspect.rb +2 -2
- data/lib/command/interactive.rb +1 -1
- data/lib/command/list.rb +5 -2
- data/lib/command/new.rb +1 -1
- data/lib/command/remove.rb +2 -2
- data/lib/command/send.rb +7 -7
- data/lib/command/setting.rb +8 -8
- data/lib/command/update.rb +2 -2
- data/lib/command/version.rb +1 -1
- data/lib/commandbase.rb +6 -4
- data/lib/commandline.rb +2 -2
- data/lib/converterbase.rb +81 -44
- data/lib/database.rb +1 -1
- data/lib/device.rb +3 -3
- data/lib/device/kindle.rb +1 -1
- data/lib/device/kobo.rb +1 -1
- data/lib/device/library/windows.rb +2 -14
- data/lib/downloader.rb +41 -14
- data/lib/extensions/windows.rb +58 -0
- data/lib/globalsetting.rb +1 -1
- data/lib/helper.rb +1 -1
- data/lib/illustration.rb +1 -1
- data/lib/ini.rb +1 -1
- data/lib/inspector.rb +2 -2
- data/lib/kindlestrip.rb +1 -1
- data/lib/loadconverter.rb +2 -2
- data/lib/localsetting.rb +1 -1
- data/lib/logger.rb +26 -9
- data/lib/narou.rb +1 -1
- data/lib/narou/api.rb +41 -0
- data/lib/novelconverter.rb +14 -14
- data/lib/novelsetting.rb +7 -2
- data/lib/progressbar.rb +1 -1
- data/lib/ruby.rb +1 -1
- data/lib/sitesetting.rb +1 -1
- data/lib/template.rb +3 -3
- data/lib/version.rb +2 -2
- data/narou.gemspec +3 -2
- data/narou.rb +5 -3
- data/template/converter.rb.erb +1 -1
- data/template/diff.txt.erb +1 -1
- data/template/novel.txt.erb +2 -1
- data/template/replace.txt.erb +2 -2
- data/template/setting.ini.erb +1 -1
- data/webnovel/ncode.syosetu.com.yaml +2 -1
- data/webnovel/novel18.syosetu.com.yaml +2 -1
- metadata +48 -19
- checksums.yaml +0 -7
data/lib/database.rb
CHANGED
data/lib/device.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# -*- coding:
|
1
|
+
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
3
|
# Copyright 2013 whiteleaf. All rights reserved.
|
4
4
|
#
|
@@ -65,7 +65,7 @@ class Device
|
|
65
65
|
capture = `#{cmd}`
|
66
66
|
if $?.exitstatus > 0
|
67
67
|
puts
|
68
|
-
|
68
|
+
error capture.force_encoding(Encoding::Windows_31J).rstrip
|
69
69
|
exit 1
|
70
70
|
end
|
71
71
|
else
|
@@ -73,7 +73,7 @@ class Device
|
|
73
73
|
FileUtils.cp(src_file, dst_path)
|
74
74
|
rescue => e
|
75
75
|
puts
|
76
|
-
|
76
|
+
error e.message
|
77
77
|
exit 1
|
78
78
|
end
|
79
79
|
end
|
data/lib/device/kindle.rb
CHANGED
data/lib/device/kobo.rb
CHANGED
@@ -1,22 +1,10 @@
|
|
1
|
-
# -*- coding:
|
1
|
+
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
3
|
# Copyright 2013 whiteleaf. All rights reserved.
|
4
4
|
#
|
5
5
|
|
6
|
-
require "fiddle"
|
7
6
|
require "win32ole"
|
8
|
-
|
9
|
-
module WinAPI
|
10
|
-
include Fiddle
|
11
|
-
class InvalidOS < StandardError; end
|
12
|
-
Handle = RUBY_VERSION >= "2.0.0" ? Fiddle::Handle : DL::Handle
|
13
|
-
|
14
|
-
def self.GetLogicalDriveStrings(buf_size, buffer)
|
15
|
-
@@get_logical_drive_strings ||= Function.new(Handle.new("kernel32")["GetLogicalDriveStrings"],
|
16
|
-
[TYPE_LONG, TYPE_VOIDP], TYPE_LONG)
|
17
|
-
@@get_logical_drive_strings.call(buf_size, buffer)
|
18
|
-
end
|
19
|
-
end
|
7
|
+
require_relative "../../extensions/windows"
|
20
8
|
|
21
9
|
module Device::Library
|
22
10
|
module Windows
|
data/lib/downloader.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
|
-
# -*- coding:
|
1
|
+
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
3
|
# Copyright 2013 whiteleaf. All rights reserved.
|
4
4
|
#
|
5
5
|
|
6
|
+
require "yaml"
|
6
7
|
require "fileutils"
|
7
8
|
require_relative "narou"
|
8
9
|
require_relative "sitesetting"
|
9
10
|
require_relative "template"
|
10
11
|
require_relative "database"
|
11
12
|
require_relative "localsetting"
|
13
|
+
require_relative "narou/api"
|
12
14
|
|
13
15
|
#
|
14
16
|
# 小説サイトからのダウンロード
|
@@ -23,6 +25,8 @@ class Downloader
|
|
23
25
|
|
24
26
|
attr_reader :id
|
25
27
|
|
28
|
+
class NoSerialNovel < StandardError; end
|
29
|
+
|
26
30
|
#
|
27
31
|
# ターゲット(ID、URL、Nコード、小説名)を指定して小説データのダウンロードを開始する
|
28
32
|
#
|
@@ -35,13 +39,13 @@ class Downloader
|
|
35
39
|
when :url, :ncode
|
36
40
|
setting = get_sitesetting_by_target(target)
|
37
41
|
unless setting
|
38
|
-
|
42
|
+
error "対応外の#{type}です(#{target})"
|
39
43
|
return false
|
40
44
|
end
|
41
45
|
when :id
|
42
46
|
data = @@database[target.to_i]
|
43
47
|
unless data
|
44
|
-
|
48
|
+
error "指定のID(#{target})は存在しません"
|
45
49
|
return false
|
46
50
|
end
|
47
51
|
setting = get_sitesetting_by_sitename(data["sitename"])
|
@@ -52,7 +56,7 @@ class Downloader
|
|
52
56
|
setting = get_sitesetting_by_sitename(data["sitename"])
|
53
57
|
setting.multi_match(data["toc_url"], "url")
|
54
58
|
else
|
55
|
-
|
59
|
+
error "指定の小説(#{target})は存在しません"
|
56
60
|
return false
|
57
61
|
end
|
58
62
|
end
|
@@ -126,7 +130,7 @@ class Downloader
|
|
126
130
|
else
|
127
131
|
@@database.delete(id)
|
128
132
|
@@database.save_database
|
129
|
-
|
133
|
+
error "#{path} が見つかりません。"
|
130
134
|
warn "保存フォルダが消去されていたため、データベースのインデックスを削除しました。"
|
131
135
|
return nil
|
132
136
|
end
|
@@ -210,7 +214,7 @@ class Downloader
|
|
210
214
|
def self.get_sitesetting_by_sitename(sitename)
|
211
215
|
setting = @@settings.find { |s| s["name"] == sitename }
|
212
216
|
return setting if setting
|
213
|
-
|
217
|
+
error "#{data["sitename"]} の設定ファイルが見つかりません"
|
214
218
|
exit 1
|
215
219
|
end
|
216
220
|
|
@@ -227,11 +231,11 @@ class Downloader
|
|
227
231
|
settings << setting
|
228
232
|
end
|
229
233
|
if settings.empty?
|
230
|
-
|
234
|
+
error "小説サイトの定義ファイルがひとつもありません"
|
231
235
|
exit 1
|
232
236
|
end
|
233
237
|
unless @@narou
|
234
|
-
|
238
|
+
error "小説家になろうの定義ファイルが見つかりませんでした"
|
235
239
|
exit 1
|
236
240
|
end
|
237
241
|
settings
|
@@ -295,12 +299,18 @@ class Downloader
|
|
295
299
|
# ダウンロード処理本体
|
296
300
|
#
|
297
301
|
# 返り値:ダウンロードしたものが1話でもあったかどうか(Boolean)
|
302
|
+
# nil なら何らかの原因でダウンロード自体出来なかった
|
298
303
|
#
|
299
304
|
def start_download
|
300
|
-
|
305
|
+
begin
|
306
|
+
latest_toc = get_latest_table_of_contents
|
307
|
+
rescue NoSerialNovel
|
308
|
+
error @setting["ncode"] + " は短編小説です。現在短編小説には対応していません"
|
309
|
+
return nil
|
310
|
+
end
|
301
311
|
unless latest_toc
|
302
|
-
|
303
|
-
|
312
|
+
error @setting["url"] + " の目次データが取得出来ませんでした"
|
313
|
+
return nil
|
304
314
|
end
|
305
315
|
if @setting["confirm_over18"]
|
306
316
|
unless confirm_over18?
|
@@ -364,11 +374,25 @@ class Downloader
|
|
364
374
|
"file_title" => @file_title,
|
365
375
|
"toc_url" => @setting["toc_url"],
|
366
376
|
"sitename" => @setting["name"],
|
377
|
+
"novel_type" => @novel_type,
|
367
378
|
"last_update" => Time.now
|
368
379
|
}
|
369
380
|
@@database.save_database
|
370
381
|
end
|
371
382
|
|
383
|
+
#
|
384
|
+
# 連載小説かどうか調べる
|
385
|
+
#
|
386
|
+
def serial_novel?
|
387
|
+
if @@database[@id]
|
388
|
+
@novel_type = @@database[@id]["novel_type"] || 1
|
389
|
+
else
|
390
|
+
api = Narou::API.new(@setting, "nt")
|
391
|
+
@novel_type = api["novel_type"]
|
392
|
+
end
|
393
|
+
@novel_type == 1
|
394
|
+
end
|
395
|
+
|
372
396
|
#
|
373
397
|
# 目次データを取得する
|
374
398
|
#
|
@@ -390,12 +414,15 @@ class Downloader
|
|
390
414
|
end
|
391
415
|
rescue OpenURI::HTTPError => e
|
392
416
|
if e.message =~ /^404/
|
393
|
-
|
417
|
+
error "指定されたURLは存在しません"
|
394
418
|
return false
|
395
419
|
else
|
396
420
|
raise
|
397
421
|
end
|
398
422
|
end
|
423
|
+
if @setting["narou_api_url"] && !serial_novel?
|
424
|
+
raise NoSerialNovel
|
425
|
+
end
|
399
426
|
@setting.multi_match(toc_source, "title", "author", "story", "tcode")
|
400
427
|
@title = @setting["title"]
|
401
428
|
@file_title = Helper.replace_filename_special_chars(@title)
|
@@ -461,7 +488,7 @@ class Downloader
|
|
461
488
|
def sections_download_and_save(subtitles)
|
462
489
|
max = subtitles.count
|
463
490
|
return if max == 0
|
464
|
-
puts "ID:#{@id} #{@title} のDL開始"
|
491
|
+
puts ("<green>" + TermColor.escape("ID:#{@id} #{@title} のDL開始") + "</green>").termcolor
|
465
492
|
interval_sleep_time = LocalSetting.get["local_setting"]["download.interval"] || 0
|
466
493
|
interval_sleep_time = 0 if interval_sleep_time < 0
|
467
494
|
save_least_one = false
|
@@ -536,7 +563,7 @@ class Downloader
|
|
536
563
|
rescue OpenURI::HTTPError => e
|
537
564
|
if e.message =~ /^503/
|
538
565
|
if retry_count == 0
|
539
|
-
|
566
|
+
error "上限までリトライしましたがファイルがダウンロード出来ませんでした"
|
540
567
|
exit 1
|
541
568
|
end
|
542
569
|
retry_count -= 1
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "fiddle"
|
5
|
+
|
6
|
+
module WinAPI
|
7
|
+
include Fiddle
|
8
|
+
Handle = RUBY_VERSION >= "2.0.0" ? Fiddle::Handle : DL::Handle
|
9
|
+
Kernel32 = Handle.new("kernel32")
|
10
|
+
|
11
|
+
def self.GetLogicalDriveStrings(buf_size, buffer)
|
12
|
+
@@get_logical_drive_strings ||=
|
13
|
+
Function.new(Kernel32["GetLogicalDriveStrings"], [TYPE_LONG, TYPE_VOIDP], TYPE_LONG)
|
14
|
+
@@get_logical_drive_strings.call(buf_size, buffer)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.SetConsoleTextAttribute(cons_handle, attr)
|
18
|
+
@@set_console_text_attribute ||=
|
19
|
+
Function.new(Kernel32["SetConsoleTextAttribute"], [-TYPE_INT, -TYPE_INT], -TYPE_INT)
|
20
|
+
@@set_console_text_attribute.call(cons_handle, attr)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.GetConsoleScreenBufferInfo(cons_handle, lp_buffer)
|
24
|
+
@@get_console_screen_buffer_info ||=
|
25
|
+
Function.new(Kernel32["GetConsoleScreenBufferInfo"], [TYPE_LONG, TYPE_VOIDP], TYPE_INT)
|
26
|
+
@@get_console_screen_buffer_info.call(cons_handle, lp_buffer)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.GetStdHandle(handle_type)
|
30
|
+
@@get_std_handle ||= Function.new(Kernel32["GetStdHandle"], [-TYPE_INT], -TYPE_INT)
|
31
|
+
@@get_std_handle.call(handle_type)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.GetLastError
|
35
|
+
@@get_last_error ||= Function.new(Kernel32["GetLastError"], [], -TYPE_INT)
|
36
|
+
@@get_last_error.call
|
37
|
+
end
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
# Fiddle がない環境用(http://www.artonx.org/data/asr/ の1.9.3とか)
|
41
|
+
require "dl/import"
|
42
|
+
|
43
|
+
class InvalidOS < StandardError; end
|
44
|
+
|
45
|
+
module WinAPI
|
46
|
+
extend DL::Importer
|
47
|
+
begin
|
48
|
+
dlload "kernel32"
|
49
|
+
extern "long GetLogicalDriveStrings(long, void*)"
|
50
|
+
extern "long SetConsoleTextAttribute(long, long)"
|
51
|
+
extern "long GetConsoleScreenBufferInfo(long, void*)"
|
52
|
+
extern "long GetStdHandle(long)"
|
53
|
+
extern "long GetLastError()"
|
54
|
+
rescue DL::DLError
|
55
|
+
raise InvalidOS, "not Windows"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/globalsetting.rb
CHANGED
data/lib/helper.rb
CHANGED
data/lib/illustration.rb
CHANGED
data/lib/ini.rb
CHANGED
data/lib/inspector.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# -*- coding:
|
1
|
+
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
3
|
# Copyright 2013 whiteleaf. All rights reserved.
|
4
4
|
#
|
@@ -58,7 +58,7 @@ class Inspector
|
|
58
58
|
@info
|
59
59
|
end
|
60
60
|
|
61
|
-
def display(klass = ALL, target = $
|
61
|
+
def display(klass = ALL, target = $stdout)
|
62
62
|
target.puts @messages.map { |msg|
|
63
63
|
if msg =~ /^\[(.+)\]/
|
64
64
|
key = KLASS_TAG.key($1)
|
data/lib/kindlestrip.rb
CHANGED
data/lib/loadconverter.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# -*- coding:
|
1
|
+
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
3
|
# Copyright 2013 whiteleaf. All rights reserved.
|
4
4
|
#
|
@@ -72,7 +72,7 @@ def load_converter(title, archive_path)
|
|
72
72
|
if conv
|
73
73
|
return conv
|
74
74
|
else
|
75
|
-
|
75
|
+
error "converter.rbは見つかりましたが、`converter'で登録されていないようです。" +
|
76
76
|
"変換処理は converter \"#{title}\" として登録する必要があります"
|
77
77
|
return BlankConverter
|
78
78
|
end
|
data/lib/localsetting.rb
CHANGED
data/lib/logger.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
# -*- coding:
|
1
|
+
# -*- coding: utf-8 -*-
|
2
2
|
#
|
3
3
|
# Copyright 2013 whiteleaf. All rights reserved.
|
4
4
|
#
|
5
5
|
|
6
6
|
require "singleton"
|
7
7
|
require "stringio"
|
8
|
+
require_relative "color"
|
8
9
|
|
9
10
|
module LoggerModule
|
10
11
|
def initialize
|
@@ -20,9 +21,23 @@ module LoggerModule
|
|
20
21
|
@is_silent
|
21
22
|
end
|
22
23
|
|
24
|
+
def strip_color(str)
|
25
|
+
str.gsub(/(?:\e\[\d*[a-zA-Z])+/, "")
|
26
|
+
end
|
27
|
+
|
23
28
|
def save(path)
|
24
29
|
File.write(path, string)
|
25
30
|
end
|
31
|
+
|
32
|
+
def write_console(str, target)
|
33
|
+
if str.encoding == Encoding::ASCII_8BIT
|
34
|
+
str.force_encoding("utf-8")
|
35
|
+
end
|
36
|
+
unless @is_silent
|
37
|
+
str = strip_color(str) if $disable_color
|
38
|
+
write_color(str, target)
|
39
|
+
end
|
40
|
+
end
|
26
41
|
end
|
27
42
|
|
28
43
|
class Logger < StringIO
|
@@ -34,10 +49,9 @@ class Logger < StringIO
|
|
34
49
|
end
|
35
50
|
|
36
51
|
def write(str)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
52
|
+
str = str.to_s
|
53
|
+
super(strip_color(str))
|
54
|
+
write_console(str, STDOUT)
|
41
55
|
end
|
42
56
|
end
|
43
57
|
|
@@ -50,12 +64,15 @@ class LoggerError < StringIO
|
|
50
64
|
end
|
51
65
|
|
52
66
|
def write(str)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
67
|
+
str = str.to_s
|
68
|
+
super(strip_color(str))
|
69
|
+
write_console(str, STDERR)
|
57
70
|
end
|
58
71
|
end
|
59
72
|
|
73
|
+
def error(str)
|
74
|
+
warn "<red>[ERROR]</red> #{str}".termcolor
|
75
|
+
end
|
76
|
+
|
60
77
|
$stdout = Logger.get
|
61
78
|
$stderr = LoggerError.get
|
data/lib/narou.rb
CHANGED
data/lib/narou/api.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright 2013 whiteleaf. All rights reserved.
|
4
|
+
#
|
5
|
+
|
6
|
+
require "open-uri"
|
7
|
+
require "yaml"
|
8
|
+
|
9
|
+
module Narou
|
10
|
+
#
|
11
|
+
# 小説家になろうデベロッパーAPI操作クラス
|
12
|
+
#
|
13
|
+
class API
|
14
|
+
def initialize(setting, of = "")
|
15
|
+
@setting = setting
|
16
|
+
@api_url = @setting["narou_api_url"]
|
17
|
+
@ncode = @setting["ncode"]
|
18
|
+
request_api(of)
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](key)
|
22
|
+
@api_result[key]
|
23
|
+
end
|
24
|
+
|
25
|
+
def request_api(of)
|
26
|
+
url = "#{@api_url}?ncode=#{@ncode}&of=#{of}"
|
27
|
+
open(url) do |fp|
|
28
|
+
result = YAML.load(fp.read)
|
29
|
+
if result[0]["allcount"] == 1
|
30
|
+
@api_result = result[1]
|
31
|
+
if of.length > 0
|
32
|
+
@api_result["novel_type"] = @api_result["noveltype"]
|
33
|
+
end
|
34
|
+
else
|
35
|
+
error "なろうAPIから情報が取得出来ませんでした"
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|