sony_camera_remote_api 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d22147c515ca8ec69ab26b0eaf6c244c04030e5e
4
- data.tar.gz: b6f7dd99685957579340411cda11133e29c3ebf6
3
+ metadata.gz: 6cffaf29bb654037c1468ab6ef14e78cbd966979
4
+ data.tar.gz: 71d002fd2b81d58c40bd4a1602c5434cdd2aa8dc
5
5
  SHA512:
6
- metadata.gz: fc07a67d20bdad6c13d02b6c3bd6fb763882fc40c540ecc734329ee7cf8cd868aa2e871e74347df9800aa3bb54431b75617fccb7181fb11c0cb2d185d9e8e68c
7
- data.tar.gz: e1f1e9047a6d92284207f5f73b00ba5a66d58fb8ddfaa4ca1151292b1144076771a24b12f7984f4096e0ed85dcc87f5d404b1e565d145febab206d7849f33970
6
+ metadata.gz: d354a41277b0ca16ebb1a861958c545ceb66f3c8d4b8fb8b208867a02038340b873a00e21f9343706b8a2cfd853d64c7111865a752e5e3c9efa3c454c4ebfce5
7
+ data.tar.gz: 848e7169aff5e97ba6a95c4d6360a3160a227cc0e890cc585dac5d7f9637c1331f053fed5ff7984b5ace0fa8ebdb60a5ef80c9ffaccd672f5f7c79b605e4673e
data/Gemfile CHANGED
@@ -2,3 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in sony_camera_remote_api.gemspec
4
4
  gemspec
5
+
6
+ if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.2.2")
7
+ # NOTE: activesupport 5.x supports only ruby 2.2.2+
8
+ gem "activesupport", ">= 4.0.0", "< 5.0.0"
9
+ end
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Sony Camera Remote API Wrapper
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/sony_camera_remote_api.svg)](https://badge.fury.io/rb/sony_camera_remote_api)
4
+
3
5
  A Ruby Gem that facilitates the use of Sony Camera Remote API.
4
6
 
5
7
  - [Backgrounds](#backgrounds)
@@ -21,12 +23,12 @@ This gem is a wrapper library that make it easy to use Sony camera functions for
21
23
 
22
24
  ## Features
23
25
 
26
+ * Easy connection to camera
24
27
  * Streaming live-view images by one method
25
28
  * Simplified contents transfer
26
29
  * Consistent interface for changing parameters safely
27
30
  * Auto reconnection
28
- * Also supports the low-level APIs call
29
- * Client application bundled
31
+ * CLI application bundled (like [Raspberry Pi camera application](https://www.raspberrypi.org/documentation/raspbian/applications/camera.md))
30
32
 
31
33
 
32
34
  ## Supported version
@@ -52,28 +54,28 @@ Or install it yourself as:
52
54
 
53
55
  ## Usage
54
56
 
55
- 1. Connect your PC (or device) to the camera with Direct Wi-Fi. If you are using Linux, it is recommended to use
56
- Shelf class like the following example.
57
+ 1. Connect your PC (or device) to the camera with Direct Wi-Fi. If your PC is **Linux** or **MacOS** machine, you can easiy do it by using `Shelf` class like following example.
57
58
  2. Create SonyCameraRemoteAPI::Camera instance with Shelf instance.
58
59
  3. Now you can access all of camera APIs and useful wrapper methods!
59
60
 
60
- This is an example code of capturing single still image.
61
+ This is an example code that takes a picture and transfer it to your PC.
61
62
 
62
63
  ```ruby
63
64
  require 'sony_camera_remote_api'
64
65
 
65
- interface = "wlan0"
66
- ssid = "DIRECT-xxxx:ILCE-QX1"
67
- pass = "xxxxxxxx"
66
+ ssid = "DIRECT-xxxx:ILCE-QX1" # SSID of camera
67
+ pass = "xxxxxxxx" # passphrase
68
+ interface = "wlan0" # interface by which you connect to the camera
68
69
 
70
+ # Connect to the camera
69
71
  shelf = SonyCameraRemoteAPI::Shelf.new 'sonycam.shelf'
70
72
  shelf.add_and_select ssid, pass, interface
71
73
  shelf.connect
72
74
 
75
+ # Initialize Camera class instance, with which you can access all camera functions.
73
76
  cam = SonyCameraRemoteAPI::Camera.new shelf
74
77
  cam.change_function_to_shoot 'still', 'Single'
75
78
  cam.capture_still
76
- # => Captured jpeg file is transferred to your PC
77
79
  ```
78
80
 
79
81
  For more information, see project's [Wiki](https://github.com/kota65535/sony_camera_remote_api/wiki).
@@ -1,5 +1,6 @@
1
1
  require 'sony_camera_remote_api/raw_api'
2
2
  require 'sony_camera_remote_api/camera_api_group'
3
+ require 'sony_camera_remote_api/retrying'
3
4
  require 'forwardable'
4
5
 
5
6
  module SonyCameraRemoteAPI
@@ -26,9 +27,18 @@ module SonyCameraRemoteAPI
26
27
  # @param [Hash] endpoints
27
28
  # @param [Proc] reconnect_by
28
29
  def initialize(endpoints, reconnect_by: nil)
29
- @raw_api_manager = RawAPIManager.new endpoints
30
+ @http = HTTPClient.new
31
+ @http.connect_timeout = @http.send_timeout = @http.receive_timeout = 30
32
+
33
+ Retrying.new(reconnect_by, @http).reconnect_and_retry do
34
+ @raw_api_manager = RawAPIManager.new endpoints, @http
35
+ end
36
+
30
37
  @api_group_manager = CameraAPIGroupManager.new self
31
- @reconnect_by = reconnect_by
38
+
39
+ @retrying = Retrying.new(reconnect_by, @http).add_common_hook do
40
+ startRecMode! timeout: 0
41
+ end
32
42
  end
33
43
 
34
44
 
@@ -40,7 +50,7 @@ module SonyCameraRemoteAPI
40
50
  params = [params] unless params.is_a? Array
41
51
  name, service, id, version = @raw_api_manager.search_method(__method__, **opts)
42
52
  response = nil
43
- reconnect_and_retry do
53
+ @retrying.reconnect_and_retry do
44
54
  if params[0]
45
55
  response = @raw_api_manager.call_api_async(service, name, params, id, version, opts[:timeout])
46
56
  else
@@ -55,7 +65,8 @@ module SonyCameraRemoteAPI
55
65
  # @return [Hash] Response of API
56
66
  def getAvailableApiList(params = [], **opts)
57
67
  name, service, id, version = @raw_api_manager.search_method(__method__, **opts)
58
- reconnect_and_retry do
68
+ log.debug "calling: #{name}"
69
+ @retrying.reconnect_and_retry do
59
70
  @raw_api_manager.call_api(service, name, params, id, version)
60
71
  end
61
72
  end
@@ -130,9 +141,10 @@ module SonyCameraRemoteAPI
130
141
  method = method.to_s.delete('!').to_sym
131
142
  params = [params] unless params.is_a? Array
132
143
  response = nil
133
- reconnect_and_retry do
144
+ @retrying.reconnect_and_retry do
134
145
  begin
135
146
  name, service, id, version = @raw_api_manager.search_method(method, **opts)
147
+ log.debug "calling: #{name}"
136
148
  if service == 'camera' || name == ''
137
149
  if opts[:timeout]
138
150
  response = call_api_safe(service, name, params, id, version, opts[:timeout], **opts)
@@ -202,37 +214,5 @@ module SonyCameraRemoteAPI
202
214
  getEvent(false)
203
215
  end
204
216
 
205
-
206
- # Execute given block. And if the block raises Error caused by Wi-Fi disconnection,
207
- # Reconnect by @reconnect_by Proc and retry the given block.
208
- # @param [Boolean] retrying If true, retry given block. If false, return immediately after reconnection.
209
- # @param [Fixnum] num Number of retry.
210
- # @param [Proc] hook Hook method called after reconnection.
211
- def reconnect_and_retry(retrying: true, num: 1, hook: nil)
212
- yield
213
- rescue HTTPClient::TimeoutError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED => e
214
- retry_count ||= 0
215
- retry_count += 1
216
- raise e if @reconnect_by.nil? || retry_count > num
217
- log.error "#{e.class}: #{e.message}"
218
- log.error 'The camera seems to be disconnected! Reconnecting...'
219
- unless @reconnect_by.call
220
- log.error 'Failed to reconnect.'
221
- raise e
222
- end
223
- log.error 'Reconnected.'
224
- @raw_api_manager.reset_connection
225
- # For cameras that use Smart Remote Control app.
226
- startRecMode! timeout: 0
227
- return unless retrying
228
-
229
- if hook
230
- unless hook.call
231
- log.error 'Before-retry hook failed.'
232
- raise e
233
- end
234
- end
235
- retry
236
- end
237
217
  end
238
218
  end
@@ -442,13 +442,13 @@ module SonyCameraRemoteAPI
442
442
  end
443
443
 
444
444
 
445
- desc 'intrec [options]', 'Do interval recording'
445
+ desc 'intstill [options]', 'Do interval recording'
446
446
  option :time, type: :numeric, desc: 'Recording time (sec)', banner: 'NSEC'
447
447
  option :interval, type: :string, desc: 'Interval (sec)', banner: 'NSEC'
448
448
  still_common_options
449
449
  common_options
450
450
  option :transfer, type: :boolean, desc: 'Transfer selected contents '
451
- def intrec
451
+ def intstill
452
452
  init_camera
453
453
  @cam.change_function_to_shoot('intervalstill')
454
454
 
@@ -187,9 +187,9 @@ module SonyCameraRemoteAPI
187
187
 
188
188
  # Connect to camera by external script
189
189
  if options[:restart]
190
- result = Scripts.restart_and_connect(camera['interface'], camera['ssid'], camera['pass'])
190
+ result = @shelf.reconnect camera['ssid']
191
191
  else
192
- result = Scripts.connect(camera['interface'], camera['ssid'], camera['pass'])
192
+ result = @shelf.connect camera['ssid']
193
193
  end
194
194
  unless result
195
195
  puts 'Failed to connect!'
@@ -30,10 +30,9 @@ module SonyCameraRemoteAPI
30
30
 
31
31
 
32
32
  # @param [Hash] endpoints Endpoint URIs
33
- def initialize(endpoints)
33
+ def initialize(endpoints, httpclient)
34
34
  @endpoints = endpoints
35
- @cli = HTTPClient.new
36
- @cli.connect_timeout = @cli.send_timeout = @cli.receive_timeout = 30
35
+ @http = httpclient
37
36
 
38
37
  unless call_api('camera', 'getApplicationInfo', [], 1, '1.0').result[1] >= '2.0.0'
39
38
  raise ServerNotCompatible, 'API version of the server < 2.0.0.'
@@ -83,7 +82,7 @@ module SonyCameraRemoteAPI
83
82
  }
84
83
  # log.debug request
85
84
  begin
86
- raw_response = @cli.post_content(@endpoints[service_type], request.to_json)
85
+ raw_response = @http.post_content(@endpoints[service_type], request.to_json)
87
86
  rescue HTTPClient::BadResponseError => e
88
87
  if e.res.status_code == 403
89
88
  raise APIForbidden.new, "Method '#{method}' returned 403 Forbidden error. Maybe this method is not allowed to general users."
@@ -116,7 +115,7 @@ module SonyCameraRemoteAPI
116
115
  'id' => id,
117
116
  'version' => version
118
117
  }
119
- conn = @cli.post_async(@endpoints[service_type], request.to_json)
118
+ conn = @http.post_async(@endpoints[service_type], request.to_json)
120
119
  start_time = Time.now if timeout
121
120
  loop do
122
121
  break if conn.finished?
@@ -184,13 +183,5 @@ module SonyCameraRemoteAPI
184
183
  def support?(method)
185
184
  @apis.key? method
186
185
  end
187
-
188
-
189
- # Reset HTTPClient.
190
- # @return [void]
191
- def reset_connection
192
- @cli.reset_all
193
- end
194
-
195
186
  end
196
187
  end
@@ -0,0 +1,105 @@
1
+ require 'sony_camera_remote_api/logging'
2
+
3
+ module SonyCameraRemoteAPI
4
+ class Retrying
5
+ include Logging
6
+
7
+ RECONNECTION_INTERVAL = 5
8
+ DEFAULT_RETRY_LIMIT = 2
9
+ DEFAULT_RECONNECTION_LIMIT = 2
10
+
11
+
12
+ def initialize(reconnect_by, httpclient)
13
+ @reconnect_by = reconnect_by
14
+ @http = httpclient
15
+ end
16
+
17
+
18
+ def reconnect_and_retry(retry_limit: DEFAULT_RETRY_LIMIT,
19
+ reconnection_limit: DEFAULT_RECONNECTION_LIMIT,
20
+ hook: nil, &block)
21
+ reconnect_and_retry_inner(retry_limit, reconnection_limit, hook, &block)
22
+ end
23
+
24
+
25
+ def reconnect_and_give_up(reconnection_limit: DEFAULT_RECONNECTION_LIMIT,
26
+ hook: nil, &block)
27
+ reconnect_and_retry_inner(0, reconnection_limit, hook, &block)
28
+ end
29
+
30
+
31
+ def reconnect_and_retry_forever(hook: nil, &block)
32
+ reconnect_and_retry_inner(:forever, :forever, hook, &block)
33
+ end
34
+
35
+
36
+ def add_common_hook(&block)
37
+ @common_hook = block
38
+ self
39
+ end
40
+
41
+
42
+ private
43
+
44
+ # Execute given block with reconnection and retry.
45
+ # When the given block raises Error caused by Wi-Fi disconnection,
46
+ # Try to reconnect by @reconnect_by Proc, and then execute again the given block.
47
+ # @param [Fixnum] retry_limit Limit number of retrying given block.
48
+ # @param [Fixnum] reconnection_limit Limit number of retrying reconnection.
49
+ # @param [Proc] one_off_hook Hook method called after reconnection.
50
+ def reconnect_and_retry_inner(retry_limit, reconnection_limit, one_off_hook)
51
+ # Try to execute the given block by 'retry_limit' times.
52
+ yield
53
+ rescue HTTPClient::BadResponseError, HTTPClient::TimeoutError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED => e
54
+ # Reraise immediately if reconnection is disabled
55
+ raise e if @reconnect_by.nil?
56
+
57
+ log.error "#{e.class}: #{e.message}"
58
+ # Init retry_count
59
+ retry_count ||= 0
60
+ if retry_count == 0
61
+ log.error 'The camera seems to be disconnected, starting retry sequence.'
62
+ forever = retry_limit.is_a?(Symbol) && retry_limit == :forever
63
+ else
64
+ if forever || retry_count < retry_limit
65
+ log.error "Failed to execute the block! (#{retry_count}/#{retry_limit})"
66
+ else
67
+ log.error 'Execution failed! Finishing retry sequence...'
68
+ raise e
69
+ end
70
+ end
71
+
72
+ # Try to reconnect by 'reconnection_limit' times.
73
+ unless try_to_reconnect reconnection_limit
74
+ log.error 'Reconnection failed! Finishing retry sequence...'
75
+ raise e
76
+ end
77
+
78
+ # Call hooks
79
+ @common_hook.call if @common_hook
80
+ one_off_hook.call if one_off_hook
81
+
82
+ # Increment retry count
83
+ retry_count += 1
84
+ retry
85
+ end
86
+
87
+
88
+ def try_to_reconnect(limit)
89
+ log.error 'Reconnecting...'
90
+ forever = limit.is_a?(Symbol) && limit == :forever
91
+ reconnection_count = 0
92
+ loop do
93
+ reconnection_count += 1
94
+ return false unless forever || reconnection_count <= limit
95
+ break if @reconnect_by.call
96
+ log.error "Failed to reconnect! Retrying... (#{reconnection_count}/#{limit})"
97
+ sleep RECONNECTION_INTERVAL
98
+ end
99
+ @http.reset_all
100
+ log.error 'Reconnected.'
101
+ true
102
+ end
103
+
104
+ end
105
+ end
@@ -12,8 +12,8 @@ module SonyCameraRemoteAPI
12
12
  # @param [String] ssid SSID of the camera to connect
13
13
  # @param [String] pass Password of the camera to connect
14
14
  # @return [Boolean] +true+ if succeeded, +false+ otherwise.
15
- def connect(interface, ssid, pass)
16
- run_external_command "sudo bash #{connection_script} #{interface} #{ssid} #{pass}"
15
+ def connect(ssid, pass, interface)
16
+ run_external_command "sudo bash #{connection_script} #{ssid} #{pass} #{interface}"
17
17
  end
18
18
 
19
19
 
@@ -22,8 +22,8 @@ module SonyCameraRemoteAPI
22
22
  # @param [String] ssid SSID of the camera to connect
23
23
  # @param [String] pass Password of the camera to connect
24
24
  # @return [Boolean] +true+ if succeeded, +false+ otherwise.
25
- def restart_and_connect(interface, ssid, pass)
26
- run_external_command "sudo bash #{connection_script} -r #{interface} #{ssid} #{pass}"
25
+ def restart_and_connect(ssid, pass, interface)
26
+ run_external_command "sudo bash #{connection_script} -r #{ssid} #{pass} #{interface}"
27
27
  end
28
28
 
29
29
 
@@ -169,7 +169,7 @@ module SonyCameraRemoteAPI
169
169
  def connect(ssid = nil)
170
170
  entry = get(ssid)
171
171
  if entry
172
- Scripts.connect entry['interface'], entry['ssid'], entry['pass']
172
+ Scripts.connect entry['ssid'], entry['pass'], entry['interface']
173
173
  else
174
174
  false
175
175
  end
@@ -183,7 +183,7 @@ module SonyCameraRemoteAPI
183
183
  def reconnect(ssid = nil)
184
184
  entry = get(ssid)
185
185
  if entry
186
- Scripts.restart_and_connect entry['interface'], entry['ssid'], entry['pass']
186
+ Scripts.restart_and_connect entry['ssid'], entry['pass'], entry['interface']
187
187
  else
188
188
  false
189
189
  end
@@ -1,4 +1,4 @@
1
1
  module SonyCameraRemoteAPI
2
2
  # Version
3
- VERSION = '0.2.2'.freeze
3
+ VERSION = '0.3.0'.freeze
4
4
  end
@@ -5,6 +5,7 @@ require 'sony_camera_remote_api/utils'
5
5
  require 'sony_camera_remote_api/camera_api'
6
6
  require 'sony_camera_remote_api/packet'
7
7
  require 'sony_camera_remote_api/shelf'
8
+ require 'sony_camera_remote_api/retrying'
8
9
  require 'core_ext/hash_patch'
9
10
  require 'httpclient'
10
11
  require 'active_support'
@@ -30,7 +31,7 @@ module SonyCameraRemoteAPI
30
31
 
31
32
  attr_reader :endpoints
32
33
 
33
- # Timeout for saving images captured by continous shooting.
34
+ # Timeout for saving images captured by continuous shooting.
34
35
  CONT_SHOOT_SAVING_TIME = 25
35
36
  # Timeout for focusing by tracking focus.
36
37
  TRACKING_FOCUS_TIMEOUT = 4
@@ -57,8 +58,11 @@ module SonyCameraRemoteAPI
57
58
  @reconnect_by = reconnect_by if reconnect_by
58
59
 
59
60
  @api_manager = CameraAPIManager.new @endpoints, reconnect_by: @reconnect_by
60
- @cli = HTTPClient.new
61
- @cli.connect_timeout = @cli.send_timeout = @cli.receive_timeout = 30
61
+ @http = HTTPClient.new
62
+ @http.connect_timeout = @http.send_timeout = @http.receive_timeout = 10
63
+ @retrying = Retrying.new(@reconnect_by, @http).add_common_hook do
64
+ startRecMode! timeout: 0
65
+ end
62
66
 
63
67
  # Some cameras which use "Smart Remote Control" app must call this method before remote shooting.
64
68
  startRecMode! timeout: 0
@@ -498,7 +502,7 @@ module SonyCameraRemoteAPI
498
502
 
499
503
  x = [[x, 100].min, 0].max
500
504
  y = [[y, 100].min, 0].max
501
- actTrackingFocus(['xPosition': x, 'yPosition': y]).result
505
+ actTrackingFocus(['xPosition' => x, 'yPosition' => y]).result
502
506
  begin
503
507
  wait_event(timeout: TRACKING_FOCUS_TIMEOUT) { |r| r[54]['trackingFocusStatus'] == 'Tracking' }
504
508
  log.info "Tracking focus (#{x}, #{y}) OK."
@@ -597,9 +601,9 @@ module SonyCameraRemoteAPI
597
601
  # Break from loop inside when timeout
598
602
  catch :finished do
599
603
  # For reconnection
600
- reconnect_and_retry_forever do
604
+ @retrying.reconnect_and_retry_forever do
601
605
  # Retrieve streaming data
602
- @cli.get_content(liveview_url) do |chunk|
606
+ @http.get_content(liveview_url) do |chunk|
603
607
  loop_start = Time.now
604
608
  received_sec = loop_start - loop_end
605
609
 
@@ -658,6 +662,8 @@ module SonyCameraRemoteAPI
658
662
  end
659
663
  end
660
664
  end
665
+ rescue StandardError => e
666
+ log.error e.backtrace.join "\n"
661
667
  ensure
662
668
  # Comes here when liveview finished or killed by signal
663
669
  puts 'Stopping Liveview...'
@@ -813,13 +819,14 @@ module SonyCameraRemoteAPI
813
819
  TRANSFER_SIZE_LIST = %w(original large small thumbnail).freeze
814
820
  # Transfer content(s) from the camera storage.
815
821
  # @note You have to set camera function to 'Contents Transfer' before calling this method.
816
- # @param [Array<Hash>] contents Array of content information, which can be obtained by get_content_list
817
- # @param [Array<String>] filenames Array of filename strings
818
- # @param [String] size Content size. available values are 'original', 'large', 'small', 'thumbnail'.
822
+ # @param [Array<Hash>] contents Array of content information, which can be obtained by get_content_list
823
+ # @param [Array<String>] filenames Array of filename strings
824
+ # @param [String, Array<String>] size Content size. available values are 'original', 'large', 'small', 'thumbnail'.
825
+ # @param [Boolean] add_postfix If +true+, postfix is appended for 'large', 'small' and 'thumbnail' content size.
819
826
  # @see get_content_list
820
827
  # @see get_date_list
821
828
  # @todo If 'contents' is directory (date), get all contents of the directory.
822
- def transfer_contents(contents, filenames=[], dir: nil, size: 'original')
829
+ def transfer_contents(contents, filenames=[], dir: nil, size: 'original', add_postfix: true)
823
830
  contents = [contents].compact unless contents.is_a? Array
824
831
  filenames = [filenames].compact unless filenames.is_a? Array
825
832
  size = [size].compact unless size.is_a? Array
@@ -838,7 +845,7 @@ module SonyCameraRemoteAPI
838
845
  end
839
846
  end
840
847
 
841
- urls_filenames = get_content_url(contents, filenames, size)
848
+ urls_filenames = get_content_url(contents, filenames, size, add_postfix: add_postfix)
842
849
  if urls_filenames.empty?
843
850
  log.warn 'No contents to be transferred.'
844
851
  return []
@@ -889,9 +896,9 @@ module SonyCameraRemoteAPI
889
896
  FileUtils.mkdir_p dir if dir
890
897
  result = true
891
898
  time = Benchmark.realtime do
892
- result = reconnect_and_give_up do
899
+ result = @retrying.reconnect_and_give_up do
893
900
  open(filepath, 'wb') do |file|
894
- @cli.get_content(url) do |chunk|
901
+ @http.get_content(url) do |chunk|
895
902
  file.write chunk
896
903
  end
897
904
  end
@@ -936,7 +943,7 @@ module SonyCameraRemoteAPI
936
943
  else
937
944
  filenames = contents.map { |c| c['content']['original'][0]['fileName'] }
938
945
  end
939
- transferred = transfer_contents contents, filenames, size: transfer_size, dir: dir
946
+ transferred = transfer_contents contents, filenames, size: transfer_size, dir: dir, add_postfix: false
940
947
  change_function_to_shoot 'still', 'Burst'
941
948
  transferred
942
949
  end
@@ -957,9 +964,9 @@ module SonyCameraRemoteAPI
957
964
  contents = get_content_list type: 'still', sort: 'descending', count: num_shots
958
965
  if prefix
959
966
  filenames = generate_sequencial_filenames prefix, 'JPG', num: contents.size
960
- transferred = transfer_contents contents, filenames, dir: dir
967
+ transferred = transfer_contents contents, filenames, dir: dir, add_postfix: false
961
968
  else
962
- transferred = transfer_contents contents, size: transfer_size, dir: dir
969
+ transferred = transfer_contents contents, size: transfer_size, dir: dir, add_postfix: false
963
970
  end
964
971
  change_function_to_shoot 'intervalstill'
965
972
  transferred
@@ -1090,7 +1097,7 @@ module SonyCameraRemoteAPI
1090
1097
  end
1091
1098
 
1092
1099
 
1093
- def get_content_list_sub(source, type: nil, target: 'all', view:, sort:, count: nil)
1100
+ def get_content_list_sub(source, type: nil, target: 'all', view: nil, sort: nil, count: nil)
1094
1101
  max_count = getContentCount([{'uri' => source, 'type' => type, 'view' => view}]).result[0]['count']
1095
1102
  count = count ? [max_count, count].min : max_count
1096
1103
  contents = []
@@ -1113,9 +1120,9 @@ module SonyCameraRemoteAPI
1113
1120
  log.debug url
1114
1121
  log.info "Transferring #{filepath}..."
1115
1122
  time = Benchmark.realtime do
1116
- reconnect_and_retry(hook: method(:change_function_to_transfer)) do
1123
+ @retrying.reconnect_and_retry(hook: method(:change_function_to_transfer)) do
1117
1124
  open(filepath, 'wb') do |file|
1118
- @cli.get_content(url) do |chunk|
1125
+ @http.get_content(url) do |chunk|
1119
1126
  file.write chunk
1120
1127
  end
1121
1128
  end
@@ -1128,13 +1135,14 @@ module SonyCameraRemoteAPI
1128
1135
  end
1129
1136
 
1130
1137
 
1131
- def get_content_url(contents, filenames, sizes)
1138
+ def get_content_url(contents, filenames, sizes, add_postfix: true)
1132
1139
  urls_filenames = []
1133
1140
  contents.zip(filenames).product(sizes).map { |e| e.flatten }.each do |content, filename, size|
1134
1141
  next unless content
1135
1142
 
1136
1143
  filename ||= content['content']['original'][0]['fileName']
1137
1144
  base = File.basename filename, '.*'
1145
+ postfix = add_postfix ? "_#{size}" : ''
1138
1146
  case size
1139
1147
  when 'original'
1140
1148
  raise StandardError if content['content']['original'].size > 1 # FIXME: When do we come here???
@@ -1151,13 +1159,13 @@ module SonyCameraRemoteAPI
1151
1159
  filename = "#{base}#{ext}"
1152
1160
  when 'large'
1153
1161
  url = content['content']['largeUrl']
1154
- filename = "#{base}_large.JPG"
1162
+ filename = "#{base}#{postfix}.JPG"
1155
1163
  when 'small'
1156
1164
  url = content['content']['smallUrl']
1157
- filename = "#{base}_small.JPG"
1165
+ filename = "#{base}#{postfix}.JPG"
1158
1166
  when 'thumbnail'
1159
1167
  url = content['content']['thumbnailUrl']
1160
- filename = "#{base}_thumbnail.JPG"
1168
+ filename = "#{base}#{postfix}.JPG"
1161
1169
  end
1162
1170
  if url.empty?
1163
1171
  log.error "Skipping empty URL for file: #{filename}, size: #{size}"
@@ -1168,44 +1176,5 @@ module SonyCameraRemoteAPI
1168
1176
  urls_filenames
1169
1177
  end
1170
1178
 
1171
-
1172
- # Try to run given block.
1173
- # If an error raised because of the Wi-Fi disconnection, try to reconnect and run the block again.
1174
- # If error still continues, give it up and raise the error.
1175
- def reconnect_and_retry(retrying: true, num: 1, hook: nil)
1176
- yield
1177
- rescue HTTPClient::BadResponseError, HTTPClient::TimeoutError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED => e
1178
- retry_count ||= 0
1179
- retry_count += 1
1180
- raise e if @reconnect_by.nil? || retry_count > num
1181
- log.error "#{e.class}: #{e.message}"
1182
- log.error 'The camera seems to be disconnected! Reconnecting...'
1183
- unless @reconnect_by.call
1184
- log.error 'Failed to reconnect.'
1185
- raise e
1186
- end
1187
- log.error 'Reconnected.'
1188
- @cli.reset_all
1189
- # For cameras that use Smart Remote Control app.
1190
- startRecMode! timeout: 0
1191
-
1192
- if hook
1193
- unless hook.call
1194
- log.error 'Before-retry hook failed.'
1195
- raise e
1196
- end
1197
- end
1198
- retry if retrying
1199
- end
1200
-
1201
- def reconnect_and_retry_forever(&block)
1202
- reconnect_and_retry &block
1203
- rescue HTTPClient::BadResponseError, HTTPClient::TimeoutError, Errno::EHOSTUNREACH, Errno::ECONNREFUSED => e
1204
- retry
1205
- end
1206
-
1207
- def reconnect_and_give_up(&block)
1208
- reconnect_and_retry(retrying: false, &block)
1209
- end
1210
1179
  end
1211
1180
  end
@@ -0,0 +1,61 @@
1
+ #!/bin/bash
2
+ set -u
3
+ export LANG="C"
4
+
5
+ function usage() {
6
+ cat <<EOT
7
+ Description:
8
+ *** For MacOS ***
9
+ Add a static route to the SSDP broadcast address via the specified interface.
10
+ If interface is not given, the internal Wi-Fi device is used.
11
+ Usage:
12
+ bash ${0##*/} <interface-name>
13
+ EOT
14
+ }
15
+
16
+ # Verify the number of arguments
17
+ if [ $# -gt 1 ]; then usage && exit 1; fi
18
+
19
+ if [ -z "${1+x}" ]; then
20
+ # Get the device name associated with Wi-Fi network service
21
+ WIFI_DEV_NAME=$(networksetup -listallhardwareports | grep -w Wi-Fi -A1 | awk '/^Device:/{ print $2 }')
22
+ if [ -z "${WIFI_DEV_NAME}" ]; then
23
+ echo "Internal Wi-Fi device not found!"
24
+ exit 2
25
+ fi
26
+ else
27
+ # Verify the specified interface exists
28
+ WIFI_DEV_NAME=$(networksetup -listallhardwareports | awk "/^Device: $1/{ print \$2 }")
29
+ if [ -z "${WIFI_DEV_NAME}" ]; then
30
+ echo "Specified interface not found!"
31
+ exit 2
32
+ fi
33
+ fi
34
+
35
+ # The broadcast address for SSDP discover
36
+ SSDP_ADDR=239.255.255.250
37
+
38
+
39
+ # Verify the existence of the route
40
+ route -n get ${SSDP_ADDR} | grep -q "interface: ${WIFI_DEV_NAME}"
41
+ if [ $? -eq 0 ]; then
42
+ echo 'Route already configured.'
43
+ exit 0
44
+ fi
45
+
46
+ # Add the route via the internal Wi-Fi device
47
+ route add -host ${SSDP_ADDR} -interface ${WIFI_DEV_NAME}
48
+ if [ $? -ne 0 ]; then
49
+ echo "Failed to add route to ${SSDP_ADDR} via ${WIFI_DEV_NAME}"
50
+ exit 1
51
+ fi
52
+
53
+ # Confirm the addition of the route completes
54
+ route -n get ${SSDP_ADDR} | grep -q "interface: ${WIFI_DEV_NAME}"
55
+ if [ $? -ne 0 ]; then
56
+ echo "Something goes wrong!"
57
+ exit 2
58
+ fi
59
+
60
+ echo 'Route added successfully.'
61
+ exit 0
@@ -0,0 +1,108 @@
1
+ #!/bin/bash
2
+ set -u
3
+ export LANG="C"
4
+
5
+ function usage() {
6
+ cat <<EOT
7
+ Description:
8
+ *** For Mac ***
9
+ Connect to the specified wireless network.
10
+ Cannot be used with a third-party Wi-Fi adapter.
11
+ Usage:
12
+ bash ${0##*/} <network-SSID> <password>
13
+ Option:
14
+ -r : restart interface even if already connected.
15
+ EOT
16
+ }
17
+
18
+ # Options
19
+ while getopts r OPT
20
+ do
21
+ case $OPT in
22
+ r) FLAG_RESTART=1
23
+ ;;
24
+ \?) usage && exit 1
25
+ ;;
26
+ esac
27
+ done
28
+ shift $((OPTIND - 1))
29
+
30
+ # Verify the number of arguments
31
+ if [ $# -ne 2 ]; then usage && exit 1; fi
32
+
33
+ # Get the device name associated with Wi-Fi network service
34
+ WIFI_DEV_NAME=$(networksetup -listallhardwareports | grep -w Wi-Fi -A1 | awk '/^Device:/{ print $2 }')
35
+ if [ -z "${WIFI_DEV_NAME}" ]; then
36
+ echo "Internal Wi-Fi device not found!"
37
+ exit 2
38
+ fi
39
+
40
+ # If --restart option is not given, exit if already connected.
41
+ if [ -z ${FLAG_RESTART+x} ]; then
42
+ networksetup -getairportnetwork ${WIFI_DEV_NAME} | grep -wq $1
43
+ if [ $? -eq 0 ]; then
44
+ echo 'Already connected.'
45
+ exit 0
46
+ fi
47
+ fi
48
+
49
+ # Power ON the interface if not.
50
+ POWER_ON_WAIT=1
51
+ networksetup -getairportpower ${WIFI_DEV_NAME} | grep -wiq 'off'
52
+ if [ $? -eq 0 ]; then
53
+ networksetup -setairportpower ${WIFI_DEV_NAME} on
54
+ if [ $? -ne 0 ]; then
55
+ echo "Failed to power on ${WIFI_DEV_NAME}."
56
+ exit 3
57
+ fi
58
+ sleep ${POWER_ON_WAIT}
59
+ fi
60
+
61
+ # Full path of airport command
62
+ AIRPORT_CMD='/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport'
63
+
64
+ # Scan networks with specified SSID
65
+ SCAN_RETRY=4
66
+ SCAN_INTERVAL=15
67
+ try=1
68
+ while true; do
69
+ ${AIRPORT_CMD} --scan=$1 | grep -wq $1
70
+ if [ $? -eq 0 ]; then
71
+ break
72
+ elif [ ${try} -eq ${SCAN_RETRY} ]; then
73
+ echo "The wi-fi network with SSID '$1' not found."
74
+ exit 4
75
+ fi
76
+ ((try++))
77
+ echo "Scan failed, retrying... (${try}/${SCAN_RETRY})"
78
+ sleep ${SCAN_INTERVAL}
79
+ done
80
+
81
+ # Try to connect
82
+ CONNECTION_RETRY=3
83
+ RETRY_INTERVAL=2
84
+ remain=${CONNECTION_RETRY}
85
+ while [ ${remain} -gt 0 ]
86
+ do
87
+ # Judge from the output because 'networksetup -setairportnetwork' returns always 0.
88
+ networksetup -setairportnetwork ${WIFI_DEV_NAME} $1 $2 | grep -wq 'Error'
89
+ if [ $? -ne 0 ]; then
90
+ break
91
+ fi
92
+ ((remain--))
93
+ sleep ${RETRY_INTERVAL}
94
+ done
95
+ if [ ${remain} -eq 0 ]; then
96
+ echo "Failed to join the network. Password may be incorrect."
97
+ exit 5
98
+ fi
99
+
100
+ # Confirm the connection completes.
101
+ networksetup -getairportnetwork ${WIFI_DEV_NAME} | grep -wq $1
102
+ if [ $? -ne 0 ]; then
103
+ echo 'Something wrong occured!'
104
+ exit 6
105
+ fi
106
+
107
+ echo 'Connected successfully.'
108
+ exit 0
@@ -4,12 +4,16 @@ export LANG="C"
4
4
 
5
5
  function usage() {
6
6
  cat <<EOT
7
- Usage: bash ${0##*/} [options] <interface-name> <network-SSID> <password>
8
- Option: -r : restart interface even if already connected.
7
+ Description:
8
+ Connect to the specified wireless network.
9
+ Usage:
10
+ bash ${0##*/} [options] <network-SSID> <password> <interface-name>
11
+ Option:
12
+ -r : restart interface even if already connected.
9
13
  EOT
10
14
  }
11
15
 
12
- # オプション解析
16
+ # Options
13
17
  while getopts r OPT
14
18
  do
15
19
  case $OPT in
@@ -21,46 +25,46 @@ do
21
25
  done
22
26
  shift $((OPTIND - 1))
23
27
 
24
- # 引数チェック
28
+ # Verify the number of arguments
25
29
  if [ $# -ne 3 ]; then usage && exit 1; fi
26
30
 
27
- # 指定されたインターフェースが存在するか調べる
28
- iwconfig $1 > /dev/null 2>&1
31
+ # Verify the existence of specified interface
32
+ iwconfig $3 > /dev/null 2>&1
29
33
  if [ $? -ne 0 ]; then
30
- echo "Device named '$1' not found."
34
+ echo "Specified interface '$3' not found."
31
35
  exit 2
32
36
  fi
33
37
 
34
- # -rオプション有りなら接続済みでも再接続
38
+ # If --restart option is not given, exit if already connected.
35
39
  if [ -z ${FLAG_RESTART+x} ]; then
36
- # すでに接続されていれば終了
37
- iwconfig $1 | grep -wq "ESSID:\"$2\"" && ifconfig $1 | grep -q 'inet addr'
40
+ # Exit if already connected
41
+ iwconfig $3 | grep -wq "ESSID:\"$1\"" && ifconfig $3 | grep -q 'inet addr'
38
42
  if [ $? -eq 0 ]; then
39
43
  echo "Already connected."
40
44
  exit 0
41
45
  fi
42
46
  fi
43
47
 
44
- # インターフェースを再起動
48
+ # Restart the interface
45
49
  POWER_ON_WAIT=1
46
- ifconfig $1 down
47
- ifconfig $1 up
50
+ ifconfig $3 down
51
+ ifconfig $3 up
48
52
  if [ $? -ne 0 ]; then
49
- echo "Failed to activate interface $1"
53
+ echo "Failed to activate interface '$3'"
50
54
  exit 3
51
55
  fi
52
56
  sleep ${POWER_ON_WAIT}
53
57
 
54
- # 指定のSSIDを持つアクセスポイントがあるか調べる
58
+ # Scan networks with specified SSID
55
59
  SCAN_RETRY=4
56
- SCAN_INTERVAL=10
60
+ SCAN_INTERVAL=15
57
61
  try=1
58
62
  while true; do
59
- iwlist $1 scan | grep -wq "ESSID:\"$2\""
63
+ iwlist $3 scan | grep -wq "ESSID:\"$1\""
60
64
  if [ $? -eq 0 ]; then
61
65
  break
62
66
  elif [ ${try} -eq ${SCAN_RETRY} ]; then
63
- echo "The wi-fi network with SSID '$2' not found."
67
+ echo "The wi-fi network with SSID '$1' not found."
64
68
  exit 4
65
69
  fi
66
70
  ((try++))
@@ -68,42 +72,43 @@ while true; do
68
72
  sleep ${SCAN_INTERVAL}
69
73
  done
70
74
 
71
- # wpa_supplicantが動いていたら殺す
72
- pkill -f "wpa_supplicant.+-i *$1 .*"
75
+ # Kill if wpa_supplicant is alive
76
+ pkill -f "wpa_supplicant.+-i *$3 .*"
73
77
 
74
- # SSIDを設定
75
- iwconfig $1 essid $2
78
+ # Set SSID for the interface
79
+ iwconfig $3 essid $1
76
80
 
77
- # WPA認証タイムアウト秒数
78
- WPA_AUTH_TIMEOUT=20
81
+ # Try to connect
82
+ WPA_AUTH_TIMEOUT=30
79
83
  is_connected=false
80
84
  current_time=$(date +%s)
81
- # wpa_supplicantnohupで起動し接続。stdbufはバッファリングを防止するために必要
85
+ # Run wpa_supplicant by nohup. stdbuf is introduced to prevent buffering.
82
86
  while read -t ${WPA_AUTH_TIMEOUT} line; do
83
87
  echo " $line"
88
+ # Judge from the output log
84
89
  echo $line | grep -wq 'CTRL-EVENT-CONNECTED'
85
90
  if [ $? -eq 0 ]; then
86
91
  is_connected=true
87
92
  break
88
93
  fi
89
- # タイムアウト判定
94
+ # Is timeout?
90
95
  if [ $(($(date +%s) - ${current_time})) -gt ${WPA_AUTH_TIMEOUT} ]; then
91
96
  echo "Timeout."
92
97
  break
93
98
  fi
94
- done < <(nohup bash -c "wpa_passphrase $2 $3 | stdbuf -oL wpa_supplicant -B -i $1 -D wext -c /dev/stdin -f /dev/stdout")
95
- # done < <(nohup bash -c "wpa_passphrase $2 $3 | stdbuf -oL wpa_supplicant -i $1 -D wext -c /dev/stdin 2>&1 | stdbuf -oL tee wpa.log &")
99
+ done < <(nohup bash -c "wpa_passphrase $1 $2 | stdbuf -oL wpa_supplicant -B -i $3 -D wext -c /dev/stdin -f /dev/stdout")
100
+ # done < <(nohup bash -c "wpa_passphrase $1 $2 | stdbuf -oL wpa_supplicant -i $3 -D wext -c /dev/stdin 2>&1 | stdbuf -oL tee wpa.log &")
96
101
  if ! $is_connected; then
97
102
  echo 'WPA authentication failed.'
98
- pkill -f "wpa_supplicant.+-i *$1 .*"
103
+ pkill -f "wpa_supplicant.+-i *$3 .*"
99
104
  exit 5
100
105
  fi
101
106
 
102
- # IPアドレス割り当て
103
- ifconfig $1 | grep -q 'inet addr'
107
+ # Assign IP address by DHCP
108
+ ifconfig $3 | grep -q 'inet addr'
104
109
  if [ $? -ne 0 ]; then
105
- dhclient $1
106
- ifconfig $1 | grep -q 'inet addr'
110
+ dhclient $3
111
+ ifconfig $3 | grep -q 'inet addr'
107
112
  if [ $? -ne 0 ]; then
108
113
  echo 'IP address cannot not be assgined.'
109
114
  exit 6
data/scripts/connect.sh CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  function usage() {
4
4
  cat <<EOT
5
- Usage: bash ${0##*/} <interface-name> <network-SSID> <password>
5
+ Usage: bash ${0##*/} <network-SSID> <password> <interface-name>
6
6
  EOT
7
7
  }
8
8
 
9
9
  CONNECT_OPTION=''
10
10
 
11
- # オプション解析
11
+ # Options
12
12
  while getopts r OPT
13
13
  do
14
14
  case $OPT in
@@ -20,17 +20,22 @@ do
20
20
  done
21
21
  shift $((OPTIND - 1))
22
22
 
23
+ # Verify the number of arguments
23
24
  if [ $# -ne 3 ]; then usage && exit 1; fi
24
25
 
25
26
  cd $(dirname $0)
26
- arch=$(uname)
27
- case "${arch}" in
28
- 'Linux' | 'Darwin' )
29
- bash ./${arch}/connect_wifi.sh ${CONNECT_OPTION} $1 $2 $3 && bash ./${arch}/add_ssdp_route.sh $1
27
+ sysname=$(uname)
28
+ case "${sysname}" in
29
+ 'Linux')
30
+ bash ./${sysname}/connect_wifi.sh ${CONNECT_OPTION} $1 $2 $3 && bash ./${sysname}/add_ssdp_route.sh $3
31
+ exit $?
32
+ ;;
33
+ 'Darwin')
34
+ bash ./${sysname}/connect_wifi.sh ${CONNECT_OPTION} $1 $2 && bash ./${sysname}/add_ssdp_route.sh
30
35
  exit $?
31
36
  ;;
32
37
  * )
33
- echo "The platform '${arch}' is not supported!"
38
+ echo "The system '${sysname}' is not supported!"
34
39
  exit 1
35
40
  ;;
36
41
  esac
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "rspec", "~> 3.5"
28
28
  spec.add_development_dependency "simplecov"
29
29
 
30
- spec.add_dependency "activesupport"
30
+ spec.add_dependency "activesupport", ">= 4.0.0"
31
31
  spec.add_dependency "frisky"
32
32
  spec.add_dependency "httpclient"
33
33
  spec.add_dependency "nokogiri"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sony_camera_remote_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kota65535
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-10-01 00:00:00.000000000 Z
11
+ date: 2016-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 4.0.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 4.0.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: frisky
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -195,11 +195,14 @@ files:
195
195
  - lib/sony_camera_remote_api/logging.rb
196
196
  - lib/sony_camera_remote_api/packet.rb
197
197
  - lib/sony_camera_remote_api/raw_api.rb
198
+ - lib/sony_camera_remote_api/retrying.rb
198
199
  - lib/sony_camera_remote_api/scripts.rb
199
200
  - lib/sony_camera_remote_api/shelf.rb
200
201
  - lib/sony_camera_remote_api/ssdp.rb
201
202
  - lib/sony_camera_remote_api/utils.rb
202
203
  - lib/sony_camera_remote_api/version.rb
204
+ - scripts/Darwin/add_ssdp_route.sh
205
+ - scripts/Darwin/connect_wifi.sh
203
206
  - scripts/Linux/add_ssdp_route.sh
204
207
  - scripts/Linux/connect_wifi.sh
205
208
  - scripts/connect.sh