ffi-gphoto2 0.5.1 → 0.6.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.
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