ffi-gphoto2 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +11 -0
  3. data/CHANGELOG.md +20 -0
  4. data/README.md +19 -10
  5. data/examples/autofocus.rb +21 -0
  6. data/examples/capture.rb +8 -0
  7. data/examples/continuous_burst.rb +27 -0
  8. data/examples/list_config.rb +1 -0
  9. data/examples/list_files.rb +52 -0
  10. data/ffi-gphoto2.gemspec +3 -3
  11. data/lib/ffi/gphoto2.rb +10 -1
  12. data/lib/ffi/gphoto2/camera_abilities_list.rb +1 -1
  13. data/lib/ffi/gphoto2/camera_file_info.rb +10 -0
  14. data/lib/ffi/gphoto2/camera_file_info_audio.rb +11 -0
  15. data/lib/ffi/gphoto2/camera_file_info_fields.rb +14 -0
  16. data/lib/ffi/gphoto2/camera_file_info_file.rb +15 -0
  17. data/lib/ffi/gphoto2/camera_file_info_preview.rb +13 -0
  18. data/lib/ffi/gphoto2/camera_file_permissions.rb +9 -0
  19. data/lib/ffi/gphoto2/camera_file_status.rb +7 -0
  20. data/lib/ffi/gphoto2/camera_list.rb +1 -1
  21. data/lib/ffi/gphoto2/camera_widget.rb +1 -1
  22. data/lib/ffi/gphoto2_port/gp_port_info_list.rb +1 -1
  23. data/lib/gphoto2.rb +23 -2
  24. data/lib/gphoto2/camera.rb +9 -1
  25. data/lib/gphoto2/camera/capture.rb +22 -0
  26. data/lib/gphoto2/camera/configuration.rb +1 -0
  27. data/lib/gphoto2/camera/event.rb +3 -6
  28. data/lib/gphoto2/camera_abilities.rb +9 -0
  29. data/lib/gphoto2/camera_file.rb +25 -7
  30. data/lib/gphoto2/camera_file_info/camera_file_info.rb +52 -0
  31. data/lib/gphoto2/camera_file_info/file_camera_file_info.rb +30 -0
  32. data/lib/gphoto2/camera_widgets/camera_widget.rb +2 -6
  33. data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +0 -4
  34. data/lib/gphoto2/camera_widgets/text_camera_widget.rb +1 -3
  35. data/lib/gphoto2/entry.rb +0 -4
  36. data/lib/gphoto2/port_info.rb +0 -4
  37. data/lib/gphoto2/version.rb +1 -1
  38. data/spec/gphoto2/camera_abilities_spec.rb +23 -0
  39. data/spec/gphoto2/camera_file_info/file_camera_file_info_spec.rb +56 -0
  40. data/spec/gphoto2/camera_file_spec.rb +17 -0
  41. data/spec/gphoto2/camera_spec.rb +11 -5
  42. data/spec/gphoto2_spec.rb +18 -0
  43. data/spec/support/shared_examples/camera/capture_examples.rb +13 -0
  44. data/spec/support/shared_examples/camera/configuration_examples.rb +26 -18
  45. data/spec/support/shared_examples/camera_file_info_examples.rb +42 -0
  46. metadata +29 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 83f9c14b8b7006440420e875c32d4a93b809ce28
4
- data.tar.gz: 9e3ccef87bc4565c88c4750e64d09ccde23f3330
3
+ metadata.gz: 39745534e29e0571decd8eda706d74278a146cc1
4
+ data.tar.gz: 83c84f47474b4bd940748b11f4b65769db508550
5
5
  SHA512:
6
- metadata.gz: 9d90104862dfccd44685aefa3870d83cde93e23a0aab57df6a7eb5a7feb2d9e712ca8c3125260b36f18b090df6ea8fce60726e913b667617f551180058c12397
7
- data.tar.gz: f8536cef9cf2d3d7c9cfedb71fa0e7079280a7a7a0e7f579e1b886c4cba8e37e9316504e2298f1aabc0358638a3cd7f0ef5d20e5c87eee95ea4e15d82acef454
6
+ metadata.gz: 287d38fca23d5e76207ea9f4e93d89ee4856f4c30a7deb198c5c2dc305d1eae171b881b3427671c10d623d605ffa877e9708a1db4efbc72ceb19fe0bc3c2589b
7
+ data.tar.gz: 8e69d5a190b2db161ce6d8be0bdb153282d7407955b8ce98a8b0269ecc544b63d2ac7b3bf0a2ffcf22631f23e75694bf15d1f0d7441d8124753b8a4a4f7f83bf
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ sudo: required
2
+ dist: trusty
3
+ language: ruby
4
+ rvm:
5
+ - 2.3.1
6
+ before_install:
7
+ - gem install bundler -v 1.12.5
8
+ - sudo apt-get -qq update
9
+ - sudo apt-get install libgphoto2-6 libgphoto2-port10
10
+ - sudo ln -s /usr/lib/x86_64-linux-gnu/libgphoto2.so{.6,}
11
+ - sudo ln -s /usr/lib/x86_64-linux-gnu/libgphoto2_port.so{.10,}
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## HEAD
4
+
5
+ ## 0.6.0
6
+
7
+ * [FIX] Use the correct default filename when a `CameraFile` is a preview.
8
+ * [ADD] Add `CameraFileInfo` and related operations. `CameraFile#info` only
9
+ supports files.
10
+ * [FIX] `CameraWidget#label` calls the correct widget label function.
11
+ * [FIX] `Camera#[]=` raises an `ArgumentError` when passed an invalid key
12
+ instead of failing on `nil`.
13
+ * [ADD] Add `CameraAbilities#operations` to always return an `Integer`.
14
+ libgphoto2 does not stay in the defined enum set of `CameraOperations`;
15
+ therefore, it is treated as an integer bit field instead.
16
+ * [CHANGE] Errors are thrown as `GPhoto2::Error` rather than `RuntimeError`.
17
+ Use `#message` and `#code` to extract the the GPhoto2 error message and
18
+ return code, respectively.
19
+ * [ADD] `Camera#trigger` for trigger capture.
20
+ * [FIX] `TextCameraWidget` values can by any object that implements `#to_s`.
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # ffi-gphoto2
2
2
 
3
- **ffi-gphoto2** provides an FFI for common functions in [libgphoto2][1].
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/ffi-gphoto2.svg)](https://badge.fury.io/rb/ffi-gphoto2)
5
+ [![Build Status](https://travis-ci.org/zaeleus/ffi-gphoto2.svg?branch=master)](https://travis-ci.org/zaeleus/ffi-gphoto2)
6
+
7
+ **ffi-gphoto2** provides an FFI for common functions in [libgphoto2][gphoto].
4
8
  It also includes a facade to interact with the library in a more
5
9
  idiomatic Ruby way.
6
10
 
@@ -34,9 +38,17 @@ camera = cameras.first
34
38
  # ...or more conveniently
35
39
  camera = GPhoto2::Camera.first
36
40
 
37
- # ...or even search by model name
41
+ # search by model name
38
42
  camera = GPhoto2::Camera.where(model: /nikon/i).first
39
43
 
44
+ # the above examples require the camera be manually closed when done
45
+ camera.close
46
+
47
+ # pass a block to automatically close the camera
48
+ GPhoto2::Camera.first do |camera|
49
+ # ...
50
+ end
51
+
40
52
  # check camera abilities (see `FFI::GPhoto2::CameraOperation.symbols`)
41
53
  camera.can? :capture_image
42
54
  # => true
@@ -97,14 +109,11 @@ file.save
97
109
 
98
110
  # ...and then delete it from the camera
99
111
  file.delete
100
-
101
- # close the camera
102
- camera.finalize
103
112
  ```
104
113
 
105
- More examples can be found in [`examples/`][2]. YARD documentation can be
106
- generated using the `rake yard` task or [browsed online][3].
114
+ More examples can be found in [`examples/`][examples]. YARD documentation can be
115
+ generated using the `rake yard` task or [browsed online][rubydoc].
107
116
 
108
- [1]: http://www.gphoto.org/
109
- [2]: https://github.com/zaeleus/ffi-gphoto2/tree/master/examples
110
- [3]: http://www.rubydoc.info/gems/ffi-gphoto2/frames
117
+ [gphoto]: http://www.gphoto.org/
118
+ [examples]: https://github.com/zaeleus/ffi-gphoto2/tree/master/examples
119
+ [rubydoc]: http://www.rubydoc.info/gems/ffi-gphoto2/frames
@@ -0,0 +1,21 @@
1
+ require 'gphoto2'
2
+
3
+ # Capture an image only after successfully autofocusing.
4
+ #
5
+ # Typically, if the camera fails to autofocus, updating the `autofocusdrive`
6
+ # key will throw an "Unspecified error (-1)". This catches the exception and
7
+ # continues to autofocus until it is successful.
8
+
9
+ GPhoto2::Camera.first do |camera|
10
+ begin
11
+ camera.update(autofocusdrive: true)
12
+ rescue GPhoto2::Error
13
+ puts "autofocus failed... retrying"
14
+ camera.reload
15
+ retry
16
+ ensure
17
+ camera.update(autofocusdrive: false)
18
+ end
19
+
20
+ camera.capture
21
+ end
@@ -0,0 +1,8 @@
1
+ require 'gphoto2'
2
+
3
+ # Captures a single photo and saves it to the current working directory.
4
+
5
+ GPhoto2::Camera.first do |camera|
6
+ file = camera.capture
7
+ file.save
8
+ end
@@ -0,0 +1,27 @@
1
+ require 'gphoto2'
2
+
3
+ # This example is limited to Nikon cameras with a continuous burst mode.
4
+
5
+ # The number of frames to capture.
6
+ N = 3
7
+
8
+ GPhoto2::Camera.first do |camera|
9
+ # Set the camera to continuous burst mode.
10
+ camera.update(capturemode: 'Burst', burstnumber: N)
11
+
12
+ # Use `#trigger` instead of `#capture` so the data remains in the camera
13
+ # buffer.
14
+ camera.trigger
15
+
16
+ # Wait for the camera to process all the images.
17
+ queue = N.times.map do
18
+ event = camera.wait_for(:file_added)
19
+
20
+ # The event data contains a `CameraFile` when the `:file_added` event is
21
+ # triggered.
22
+ event.data
23
+ end
24
+
25
+ # Save all the images to the current working directory.
26
+ queue.each(&:save)
27
+ end
@@ -14,6 +14,7 @@ def visit(widget, level = 0)
14
14
 
15
15
  indent << ' '
16
16
 
17
+ puts "#{indent}label: #{widget.label}"
17
18
  puts "#{indent}type: #{widget.type}"
18
19
  puts "#{indent}value: #{widget.value}"
19
20
 
@@ -0,0 +1,52 @@
1
+ require 'gphoto2'
2
+
3
+ # Recursively list folder contents with extended metadata.
4
+
5
+ MAGNITUDES = %w[bytes KiB MiB GiB].freeze
6
+
7
+ # @param [Integer] size filesize in bytes
8
+ # @param [Integer[ precision
9
+ # @return [String]
10
+ def format_filesize(size, precision = 1)
11
+ n = 0
12
+
13
+ while size >= 1024.0 && n < MAGNITUDES.size
14
+ size /= 1024.0
15
+ n += 1
16
+ end
17
+
18
+ "%.#{precision}f %s" % [size, MAGNITUDES[n]]
19
+ end
20
+
21
+ # @param [CameraFolder] folder a root directory
22
+ def visit(folder)
23
+ files = folder.files
24
+
25
+ puts "#{folder.path} (#{files.size} files)"
26
+
27
+ files.each do |file|
28
+ info = file.info
29
+
30
+ name = file.name
31
+ # Avoid using `File#size` here to prevent having to load the data along
32
+ # with it.
33
+ size = format_filesize(info.size)
34
+ mtime = info.mtime.utc.iso8601
35
+
36
+ if info.has_field?(:width) && info.has_field?(:height)
37
+ dimensions = "#{info.width}x#{info.height}"
38
+ else
39
+ dimensions = '-'
40
+ end
41
+
42
+ puts "#{name.ljust(30)} #{size.rjust(12)} #{dimensions.rjust(12)} #{mtime}"
43
+ end
44
+
45
+ puts
46
+
47
+ folder.folders.each { |child| visit(child) }
48
+ end
49
+
50
+ GPhoto2::Camera.first do |camera|
51
+ visit(camera.filesystem)
52
+ end
data/ffi-gphoto2.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.homepage = 'https://github.com/zaeleus/ffi-gphoto2'
10
10
 
11
11
  spec.authors = ['Michael Macias']
12
- spec.email = ['mamacias@go.olemiss.edu']
12
+ spec.email = ['zaeleus@gmail.com']
13
13
 
14
14
  spec.version = GPhoto2::VERSION
15
15
  spec.license = 'MIT'
@@ -24,8 +24,8 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_development_dependency 'bundler', '~> 1.3'
26
26
  spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'rspec', '~> 3.2.0'
28
- spec.add_development_dependency 'yard', '~> 0.8.7'
27
+ spec.add_development_dependency 'rspec', '~> 3.5.0'
28
+ spec.add_development_dependency 'yard', '~> 0.9.0'
29
29
 
30
30
  spec.add_dependency 'ffi', '~> 1.9.0'
31
31
  end
data/lib/ffi/gphoto2.rb CHANGED
@@ -11,7 +11,10 @@ module FFI
11
11
  require 'ffi/gphoto2/camera_driver_status'
12
12
  require 'ffi/gphoto2/camera_event_type'
13
13
  require 'ffi/gphoto2/camera_file_access_type'
14
+ require 'ffi/gphoto2/camera_file_info_fields'
14
15
  require 'ffi/gphoto2/camera_file_operation'
16
+ require 'ffi/gphoto2/camera_file_permissions'
17
+ require 'ffi/gphoto2/camera_file_status'
15
18
  require 'ffi/gphoto2/camera_file_type'
16
19
  require 'ffi/gphoto2/camera_folder_operation'
17
20
  require 'ffi/gphoto2/camera_operation'
@@ -23,10 +26,14 @@ module FFI
23
26
  require 'ffi/gphoto2/camera_abilities'
24
27
  require 'ffi/gphoto2/camera_abilities_list'
25
28
  require 'ffi/gphoto2/camera_file'
29
+ require 'ffi/gphoto2/camera_file_info_audio'
30
+ require 'ffi/gphoto2/camera_file_info_file'
31
+ require 'ffi/gphoto2/camera_file_info_preview'
32
+ require 'ffi/gphoto2/camera_file_info'
26
33
  require 'ffi/gphoto2/camera_file_path'
34
+ require 'ffi/gphoto2/entry'
27
35
  require 'ffi/gphoto2/camera_list'
28
36
  require 'ffi/gphoto2/camera_widget'
29
- require 'ffi/gphoto2/entry'
30
37
  require 'ffi/gphoto2/gp_context'
31
38
 
32
39
  # gphoto2/gphoto2-abilities-list.h
@@ -47,10 +54,12 @@ module FFI
47
54
  attach_function :gp_camera_get_config, [Camera.by_ref, :pointer, GPContext.by_ref], :int
48
55
  attach_function :gp_camera_set_config, [Camera.by_ref, CameraWidget.by_ref, GPContext.by_ref], :int
49
56
  attach_function :gp_camera_capture, [Camera.by_ref, CameraCaptureType, CameraFilePath.by_ref, GPContext.by_ref], :int, blocking: true
57
+ attach_function :gp_camera_trigger_capture, [Camera.by_ref, GPContext.by_ref], :int, blocking: true
50
58
  attach_function :gp_camera_capture_preview, [Camera.by_ref, CameraFile.by_ref, GPContext.by_ref], :int, blocking: true
51
59
  attach_function :gp_camera_wait_for_event, [Camera.by_ref, :int, :pointer, :pointer, GPContext.by_ref], :int
52
60
  attach_function :gp_camera_folder_list_files, [Camera.by_ref, :string, CameraList.by_ref, GPContext.by_ref], :int
53
61
  attach_function :gp_camera_folder_list_folders, [Camera.by_ref, :string, CameraList.by_ref, GPContext.by_ref], :int
62
+ attach_function :gp_camera_file_get_info, [Camera.by_ref, :string, :string, CameraFileInfo.by_ref, GPContext.by_ref], :int
54
63
  attach_function :gp_camera_file_get, [Camera.by_ref, :string, :string, CameraFileType, CameraFile.by_ref, GPContext.by_ref], :int, blocking: true
55
64
  attach_function :gp_camera_file_delete, [Camera.by_ref, :string, :string, GPContext.by_ref], :int
56
65
 
@@ -5,7 +5,7 @@ module FFI
5
5
  class CameraAbilitiesList < FFI::ManagedStruct
6
6
  # libgphoto2/gphoto2-abilities-list.c
7
7
  layout :count, :int,
8
- :abilities, :pointer # CameraAbilities*
8
+ :abilities, CameraAbilities.by_ref
9
9
 
10
10
  def self.release(ptr)
11
11
  FFI::GPhoto2.gp_abilities_list_free(ptr)
@@ -0,0 +1,10 @@
1
+ module FFI
2
+ module GPhoto2
3
+ class CameraFileInfo < FFI::Struct
4
+ # libgphoto2/gphoto2-filesys.h
5
+ layout :preview, CameraFileInfoPreview,
6
+ :file, CameraFileInfoFile,
7
+ :audio, CameraFileInfoAudio
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module FFI
2
+ module GPhoto2
3
+ class CameraFileInfoAudio < FFI::Struct
4
+ # gphoto2/gphoto2-filesys.h
5
+ layout :fields, CameraFileInfoFields,
6
+ :status, CameraFileStatus,
7
+ :size, :uint64,
8
+ :type, [:char, 64]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module FFI
2
+ module GPhoto2
3
+ # gphoto2/gphoto2-filesys.h
4
+ CameraFileInfoFields = enum :none, 0,
5
+ :type, 1 << 0,
6
+ :size, 1 << 2,
7
+ :width, 1 << 3,
8
+ :height, 1 << 4,
9
+ :permissions, 1 << 5,
10
+ :status, 1 << 6,
11
+ :mtime, 1 << 7,
12
+ :all, 0xff
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module FFI
2
+ module GPhoto2
3
+ class CameraFileInfoFile < FFI::Struct
4
+ # gphoto2/gphoto2-filesys.h
5
+ layout :fields, CameraFileInfoFields,
6
+ :status, CameraFileStatus,
7
+ :size, :uint64,
8
+ :type, [:char, 64],
9
+ :width, :uint32,
10
+ :height, :uint32,
11
+ :permissions, CameraFilePermissions,
12
+ :mtime, :time_t
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ module FFI
2
+ module GPhoto2
3
+ class CameraFileInfoPreview < FFI::Struct
4
+ # gphoto2/gphoto2-filesys.h
5
+ layout :fields, CameraFileInfoFields,
6
+ :status, CameraFileStatus,
7
+ :size, :uint64,
8
+ :type, [:char, 64],
9
+ :width, :uint32,
10
+ :height, :uint32
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module FFI
2
+ module GPhoto2
3
+ # gphoto2/gphoto2-filesys.h
4
+ CameraFilePermissions = enum :none, 0,
5
+ :read, 1 << 0,
6
+ :delete, 1 << 1,
7
+ :all, 0xff
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module FFI
2
+ module GPhoto2
3
+ # gphoto2/gphoto2-filesys.h
4
+ CameraFileStatus = enum :not_downloaded,
5
+ :downloaded
6
+ end
7
+ end
@@ -4,7 +4,7 @@ module FFI
4
4
  # libgphoto2/gphoto2-list.c
5
5
  layout :used, :int,
6
6
  :max, :int,
7
- :_entry, :pointer, # Entry*
7
+ :_entry, Entry.by_ref,
8
8
  :ref_count, :int
9
9
 
10
10
  def self.release(ptr)
@@ -7,7 +7,7 @@ module FFI
7
7
  :info, [:char, 1024],
8
8
  :name, [:char, 256],
9
9
 
10
- :parent, :pointer, # CameraWidget
10
+ :parent, CameraWidget.by_ref,
11
11
 
12
12
  :value_string, :string,
13
13
  :value_int, :int,
@@ -2,7 +2,7 @@ module FFI
2
2
  module GPhoto2Port
3
3
  class GPPortInfoList < FFI::ManagedStruct
4
4
  # libgphoto2_port/libgphoto2_port/gphoto2-port-info-list.c
5
- layout :info, :pointer, # GPPortInfo*
5
+ layout :info, GPPortInfo.by_ref,
6
6
  :count, :uint,
7
7
  :iolib_count, :uint
8
8
 
data/lib/gphoto2.rb CHANGED
@@ -17,6 +17,9 @@ require 'gphoto2/camera_widgets/text_camera_widget'
17
17
  require 'gphoto2/camera_widgets/toggle_camera_widget'
18
18
  require 'gphoto2/camera_widgets/window_camera_widget'
19
19
 
20
+ require 'gphoto2/camera_file_info/camera_file_info'
21
+ require 'gphoto2/camera_file_info/file_camera_file_info'
22
+
20
23
  require 'gphoto2/camera/capture'
21
24
  require 'gphoto2/camera/configuration'
22
25
  require 'gphoto2/camera/event'
@@ -39,6 +42,24 @@ require 'gphoto2/port_result'
39
42
  require 'gphoto2/version'
40
43
 
41
44
  module GPhoto2
45
+ # A runtime error for unsuccessful return codes.
46
+ class Error < RuntimeError
47
+ # @return [Integer]
48
+ attr_reader :code
49
+
50
+ # @param [String] message
51
+ # @param [Integer] code
52
+ def initialize(message, code)
53
+ super(message)
54
+ @code = code
55
+ end
56
+
57
+ # @return [String]
58
+ def to_s
59
+ "#{super} (#{code})"
60
+ end
61
+ end
62
+
42
63
  # @return [Logger]
43
64
  def self.logger
44
65
  @logger ||= Logger.new(STDERR)
@@ -46,10 +67,10 @@ module GPhoto2
46
67
 
47
68
  # @param [Integer] rc
48
69
  # @return [void]
49
- # @raise RuntimeError when the return code is not {FFI::GPhoto2Port::GP_OK}
70
+ # @raise [GPhoto2::Error] when the return code is not {FFI::GPhoto2Port::GP_OK}
50
71
  def self.check!(rc)
51
72
  logger.debug "#{caller.first} => #{rc}" if ENV['DEBUG']
52
73
  return if rc >= FFI::GPhoto2Port::GP_OK
53
- raise "#{PortResult.as_string(rc)} (#{rc})"
74
+ raise Error.new(PortResult.as_string(rc), rc)
54
75
  end
55
76
  end