doxieland 0.1.0 → 0.9.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 +4 -4
- data/.gitignore +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/doxieland.gemspec +5 -2
- data/exe/doxieland +198 -15
- data/lib/doxieland.rb +26 -2
- data/lib/doxieland/actions/delete_scans.rb +17 -0
- data/lib/doxieland/actions/get_info.rb +8 -0
- data/lib/doxieland/actions/get_scan.rb +20 -0
- data/lib/doxieland/actions/list_scans.rb +16 -0
- data/lib/doxieland/api.rb +3 -1
- data/lib/doxieland/client.rb +94 -0
- data/lib/doxieland/handlers/file_request.rb +43 -0
- data/lib/doxieland/logger.rb +47 -0
- data/lib/doxieland/scan.rb +101 -0
- data/lib/doxieland/version.rb +1 -1
- metadata +59 -11
- data/doxieland.sublime-project +0 -8
- data/doxieland.sublime-workspace +0 -1729
- data/lib/doxieland/handlers/json.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a8265c8aef3acb73110d4c084c45ee809391bb6
|
4
|
+
data.tar.gz: a6636e435c48b7a47fc0035a3d08635083b44c9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbc20ab3361ffadbc3d710b9a8c4e8d384470de16d3b86c6db1926d6542dc67aa2314e24f78de9c0bf51be927e0c81c332c8087c87089b86029d5f55e54276cf
|
7
|
+
data.tar.gz: f3128eb67754c15858636561f8b1588dd76271ca06414ab0a2a8ef39c03ee00a43a6c5293d4e34d343228666e26e92f813b9a757f2a1502899c6fdc06032f4d0
|
data/.gitignore
CHANGED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
doxieland
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.3.0
|
data/doxieland.gemspec
CHANGED
@@ -20,8 +20,11 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
22
|
spec.add_runtime_dependency 'activesupport', '>= 4.2'
|
23
|
-
spec.add_runtime_dependency 'apidiesel', '>= 0.
|
24
|
-
spec.add_runtime_dependency '
|
23
|
+
spec.add_runtime_dependency 'apidiesel', '>= 0.12'
|
24
|
+
spec.add_runtime_dependency 'thor', '>= 0.19'
|
25
|
+
spec.add_runtime_dependency 'ruby-progressbar', '>= 1.7'
|
26
|
+
spec.add_runtime_dependency 'hirb', '>= 0.7'
|
27
|
+
spec.add_runtime_dependency 'rainbow', '>= 2.1'
|
25
28
|
|
26
29
|
spec.add_development_dependency "bundler", "~> 1.11"
|
27
30
|
spec.add_development_dependency "rake", "~> 10.0"
|
data/exe/doxieland
CHANGED
@@ -1,23 +1,206 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
2
|
require "doxieland"
|
4
|
-
require
|
3
|
+
require "thor"
|
4
|
+
require "ruby-progressbar"
|
5
|
+
require 'hirb'
|
6
|
+
|
7
|
+
class CLI < Thor
|
8
|
+
include Thor::Actions
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
program :description, 'A command line tool for downloading scans from the doxie go wi-fi'
|
10
|
+
class_option 'scanner-ip', desc:'your scanners network address', banner: 'xxx.xxx.xxx.xxx', type: :string, aliases: '-i'
|
11
|
+
class_option :password, desc:'your scanners password', type: :string, aliases: '-p'
|
9
12
|
|
10
|
-
|
13
|
+
def initialize(*args, **kargs)
|
14
|
+
super
|
15
|
+
|
16
|
+
@config = (options || HashWithIndifferentAccess.new).reverse_merge(Doxieland.config)
|
17
|
+
@client = Doxieland::Client.new(@config)
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "info", "show information about your scanner"
|
21
|
+
def info
|
22
|
+
response =
|
23
|
+
@client.api do |api|
|
24
|
+
api.get_info.result
|
25
|
+
end
|
26
|
+
|
27
|
+
puts Hirb::Helpers::AutoTable.render(
|
28
|
+
response,
|
29
|
+
description: false,
|
30
|
+
headers: false
|
31
|
+
)
|
32
|
+
end
|
11
33
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
c.action do |args, options|
|
16
|
-
Doxieland::Api.url "http://#{@ip}:8080"
|
17
|
-
api = Doxieland::Api.new
|
34
|
+
desc "list", "list all scans saved in your scanners memory"
|
35
|
+
def list
|
36
|
+
scans = @client.api { |api| api.list_scans.result }
|
18
37
|
|
19
|
-
|
20
|
-
|
38
|
+
if scans.none?
|
39
|
+
log.info "no scans found"
|
40
|
+
exit
|
21
41
|
end
|
42
|
+
|
43
|
+
puts Hirb::Helpers::AutoTable.render(
|
44
|
+
scans,
|
45
|
+
fields: [:name, :path],
|
46
|
+
description: false
|
47
|
+
)
|
48
|
+
|
49
|
+
log.info "#{scans.length} scans available"
|
22
50
|
end
|
23
|
-
|
51
|
+
|
52
|
+
desc "download", <<-EOT.strip_heredoc
|
53
|
+
download all scans from your scanner to your computer
|
54
|
+
|
55
|
+
Available placeholders for filename format strings:
|
56
|
+
%{number} - the image number
|
57
|
+
%{date} - the current date as DD.MM.YYYY
|
58
|
+
%{time} - the current time as HH:MM:SS
|
59
|
+
|
60
|
+
You can further format date and time by passing format options inside the placeholders, separated by a colon:
|
61
|
+
|
62
|
+
%{date:%Y-%m-%d}
|
63
|
+
|
64
|
+
See http://ruby-doc.org/core-2.2.0/Time.html#method-i-strftime for all available date and time formatting options.
|
65
|
+
|
66
|
+
EOT
|
67
|
+
method_option :to, desc: "the directory to save the scans to. Defaults to .", type: :string, aliases: '-t'
|
68
|
+
method_option :filenames,
|
69
|
+
type: :string,
|
70
|
+
aliases: '-f',
|
71
|
+
banner: '"FORMAT_STRING"',
|
72
|
+
desc: 'filename format string. Default: "doxie_scan_%{date}-%{number}"'
|
73
|
+
method_option :pdf, desc: "convert scans to PDF. Requires ImageMagick to be installed", type: :boolean
|
74
|
+
method_option :delete, desc: "delete scans from scanner after download", type: :boolean, aliases: '-d'
|
75
|
+
def download
|
76
|
+
if @config[:pdf]
|
77
|
+
unless command_available?('convert')
|
78
|
+
log.fatal "could not find the 'convert' command. PDF conversion requires ImageMagick to be installed"
|
79
|
+
exit(false)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
@client.create_save_path
|
84
|
+
|
85
|
+
Doxieland::Scan.save_path = @client.save_path
|
86
|
+
Doxieland::Scan.name_format = @config[:filenames] if @config[:filenames]
|
87
|
+
|
88
|
+
scans = @client.api do |api|
|
89
|
+
api.list_scans.result
|
90
|
+
end
|
91
|
+
|
92
|
+
if scans.none?
|
93
|
+
log.info "0 scans available for download on scanner"
|
94
|
+
exit
|
95
|
+
end
|
96
|
+
|
97
|
+
log.info "downloading #{scans.length} scans to " + Rainbow(@client.save_path).cyan.bright
|
98
|
+
|
99
|
+
progressbar = create_progressbar(starting_at: 0, total: scans.length)
|
100
|
+
|
101
|
+
deletable_paths = []
|
102
|
+
save_count = 0
|
103
|
+
|
104
|
+
scans.each do |remote_scan|
|
105
|
+
progressbar.title = "downloading #{remote_scan[:name]}".truncate(22).ljust(25)
|
106
|
+
|
107
|
+
scan = @client.api { |api| api.get_scan(path: remote_scan[:path]).result }
|
108
|
+
|
109
|
+
if @config[:pdf]
|
110
|
+
scan.file_type = 'pdf'
|
111
|
+
progressbar.title = "saving #{remote_scan[:name]} as PDF".truncate(22).ljust(25)
|
112
|
+
else
|
113
|
+
progressbar.title = "saving #{remote_scan[:name]}".truncate(22).ljust(25)
|
114
|
+
end
|
115
|
+
|
116
|
+
if scan.save
|
117
|
+
save_count += 1
|
118
|
+
deletable_paths << remote_scan[:delete_path]
|
119
|
+
else
|
120
|
+
log.progress_warn "skipped existing file #{scan.path}", progressbar
|
121
|
+
end
|
122
|
+
|
123
|
+
progressbar.increment
|
124
|
+
end
|
125
|
+
|
126
|
+
if options[:delete] && deletable_paths.any?
|
127
|
+
progressbar = create_progressbar(title: "deleting scans".ljust(20))
|
128
|
+
|
129
|
+
@client.api { |api| api.delete_scans(paths: deletable_paths).result }
|
130
|
+
|
131
|
+
progressbar.progress = 1
|
132
|
+
progressbar.total = 1
|
133
|
+
progressbar.finish
|
134
|
+
end
|
135
|
+
|
136
|
+
log.success "#{save_count} scans downloaded"
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "defaults", "show or set default options for doxieland"
|
140
|
+
method_option :to, desc: "the directory to save the scans to. Defaults to .", type: :string, aliases: '-t'
|
141
|
+
method_option :filenames,
|
142
|
+
type: :string,
|
143
|
+
aliases: '-f',
|
144
|
+
banner: '"FORMAT_STRING"'
|
145
|
+
method_option :pdf, desc: "convert scans to PDF. Requires ImageMagick to be installed", type: :boolean
|
146
|
+
method_option :delete, desc: "delete scans from scanner after download", type: :boolean, aliases: '-d'
|
147
|
+
def defaults
|
148
|
+
if options.none?
|
149
|
+
config = Doxieland.config
|
150
|
+
|
151
|
+
if config.any?
|
152
|
+
puts Hirb::Helpers::AutoTable.render(
|
153
|
+
config,
|
154
|
+
description: false,
|
155
|
+
headers: { 0 => 'option', 1 => 'value' }
|
156
|
+
)
|
157
|
+
else
|
158
|
+
log.info "no defaults set"
|
159
|
+
end
|
160
|
+
|
161
|
+
else
|
162
|
+
File.open(Doxieland.config_path, 'w') { |file| file << YAML.dump(options) }
|
163
|
+
|
164
|
+
puts Hirb::Helpers::AutoTable.render(
|
165
|
+
config,
|
166
|
+
description: false,
|
167
|
+
headers: { 0 => 'option', 1 => 'value' }
|
168
|
+
)
|
169
|
+
|
170
|
+
log.success "new default settings saved"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
desc "console", "starts a Pry REPL session", hide: true
|
175
|
+
def console
|
176
|
+
begin
|
177
|
+
require 'pry'
|
178
|
+
rescue LoadError
|
179
|
+
log.fatal "doxieland console requires Pry to be installed: gem install pry"
|
180
|
+
exit
|
181
|
+
end
|
182
|
+
binding.pry
|
183
|
+
end
|
184
|
+
|
185
|
+
no_commands do
|
186
|
+
def log
|
187
|
+
@client.log
|
188
|
+
end
|
189
|
+
|
190
|
+
def create_progressbar(**kargs)
|
191
|
+
kargs.reverse_merge!({
|
192
|
+
total: nil,
|
193
|
+
format: "%t (%c of %C) |%b\u{1F431}%i| %E",
|
194
|
+
progress_mark: "\u{2728}",
|
195
|
+
remainder_mark: " ", title: ' ' * 25
|
196
|
+
})
|
197
|
+
ProgressBar.create(**kargs)
|
198
|
+
end
|
199
|
+
|
200
|
+
def command_available?(command)
|
201
|
+
`which #{command}`.present? && $?.exitstatus == 0
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
CLI.start( ARGV.any? ? ARGV : ['help'] )
|
data/lib/doxieland.rb
CHANGED
@@ -2,11 +2,35 @@ require "doxieland/version"
|
|
2
2
|
|
3
3
|
require 'active_support/all'
|
4
4
|
require 'apidiesel'
|
5
|
+
require 'rainbow'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'cgi'
|
8
|
+
require 'pathname'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'open3'
|
5
11
|
|
6
|
-
require 'doxieland/handlers/
|
12
|
+
require 'doxieland/handlers/file_request'
|
7
13
|
require 'doxieland/actions/list_scans'
|
14
|
+
require 'doxieland/actions/get_info'
|
15
|
+
require 'doxieland/actions/get_scan'
|
16
|
+
require 'doxieland/actions/delete_scans'
|
8
17
|
require 'doxieland/api'
|
18
|
+
require 'doxieland/scan'
|
19
|
+
require 'doxieland/logger'
|
20
|
+
require 'doxieland/client'
|
9
21
|
|
10
22
|
module Doxieland
|
11
|
-
|
23
|
+
class AuthenticationError < StandardError; end
|
24
|
+
|
25
|
+
def self.config_path
|
26
|
+
Pathname.new('~/.doxieland').expand_path
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.config
|
30
|
+
if File.file?(config_path)
|
31
|
+
YAML.load( File.read(config_path) ).with_indifferent_access
|
32
|
+
else
|
33
|
+
HashWithIndifferentAccess.new
|
34
|
+
end
|
35
|
+
end
|
12
36
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Doxieland
|
2
|
+
module Actions
|
3
|
+
class DeleteScans < ::Apidiesel::Action
|
4
|
+
url path: '/scans/delete.json'
|
5
|
+
|
6
|
+
http_method :post
|
7
|
+
|
8
|
+
expects do
|
9
|
+
object :paths, klass: Array
|
10
|
+
end
|
11
|
+
|
12
|
+
format_parameters do |params|
|
13
|
+
params[:paths]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Doxieland
|
2
|
+
module Actions
|
3
|
+
class GetScan < ::Apidiesel::Action
|
4
|
+
use Apidiesel::Handlers::ActionResponseProcessor
|
5
|
+
use Handlers::FileRequest
|
6
|
+
|
7
|
+
url ->(base_url, request) {
|
8
|
+
base_url.path = request.parameters.delete(:path)
|
9
|
+
|
10
|
+
base_url
|
11
|
+
}
|
12
|
+
|
13
|
+
http_method :get
|
14
|
+
|
15
|
+
expects do
|
16
|
+
string :path
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -3,6 +3,22 @@ module Doxieland
|
|
3
3
|
class ListScans < ::Apidiesel::Action
|
4
4
|
http_method :get
|
5
5
|
url path: '/scans.json'
|
6
|
+
|
7
|
+
responds_with do
|
8
|
+
array do
|
9
|
+
string :path,
|
10
|
+
at: :name,
|
11
|
+
filter: ->(s) { "/scans#{s}" }
|
12
|
+
string :delete_path,
|
13
|
+
at: :name
|
14
|
+
string :thumbnail_path,
|
15
|
+
at: :name,
|
16
|
+
filter: ->(s) { "/thumbnails#{s}" }
|
17
|
+
string :name,
|
18
|
+
filter: ->(s) { s.match(/[0-9]{4,}/)[0] }
|
19
|
+
integer :size
|
20
|
+
end
|
21
|
+
end
|
6
22
|
end
|
7
23
|
end
|
8
24
|
end
|
data/lib/doxieland/api.rb
CHANGED
@@ -0,0 +1,94 @@
|
|
1
|
+
module Doxieland
|
2
|
+
class Client
|
3
|
+
attr_reader :save_path
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@options = options
|
7
|
+
@save_path = Pathname.new(options[:to] || '.').expand_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def api(&block)
|
11
|
+
Doxieland::Api.url "http://#{scanner_ip}:8080"
|
12
|
+
|
13
|
+
if @options[:password]
|
14
|
+
Doxieland::Api.http_basic_auth 'doxie', @options[:password]
|
15
|
+
end
|
16
|
+
|
17
|
+
api = Doxieland::Api.new
|
18
|
+
|
19
|
+
begin
|
20
|
+
yield api
|
21
|
+
rescue AuthenticationError => e
|
22
|
+
log.fatal e.message
|
23
|
+
exit(false)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_save_path
|
28
|
+
raise ArgumentError, "#{@save_path} is a file" if @save_path.file?
|
29
|
+
|
30
|
+
unless @save_path.directory? || @save_path == Pathname.new('.')
|
31
|
+
FileUtils.mkdir_p(@save_path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def loglevel
|
36
|
+
@options[:verbose] ? :debug : :info
|
37
|
+
end
|
38
|
+
|
39
|
+
def log
|
40
|
+
@logger ||= Logger.new(loglevel)
|
41
|
+
end
|
42
|
+
|
43
|
+
def scanner_ip
|
44
|
+
@scanner_ip ||= begin
|
45
|
+
if @options['scanner-ip']
|
46
|
+
@options['scanner-ip']
|
47
|
+
elsif @options[:ap]
|
48
|
+
'192.168.1.100'
|
49
|
+
else
|
50
|
+
discovered_ip = ssdp_discover
|
51
|
+
|
52
|
+
unless discovered_ip
|
53
|
+
log.fatal "… sorry, your scanner could not be found. Is WiFi turned on and the status light blue?"
|
54
|
+
log.info "If you know it, you can also provide the IP address manually via the --scanner-ip flag."
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
discovered_ip
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def ssdp_discover
|
64
|
+
socket = UDPSocket.new
|
65
|
+
socket.setsockopt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true
|
66
|
+
socket.setsockopt :IPPROTO_IP, :IP_MULTICAST_TTL, 1
|
67
|
+
|
68
|
+
query = [
|
69
|
+
'M-SEARCH * HTTP/1.1',
|
70
|
+
'HOST: 239.255.255.250:1900',
|
71
|
+
'MAN: "ssdp:discover"',
|
72
|
+
'ST: urn:schemas-getdoxie-com:device:Scanner:1',
|
73
|
+
# 'ST: ssdp:all',
|
74
|
+
'MX: 3',
|
75
|
+
'',
|
76
|
+
''
|
77
|
+
].join("\r\n")
|
78
|
+
|
79
|
+
log.info "trying to find your scanner on the network. Here, Doxie Doxie…"
|
80
|
+
|
81
|
+
socket.send(query, 0, '239.255.255.250', 1900)
|
82
|
+
|
83
|
+
ready = IO.select([socket], nil, nil, 10)
|
84
|
+
|
85
|
+
if ready
|
86
|
+
_, message_sender = socket.recvfrom(65507)
|
87
|
+
|
88
|
+
log.success "found the little rascal hiding at #{message_sender.last}"
|
89
|
+
|
90
|
+
message_sender.last
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|