ffi-gphoto2 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -1
- data/Rakefile +2 -0
- data/examples/intervalometer.rb +9 -7
- data/examples/list_config.rb +3 -5
- data/examples/live_view.rb +6 -12
- data/examples/record_movie.rb +10 -13
- data/ffi-gphoto2.gemspec +2 -1
- data/lib/ffi/gphoto2_port.rb +2 -2
- data/lib/gphoto2/camera/capture.rb +59 -0
- data/lib/gphoto2/camera/configuration.rb +136 -0
- data/lib/gphoto2/camera/event.rb +56 -0
- data/lib/gphoto2/camera/filesystem.rb +44 -0
- data/lib/gphoto2/camera.rb +95 -166
- data/lib/gphoto2/camera_abilities.rb +6 -6
- data/lib/gphoto2/camera_abilities_list.rb +7 -6
- data/lib/gphoto2/camera_event.rb +1 -8
- data/lib/gphoto2/camera_file.rb +15 -5
- data/lib/gphoto2/camera_file_path.rb +3 -5
- data/lib/gphoto2/camera_folder.rb +11 -0
- data/lib/gphoto2/camera_list.rb +3 -10
- data/lib/gphoto2/camera_widgets/camera_widget.rb +27 -14
- data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +1 -0
- data/lib/gphoto2/camera_widgets/range_camera_widget.rb +1 -0
- data/lib/gphoto2/context.rb +2 -6
- data/lib/gphoto2/entry.rb +2 -0
- data/lib/gphoto2/port_info.rb +8 -6
- data/lib/gphoto2/port_info_list.rb +5 -6
- data/lib/gphoto2/port_result.rb +2 -0
- data/lib/gphoto2/struct.rb +11 -0
- data/lib/gphoto2/version.rb +1 -1
- data/lib/gphoto2.rb +13 -0
- data/spec/gphoto2/camera_abilities_list_spec.rb +5 -5
- data/spec/gphoto2/camera_abilities_spec.rb +7 -7
- data/spec/gphoto2/camera_file_path_spec.rb +2 -2
- data/spec/gphoto2/camera_file_spec.rb +7 -7
- data/spec/gphoto2/camera_folder_spec.rb +3 -3
- data/spec/gphoto2/camera_list_spec.rb +3 -3
- data/spec/gphoto2/camera_spec.rb +37 -272
- data/spec/gphoto2/camera_widgets/date_camera_widget_spec.rb +1 -1
- data/spec/gphoto2/camera_widgets/radio_camera_widget_spec.rb +4 -4
- data/spec/gphoto2/camera_widgets/range_camera_widget_spec.rb +2 -2
- data/spec/gphoto2/camera_widgets/text_camera_widget_spec.rb +1 -1
- data/spec/gphoto2/camera_widgets/toggle_camera_widget_spec.rb +2 -2
- data/spec/gphoto2/context_spec.rb +1 -1
- data/spec/gphoto2/entry_spec.rb +2 -2
- data/spec/gphoto2/port_info_list_spec.rb +4 -4
- data/spec/gphoto2/port_info_spec.rb +9 -10
- data/spec/spec_helper.rb +0 -7
- data/spec/support/shared_examples/camera/capture_examples.rb +41 -0
- data/spec/support/shared_examples/camera/configuration_examples.rb +161 -0
- data/spec/support/shared_examples/camera/event_examples.rb +39 -0
- data/spec/support/shared_examples/camera/filesystem_examples.rb +46 -0
- data/spec/support/shared_examples/camera_widget_examples.rb +13 -13
- metadata +32 -4
data/lib/gphoto2/camera.rb
CHANGED
@@ -1,9 +1,24 @@
|
|
1
1
|
module GPhoto2
|
2
2
|
class Camera
|
3
3
|
include FFI::GPhoto2
|
4
|
+
include GPhoto2::Struct
|
4
5
|
|
5
|
-
|
6
|
+
include Capture
|
7
|
+
include Configuration
|
8
|
+
include Event
|
9
|
+
include Filesystem
|
6
10
|
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :model
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
attr_reader :port
|
16
|
+
|
17
|
+
# @example
|
18
|
+
# cameras = GPhoto2::Camera.all
|
19
|
+
# # => [#<GPhoto2::Camera>, #<GPhoto2::Camera>, ...]
|
20
|
+
#
|
21
|
+
# @return [Array<GPhoto2::Camera>] a list of all available devices
|
7
22
|
def self.all
|
8
23
|
context = Context.new
|
9
24
|
|
@@ -20,37 +35,74 @@ module GPhoto2
|
|
20
35
|
entries
|
21
36
|
end
|
22
37
|
|
23
|
-
|
38
|
+
# @example
|
39
|
+
# camera = GPhoto2::Camera.first
|
40
|
+
#
|
41
|
+
# begin
|
42
|
+
# # ...
|
43
|
+
# ensure
|
44
|
+
# camera.finalize
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# # Alternatively, pass a block, which will automatically close the camera.
|
48
|
+
# GPhoto2::Camera.first do |camera|
|
49
|
+
# # ...
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# @return [GPhoto2::Camera] the first detected camera
|
53
|
+
# @raise [RuntimeError] when no devices are detected
|
54
|
+
def self.first(&blk)
|
24
55
|
entries = all
|
25
56
|
raise RuntimeError, 'no devices detected' if entries.empty?
|
26
|
-
entries.first
|
27
|
-
|
28
|
-
|
29
|
-
|
57
|
+
camera = entries.first
|
58
|
+
autorelease(camera, &blk)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @example
|
62
|
+
# model = 'Nikon DSC D5100 (PTP mode)'
|
63
|
+
# port = 'usb:250,006'
|
64
|
+
#
|
65
|
+
# camera = GPhoto2::Camera.open(model, port)
|
66
|
+
#
|
67
|
+
# begin
|
68
|
+
# # ...
|
69
|
+
# ensure
|
70
|
+
# camera.finalize
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# # Alternatively, pass a block, which will automatically close the camera.
|
74
|
+
# GPhoto2::Camera.open(model, port) do |camera|
|
75
|
+
# # ...
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# @param [String] model
|
79
|
+
# @param [String] port
|
80
|
+
# @return [GPhoto2::Camera]
|
81
|
+
def self.open(model, port, &blk)
|
30
82
|
camera = new(model, port)
|
31
|
-
|
32
|
-
if block_given?
|
33
|
-
begin
|
34
|
-
yield camera
|
35
|
-
ensure
|
36
|
-
camera.finalize
|
37
|
-
end
|
38
|
-
else
|
39
|
-
camera
|
40
|
-
end
|
83
|
+
autorelease(camera, &blk)
|
41
84
|
end
|
42
85
|
|
86
|
+
# @example
|
87
|
+
# # Find the cameras whose model names contain Nikon.
|
88
|
+
# cameras = GPhoto2::Camera.where(model: /nikon/i)
|
89
|
+
#
|
90
|
+
# @param [Hash] condition
|
91
|
+
# @return [Array<GPhoto2::Camera>]
|
43
92
|
def self.where(condition)
|
44
93
|
name = condition.keys.first
|
45
94
|
pattern = condition.values.first
|
46
95
|
all.select { |c| c.send(name).match(pattern) }
|
47
96
|
end
|
48
97
|
|
98
|
+
# @param [String] model
|
99
|
+
# @param [String] port
|
49
100
|
def initialize(model, port)
|
101
|
+
super
|
50
102
|
@model, @port = model, port
|
51
|
-
@dirty = false
|
52
103
|
end
|
53
104
|
|
105
|
+
# @return [void]
|
54
106
|
def finalize
|
55
107
|
@context.finalize if @context
|
56
108
|
@window.finalize if @window
|
@@ -58,111 +110,59 @@ module GPhoto2
|
|
58
110
|
end
|
59
111
|
alias_method :close, :finalize
|
60
112
|
|
113
|
+
# @return [void]
|
61
114
|
def exit
|
62
115
|
_exit
|
63
116
|
end
|
64
117
|
|
65
|
-
|
66
|
-
save
|
67
|
-
path = _capture(type)
|
68
|
-
CameraFile.new(self, path.folder, path.name)
|
69
|
-
end
|
70
|
-
|
71
|
-
def preview
|
72
|
-
save
|
73
|
-
capture_preview
|
74
|
-
end
|
75
|
-
|
76
|
-
# timeout in milliseconds
|
77
|
-
def wait(timeout = 2000)
|
78
|
-
wait_for_event(timeout)
|
79
|
-
end
|
80
|
-
|
81
|
-
def wait_for(event_type)
|
82
|
-
begin
|
83
|
-
event = wait
|
84
|
-
end until event.type == event_type
|
85
|
-
|
86
|
-
event
|
87
|
-
end
|
88
|
-
|
118
|
+
# @return [FFI::GPhoto::Camera]
|
89
119
|
def ptr
|
90
120
|
@ptr || (init && @ptr)
|
91
121
|
end
|
92
122
|
|
123
|
+
# @return [GPhoto2::CameraAbilities]
|
93
124
|
def abilities
|
94
125
|
@abilities || (init && @abilities)
|
95
126
|
end
|
96
127
|
|
128
|
+
# @return [GPhoto2::PortInfo]
|
97
129
|
def port_info
|
98
130
|
@port_info || (init && @port_info)
|
99
131
|
end
|
100
132
|
|
133
|
+
# @return [GPhoto2::Context]
|
101
134
|
def context
|
102
135
|
@context ||= Context.new
|
103
136
|
end
|
104
137
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
def filesystem(root = '/')
|
114
|
-
root = "/#{root}" if root[0] != '/'
|
115
|
-
CameraFolder.new(self, root)
|
116
|
-
end
|
117
|
-
alias_method :/, :filesystem
|
118
|
-
|
119
|
-
def file(file)
|
120
|
-
file_get(file)
|
121
|
-
end
|
122
|
-
|
123
|
-
def delete(file)
|
124
|
-
file_delete(file)
|
125
|
-
end
|
126
|
-
|
127
|
-
def [](key)
|
128
|
-
config[key.to_s]
|
129
|
-
end
|
130
|
-
|
131
|
-
def []=(key, value)
|
132
|
-
self[key].value = value
|
133
|
-
@dirty = true
|
134
|
-
value
|
135
|
-
end
|
136
|
-
|
137
|
-
def update(attributes = {})
|
138
|
-
attributes.each do |key, value|
|
139
|
-
self[key] = value
|
140
|
-
end
|
141
|
-
|
142
|
-
save
|
143
|
-
end
|
144
|
-
|
145
|
-
def dirty?
|
146
|
-
@dirty
|
147
|
-
end
|
148
|
-
|
138
|
+
# @example
|
139
|
+
# camera.can? :capture_image
|
140
|
+
# # => true
|
141
|
+
#
|
142
|
+
# @param [CameraOperation] operation
|
143
|
+
# @return [Boolean]
|
149
144
|
def can?(operation)
|
150
145
|
(abilities[:operations] & CameraOperation[operation]) != 0
|
151
146
|
end
|
152
147
|
|
153
|
-
|
154
|
-
return false unless dirty?
|
155
|
-
set_config
|
156
|
-
@dirty = false
|
157
|
-
true
|
158
|
-
end
|
148
|
+
private
|
159
149
|
|
160
|
-
|
161
|
-
|
150
|
+
# Ensures the given camera is finalized when passed a block.
|
151
|
+
#
|
152
|
+
# If no block is given, the camera is returned and the caller must must
|
153
|
+
# manually close it.
|
154
|
+
def self.autorelease(camera)
|
155
|
+
if block_given?
|
156
|
+
begin
|
157
|
+
yield camera
|
158
|
+
ensure
|
159
|
+
camera.finalize
|
160
|
+
end
|
161
|
+
else
|
162
|
+
camera
|
163
|
+
end
|
162
164
|
end
|
163
165
|
|
164
|
-
private
|
165
|
-
|
166
166
|
def init
|
167
167
|
new
|
168
168
|
set_abilities(CameraAbilities.find(@model))
|
@@ -193,80 +193,9 @@ module GPhoto2
|
|
193
193
|
@abilities = abilities
|
194
194
|
end
|
195
195
|
|
196
|
-
def _capture(type)
|
197
|
-
path = CameraFilePath.new
|
198
|
-
rc = gp_camera_capture(ptr, type, path.ptr, context.ptr)
|
199
|
-
GPhoto2.check!(rc)
|
200
|
-
path
|
201
|
-
end
|
202
|
-
|
203
|
-
def capture_preview
|
204
|
-
file = CameraFile.new(self)
|
205
|
-
rc = gp_camera_capture_preview(ptr, file.ptr, context.ptr)
|
206
|
-
GPhoto2.check!(rc)
|
207
|
-
file
|
208
|
-
end
|
209
|
-
|
210
|
-
def get_config
|
211
|
-
widget_ptr = FFI::MemoryPointer.new(FFI::GPhoto2::CameraWidget)
|
212
|
-
rc = gp_camera_get_config(ptr, widget_ptr, context.ptr)
|
213
|
-
GPhoto2.check!(rc)
|
214
|
-
widget = FFI::GPhoto2::CameraWidget.new(widget_ptr.read_pointer)
|
215
|
-
CameraWidget.factory(widget)
|
216
|
-
end
|
217
|
-
|
218
|
-
def set_config
|
219
|
-
rc = gp_camera_set_config(ptr, window.ptr, context.ptr)
|
220
|
-
GPhoto2.check!(rc)
|
221
|
-
end
|
222
|
-
|
223
|
-
def file_get(file, type = :normal)
|
224
|
-
rc = gp_camera_file_get(ptr, file.folder, file.name, type, file.ptr, context.ptr)
|
225
|
-
GPhoto2.check!(rc)
|
226
|
-
file
|
227
|
-
end
|
228
|
-
|
229
|
-
def file_delete(file)
|
230
|
-
rc = gp_camera_file_delete(ptr, file.folder, file.name, context.ptr)
|
231
|
-
GPhoto2.check!(rc)
|
232
|
-
end
|
233
|
-
|
234
196
|
def unref
|
235
197
|
rc = gp_camera_unref(ptr)
|
236
198
|
GPhoto2.check!(rc)
|
237
199
|
end
|
238
|
-
|
239
|
-
def wait_for_event(timeout)
|
240
|
-
# assume CameraEventType is an int
|
241
|
-
type = FFI::MemoryPointer.new(:int)
|
242
|
-
|
243
|
-
data = FFI::MemoryPointer.new(:pointer)
|
244
|
-
data_ptr = FFI::MemoryPointer.new(:pointer)
|
245
|
-
data_ptr.write_pointer(data)
|
246
|
-
|
247
|
-
rc = gp_camera_wait_for_event(ptr, timeout, type, data_ptr, context.ptr)
|
248
|
-
GPhoto2.check!(rc)
|
249
|
-
|
250
|
-
type = CameraEventType[type.read_int]
|
251
|
-
data = data_ptr.read_pointer
|
252
|
-
|
253
|
-
data =
|
254
|
-
case type
|
255
|
-
when :unknown
|
256
|
-
data.null? ? nil : data.read_string
|
257
|
-
when :file_added
|
258
|
-
path_ptr = FFI::GPhoto2::CameraFilePath.new(data)
|
259
|
-
path = CameraFilePath.new(path_ptr)
|
260
|
-
CameraFile.new(self, path.folder, path.name)
|
261
|
-
when :folder_added
|
262
|
-
path_ptr = FFI::GPhoto2::CameraFilePath.new(data)
|
263
|
-
path = CameraFilePath.new(path_ptr)
|
264
|
-
CameraFolder.new(self, '%s/%s' % [path.folder, path.name])
|
265
|
-
else
|
266
|
-
nil
|
267
|
-
end
|
268
|
-
|
269
|
-
CameraEvent.new(type, data)
|
270
|
-
end
|
271
200
|
end
|
272
201
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module GPhoto2
|
2
2
|
class CameraAbilities
|
3
3
|
include FFI::GPhoto2
|
4
|
+
include GPhoto2::Struct
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
# @param [String] model the name of the device
|
7
|
+
# @return [GPhoto2::CameraAbilities]
|
7
8
|
def self.find(model)
|
8
9
|
context = Context.new
|
9
10
|
|
@@ -16,20 +17,19 @@ module GPhoto2
|
|
16
17
|
abilities
|
17
18
|
end
|
18
19
|
|
20
|
+
# @param [GPhoto2::CameraAbilitiesList] camera_abilities_list
|
21
|
+
# @param [Integer] index
|
19
22
|
def initialize(camera_abilities_list, index)
|
20
23
|
@camera_abilities_list = camera_abilities_list
|
21
24
|
@index = index
|
22
25
|
get_abilities
|
23
26
|
end
|
24
27
|
|
28
|
+
# @return [Object]
|
25
29
|
def [](field)
|
26
30
|
ptr[field]
|
27
31
|
end
|
28
32
|
|
29
|
-
def to_ptr
|
30
|
-
@ptr
|
31
|
-
end
|
32
|
-
|
33
33
|
private
|
34
34
|
|
35
35
|
def get_abilities
|
@@ -1,33 +1,34 @@
|
|
1
1
|
module GPhoto2
|
2
2
|
class CameraAbilitiesList
|
3
3
|
include FFI::GPhoto2
|
4
|
+
include GPhoto2::Struct
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
# @param [GPhoto2::Context] context
|
7
7
|
def initialize(context)
|
8
8
|
@context = context
|
9
9
|
new
|
10
10
|
load
|
11
11
|
end
|
12
12
|
|
13
|
+
# @return [GPhoto2::CameraList]
|
13
14
|
def detect
|
14
15
|
_detect
|
15
16
|
end
|
16
17
|
|
18
|
+
# @param [String] model
|
19
|
+
# @return [Integer]
|
17
20
|
def lookup_model(model)
|
18
21
|
_lookup_model(model)
|
19
22
|
end
|
20
23
|
alias_method :index, :lookup_model
|
21
24
|
|
25
|
+
# @param [Integer] index
|
26
|
+
# @return [GPhoto2::CameraAbilities]
|
22
27
|
def at(index)
|
23
28
|
CameraAbilities.new(self, index)
|
24
29
|
end
|
25
30
|
alias_method :[], :at
|
26
31
|
|
27
|
-
def to_ptr
|
28
|
-
@ptr
|
29
|
-
end
|
30
|
-
|
31
32
|
private
|
32
33
|
|
33
34
|
def new
|
data/lib/gphoto2/camera_event.rb
CHANGED
data/lib/gphoto2/camera_file.rb
CHANGED
@@ -1,39 +1,49 @@
|
|
1
1
|
module GPhoto2
|
2
2
|
class CameraFile
|
3
3
|
include FFI::GPhoto2
|
4
|
+
include GPhoto2::Struct
|
4
5
|
|
5
|
-
|
6
|
+
# @return [String]
|
7
|
+
attr_reader :folder
|
6
8
|
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @param [GPhoto2::Camera] camera
|
13
|
+
# @param [String] folder
|
14
|
+
# @param [String] name
|
7
15
|
def initialize(camera, folder = nil, name = nil)
|
8
16
|
@camera = camera
|
9
17
|
@folder, @name = folder, name
|
10
18
|
new
|
11
19
|
end
|
12
20
|
|
21
|
+
# @return [Boolean]
|
13
22
|
def preview?
|
14
23
|
@folder.nil? && @name.nil?
|
15
24
|
end
|
16
25
|
|
26
|
+
# @param [String] pathname
|
27
|
+
# @return [Integer] the number of bytes written
|
17
28
|
def save(pathname = default_filename)
|
18
29
|
File.binwrite(pathname, data)
|
19
30
|
end
|
20
31
|
|
32
|
+
# @return [void]
|
21
33
|
def delete
|
22
34
|
@camera.delete(self)
|
23
35
|
end
|
24
36
|
|
37
|
+
# @return [String]
|
25
38
|
def data
|
26
39
|
data_and_size.first
|
27
40
|
end
|
28
41
|
|
42
|
+
# @return [Integer]
|
29
43
|
def size
|
30
44
|
data_and_size.last
|
31
45
|
end
|
32
46
|
|
33
|
-
def to_ptr
|
34
|
-
@ptr
|
35
|
-
end
|
36
|
-
|
37
47
|
private
|
38
48
|
|
39
49
|
def data_and_size
|