sony_camera_remote_api 0.2.2 → 0.3.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 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