Class: MTP::Device

Inherits:
Object
  • Object
show all
Defined in:
lib/mtp/device.rb

Constant Summary collapse

FUNCTIONAL_MODES =
{ 0x0000 => "Undefined", 0x0001 => "Sleep state",
0xC001 => "Non-responsive playback", 0xC002 => "Responsive playback" }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#manufacturerObject (readonly)

Returns the value of attribute manufacturer.



18
19
20
# File 'lib/mtp/device.rb', line 18

def manufacturer
  @manufacturer
end

#modelObject (readonly)

Returns the value of attribute model.



18
19
20
# File 'lib/mtp/device.rb', line 18

def model
  @model
end

Class Method Details

.findObject

discover connected usb devices



6
7
8
9
10
11
12
# File 'lib/mtp/device.rb', line 6

def find
  ary = Array.new
  USB.devices.find_all do |usbdev|
    ary << Device.new(usbdev) if Protocol.mtp?(usbdev)
  end
  ary
end

Instance Method Details

#[](id) ⇒ Object

call-seq:

device[id]             => object
device[filename]       => object

retrieve an object from the device by its id or filename.



170
171
172
173
174
175
176
177
# File 'lib/mtp/device.rb', line 170

def [](id)
  objects if @objects.nil?
  if id.is_a?(String)
    @objects_by_filename[id]
  else
    @objects[id]
  end
end

#clearObject

clear cached data



126
127
128
# File 'lib/mtp/device.rb', line 126

def clear
  @storages = @objects = @objects_by_filename = @tracks = @playlists = nil
end

#closeObject

close the device



110
111
112
113
114
115
116
117
# File 'lib/mtp/device.rb', line 110

def close
  MTP.logger.info("closing session")
  begin
    @ph.transaction.close_session.expect("OK")
  ensure
    @ph.close
  end
end

#delete(object) ⇒ Object

delete object from the device



258
259
260
261
262
263
264
265
266
267
268
# File 'lib/mtp/device.rb', line 258

def delete(object)
  MTP.logger.info("deleting object #{object.id}")
  @ph.transaction.delete_object(object.id, 0).expect("OK")
  @objects_by_filename[object.filename] = @objects[object.id] = nil
  if object.is_a?(Track)
    tracks.delete(object)
  elsif object.is_a?(Playlist)
    playlists.delete(object)
  end
  object
end

#get(object, io = nil, &block) ⇒ Object

call-seq:

device.get(object)                                     => object
device.get(object, io)                                 => object
device.get(object, io) { |read, total| ... }           => object

retrieve object to the device

If the io argument is given, the binary data to be read from the device will be written to it. If a block is given, each time a packet is received, it will be called with the number of bytes read and the total number of bytes to read.



235
236
237
238
239
240
241
242
243
244
245
# File 'lib/mtp/device.rb', line 235

def get(object, io = nil, &block)
  MTP.logger.info("receiving object #{object}")
  t = @ph.transaction
  if io.nil?
    t.get_object(object.id, &block)
  else
    t.get_object_with_io(io, object.id, &block)
  end
  t.expect("OK")
  return t.data.payload
end

#inspectObject

:nodoc:



65
66
67
# File 'lib/mtp/device.rb', line 65

def inspect #:nodoc:
  "#<MTP::Device manufacturer=%s model=%s>" % [ @manufacturer.inspect, @model.inspect ]
end

#objectsObject

return the MTP objects stored on the device



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/mtp/device.rb', line 142

def objects
  return @objects.values unless @objects.nil?

  MTP.logger.info("fetching objects")
  @objects = {}
  @objects_by_filename = {}
  
  # fetch on each storage
  storages.each do |storage|
    t = @ph.transaction.get_object_handles(storage.id, 0, 0)
    t.expect("OK")
    
    num, *objects = t.data.payload.unpack("I*")
    
    MTP.logger.info("retrieving #{num} objects")
    objects.each do |id|
      o = @objects[id] = Object.load(@ph, id, storage)
      @objects_by_filename[o.filename] = o
    end
  end
  @objects.values
end

#openObject

open the device

If a block is given, the library will ensure that the device is correctly closed outside of the block, even if an exception is raised in it.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/mtp/device.rb', line 80

def open
  MTP.logger.info("opening session")
  @ph.open
  begin
    t = @ph.transaction.open_session(1)
    unless t.response.one_of?("OK", "SessionAlreadyOpened")
      reset
      t = @ph.transaction.open_session(1)
    end
  rescue Exception => e
    @ph.close
    raise e
  end

  if block_given?
    begin
      yield self
    ensure
      begin
        close
      rescue Exception => e
        MTP.logger.error(e)
      end
    end
  else
    self
  end
end

#playlistsObject

the list of playlists on the device



253
254
255
# File 'lib/mtp/device.rb', line 253

def playlists
  @playlists ||= objects.select { |o| o.is_a?(Playlist) }
end

#resetObject

reset the device (this will close all sessions



120
121
122
123
# File 'lib/mtp/device.rb', line 120

def reset
  MTP.logger.info("reseting device")
  @ph.transaction.reset_device.expect("OK", "SessionNotOpen")
end

#send(object, io = nil, &block) ⇒ Object

call-seq:

device.send(object)                                     => object
device.send(object, io)                                 => object
device.send(object, io) { |written, total| ... }        => object

send object to the device

If the io argument is given, the binary data to be send to the device will be read from it. If a block is given, each time a packet is send, it will be called with the number of bytes written and the total number of bytes to write.

Raises:



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/mtp/device.rb', line 190

def send(object, io = nil, &block)
  raise InvalidObject.new(object) unless object.valid?
  
  MTP.logger.info("sending object #{object}")
  t = @ph.transaction
  t.data = Data.new(object.pack)
  t.send_object_info(0, 0)
  t.expect("OK")
  storage_id, parent_id, object_id  = *t.response.parameters

  storage = storages.detect { |s| s.id == storage_id }
  object.instance_eval do
    @storage, @parent_id, @id = storage, parent_id, object_id
  end

  object.before_sending(@ph)
  
  t = @ph.transaction
  t.data = object.data
  if io.nil?
    t.send_object(&block)
  else
    t.send_object_with_io(io, &block)
  end
  t.expect("OK")
  object.after_sending
  @objects_by_filename[object.filename] = @objects[object.id] = object
  if object.is_a?(Track)
    tracks << object
  elsif object.is_a?(Playlist)
    playlists << object
  end
end

#storagesObject

return the available storages on the device



131
132
133
134
135
136
137
138
139
# File 'lib/mtp/device.rb', line 131

def storages
  return @storages unless @storages.nil?
  
  MTP.logger.info("fetching storages")
  t = @ph.transaction.get_storage_ids
  t.expect("OK")
  num, *ids = t.data.payload.unpack("I*")
  @storages = ids.map { |id| Storage.load(@ph, id) }
end

#support?(request) ⇒ Boolean

check if the device support a particular request

Returns:

  • (Boolean)


70
71
72
73
# File 'lib/mtp/device.rb', line 70

def support?(request)
  code = Datacode.new(request)
  return @operations_supported.include?(code)
end

#tracksObject

the list of tracks on the device



248
249
250
# File 'lib/mtp/device.rb', line 248

def tracks
  @tracks ||= objects.select { |o| o.is_a?(Track) }
end