Class: EverboxClient::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/everbox_client/runner.rb

Constant Summary collapse

DEFAULT_OPTIONS =
{
  :pwd => '/home',
  :config_file => '~/.everbox_client/config',
  :consumer_key => 'TFshQutcGMifMPCtcUFWsMtTIqBg8bAqB55XJO8P',
  :consumer_secret => '9tego848novn68kboENkhW3gTy9rE2woHWpRwAwQ',
  :oauth_site => 'http://account.everbox.com',
  :fs_site => 'http://fs.everbox.com',
  :chunk_size => 1024*1024*4,
  :proxy => nil
}

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Runner

Returns a new instance of Runner.



52
53
54
55
56
# File 'lib/everbox_client/runner.rb', line 52

def initialize(opts={})
  opts ||= {}
  config_file = opts[:config_file] || DEFAULT_OPTIONS[:config_file]
  @options = DEFAULT_OPTIONS.merge(options_from_env).merge(load_config(config_file)).merge(opts)
end

Instance Method Details

#_get_upload_urls(filename, remote_path) ⇒ Object



297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/everbox_client/runner.rb', line 297

def _get_upload_urls(filename, remote_path)
  basename = File.basename(filename)
  target_path = File.expand_path_unix(basename, remote_path)
  keys = calc_digests(filename)
  params = {
    :path      => target_path,
    :keys      => keys,
    :chunkSize => @options[:chunk_size],
    :fileSize  => File.open(filename).stat.size,
    :base      => ''
  }
  JSON.parse(access_token.post(fs(:prepare_put), JSON.dump(params), {'Content-Type' => 'text/plain' }).body)
end

#cat(*args) ⇒ Object



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/everbox_client/runner.rb', line 237

def cat(*args)
  args.each do |filename|
    path = File.expand_path_unix(filename, @options[:pwd])
    data = {:path => path}
    response = access_token.post(fs(:get), JSON.dump(data), {'Content-Type' => 'text/plain' })
    fail response.inspect if response.code != "200"
    url = JSON.parse(response.body)["dataurl"]
    Net::HTTP.get_response(URI.parse(url)) do |response|
      fail response.inspect if response.code != "200"
      response.read_body do |seg|
        STDOUT.write(seg)
        STDOUT.flush
      end
    end
  end
end

#cd(newpath = nil) ⇒ Object



376
377
378
379
380
381
# File 'lib/everbox_client/runner.rb', line 376

def cd(newpath = nil)
  newpath ||= "/home"
  @options[:pwd] = File.expand_path_unix(newpath, @options[:pwd])
  @options[:pwd] = "/home" unless @options[:pwd].start_with? "/home"
  puts "current dir: #{@options[:pwd]}"
end

#config(*args) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/everbox_client/runner.rb', line 146

def config(*args)
  if args.size == 0
    @options.each do |k, v|
      puts "#{k}\t#{v}"
    end
  elsif args.size == 2
    @options[args[0].to_sym] = args[1]
  else
    raise "Usage: everbox config [KEY VALUE]"
  end
end

#download_file(path, local_path, opts = {}) ⇒ Object



269
270
271
272
273
274
275
276
277
278
279
# File 'lib/everbox_client/runner.rb', line 269

def download_file(path, local_path, opts = {})
  data = {:path => path}
  info = JSON.parse(access_token.post(fs(:get), JSON.dump(data), {'Content-Type' => 'text/plain' }).body)
  if opts[:url_only]
    puts info["dataurl"]
  else
    puts "Downloading `#{File.basename(path)}' with curl"
    ofname = File.expand_path_unix(File.basename(path), local_path)
    system('curl', '-o', ofname, info["dataurl"])
  end
end

#download_path(path) ⇒ Object



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/everbox_client/runner.rb', line 462

def download_path(path)
  local_path = File.expand_path_unix('.')
  remote_path = File.expand_path_unix(path, @options[:pwd])

  jobs = []
  jobs << [remote_path, local_path]

  until jobs.empty?
    remote_path, local_path = jobs.pop
    entry = path_info(remote_path)
    if entry.nil? or entry.deleted?
      next
    end

    if entry.dir?
      new_local_path = File.expand_path_unix(entry.basename, local_path)
      FileUtils.makedirs(new_local_path)
      entry.entries.each do |x|
        jobs << [x.path, new_local_path]
      end
    else
      download_file(remote_path, local_path)
    end
  end
end

#dump_configObject



364
365
366
367
368
369
370
# File 'lib/everbox_client/runner.rb', line 364

def dump_config
  config = {}
  [:access_token, :access_secret, :pwd, :consumer_key, :consumer_secret, :oauth_site, :fs_site].each do |k|
    config[k] = @options[k] unless @options[k].nil?
  end
  save_config(@options[:config_file], config)
end

#fs_infoObject



510
511
512
513
514
515
516
# File 'lib/everbox_client/runner.rb', line 510

def fs_info
  response = access_token.get fs :info
  data = JSON.parse(response.body)
  puts "Disk Space Info"
  puts "  used: #{data["used"]}"
  puts "  total: #{data["total"]}"
end

#get(*args) ⇒ Object

Raises:

  • (ArgumentError)


223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/everbox_client/runner.rb', line 223

def get(*args)
  filename = args.shift
  url_only = false
  if filename == '-u'
    url_only = true
    filename = args.shift
  end

  raise ArgumentError, "filename is required" if filename.nil?

  path = File.expand_path_unix(filename, @options[:pwd])
  download_file(path, File.expand_path_unix('.'), :url_only => url_only)
end

#help(arg = nil) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/everbox_client/runner.rb', line 58

def help(arg = nil)
  case arg
  when nil
    puts "Usage: everbox help COMMAND"
  when 'cat'
    puts "Usage: everbox cat [PATH]..."
  when 'cd'
    puts <<DOC
Usage: everbox cd [newpath]
切换工作目录, newpath 可以是相对路径或者绝对路径, 没有 newpath 会切换目录到 "/home"
注意: cd 并不会检查服务器上该目录是否真的存在
DOC
  when 'config'
    puts <<DOC
Usage:
  everbox config
  显示当前的配置

  everbox config KEY VALUE
  设置配置
DOC
  when 'get'
    puts <<DOC
Usage: everbox get [-u] FILENAME
下载文件, 注意可能会覆盖本地的同名文件, 如果指定 "-u", 则只打印下载 url
DOC
  when 'info'
    puts <<DOC
Usage: everbox info
显示用户信息
DOC
  when 'login'
    puts <<DOC
Usage:

  everbox login
  登录 everbox, 登录完成后的 token 保存在 $HOME/.everbox_client/config
DOC
  when 'ls'
    puts <<DOC
Usage: everbox ls [path]
显示当前目录下的文件和目录
DOC
  when 'lsdir'
    puts <<DOC
Usage: everbox lsdir
显示当前目录下的目录
DOC

  when 'mirror'
    puts <<DOC
Usage:
  everbox mirror DIRNAME
  下载目录到本地

  everbox mirror -R DIRNAME
  上传目录到服务器
DOC
  when 'mkdir'
    puts <<DOC
Usage: everbox mkdir DIRNAME
创建目录
DOC
  when 'prepare_put'
    puts <<DOC
Usage: everbox prepare_put FILENAME
只运行 prepare_put 部分
DOC
  when 'put'
    puts <<DOC
Usage: everbox put FILENAME [FILENAME]...
上传文件
DOC
  when 'pwd'
    puts <<DOC
Usage: everbox pwd
显示当前目录
DOC
  when 'rm'
    puts <<DOC
Usage: everbox rm DIRNAME [DIRNAME]...
删除文件或目录
DOC
  else
    puts "Not Documented"
  end
end

#infoObject

显示用户信息



489
490
491
492
493
# File 'lib/everbox_client/runner.rb', line 489

def info
  
  puts
  fs_info
end

#loginObject



159
160
161
# File 'lib/everbox_client/runner.rb', line 159

def 
  
end

#ls(path = '.') ⇒ Object



163
164
165
166
167
168
169
170
171
172
# File 'lib/everbox_client/runner.rb', line 163

def ls(path = '.')
  data = {:path => File.expand_path_unix(path, @options[:pwd])}
  response = access_token.post(fs(:get), JSON.dump(data), {'Content-Type' => 'text/plain' })
  fail response.inspect if response.code != "200"
  info = JSON.parse(response.body)
  info["entries"].each do |entry|
    entry = PathEntry.new(entry)
    puts entry.to_line
  end
end

#lsdirObject



174
175
176
177
178
179
180
181
# File 'lib/everbox_client/runner.rb', line 174

def lsdir
  data = {:path => @options[:pwd]}
  info = JSON.parse(access_token.post(fs(:get), JSON.dump(data), {'Content-Type' => 'text/plain' }).body)
  info["entries"].each do |entry|
    entry = PathEntry.new(entry)
    puts entry.to_line if entry.dir?
  end
end

#make_remote_path(path, opts = {}) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/everbox_client/runner.rb', line 188

def make_remote_path(path, opts = {})
  data = {
    :path => path,
    :editTime => edit_time
  }
  response = access_token.post(fs(:mkdir), data.to_json, {'Content-Type' => 'text/plain'})
  case response.code
  when "200"
    #
  when "409"
    unless opts[:ignore_conflict]
      raise Exception, "directory already exist: `#{path}'"
    end
  end
end

#mirror(*args) ⇒ Object

Raises:



383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/everbox_client/runner.rb', line 383

def mirror(*args)
  raise Exception, "everbox mirror [-R] pathname" if args.empty?

  upload = false
  if args[0] == '-R'
    upload = true
    args.shift
  end

  path = args.shift
  if path == '--'
    path = args.shift
  end

  if path.nil? or ! args.empty?
    raise Exception, "everbox mirror [-R] pathname"
  end

  if upload
    upload_path(path)
  else
    download_path(path)
  end
end

#mkdir(path) ⇒ Object



183
184
185
186
# File 'lib/everbox_client/runner.rb', line 183

def mkdir(path)
  path = File.expand_path_unix(path, @options[:pwd])
  make_remote_path(path)
end

#prepare_put(filename) ⇒ Object



290
291
292
293
294
# File 'lib/everbox_client/runner.rb', line 290

def prepare_put(filename)
  _get_upload_urls(filename, @options[:pwd])["required"].each do |x|
    puts "#{x["index"]}\t#{x["url"]}"
  end
end

#put(*filenames) ⇒ Object



281
282
283
284
285
286
287
288
# File 'lib/everbox_client/runner.rb', line 281

def put(*filenames)
  fail "at lease one FILE required" if filenames.empty?
  filenames.each do |filename|
    puts "uploading #{filename}"
    upload_file(filename, @options[:pwd])
    puts
  end
end

#pwdObject



372
373
374
# File 'lib/everbox_client/runner.rb', line 372

def pwd
  puts @options[:pwd]
end

#rm(*pathes) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/everbox_client/runner.rb', line 204

def rm(*pathes)
  fail "at least one path required" if pathes.empty?
  pathes = pathes.map {|path| File.expand_path_unix(path, @options[:pwd])}

  data = {
    :paths => pathes,
  }
  response = access_token.post(fs(:delete), data.to_json, {'Content-Type' => 'text/plain'})
  case response.code
  when "200"
    #
  when "409"
    raise Exception, "directory already exist: `#{path}'"
  else
    raise UnknownResponseException, response
  end
end

#thumbnail(*args) ⇒ Object



254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/everbox_client/runner.rb', line 254

def thumbnail(*args)
  args.each do |filename|
    path = File.expand_path_unix(filename, @options[:pwd])
    if ['.flv', '.mp4', '.3gp'].include? File.extname(path).downcase
      aimType = '0x20000'
    else
      aimType = '0'
    end
    data = {:path => path, :aimType => aimType}
    response = access_token.post(fs('/2/thumbnail'), JSON.dump(data), {'Content-Type' => 'text/plain' })
    fail response.inspect if response.code != "200"
    puts JSON.parse(response.body)["url"]
  end
end

#upload_file(filename, remote_path) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/everbox_client/runner.rb', line 311

def upload_file(filename, remote_path)
  basename = File.basename(filename)
  target_path = "#{remote_path}/#{basename}"
  keys = calc_digests(filename)
  params = {
    :path      => target_path,
    :keys      => keys,
    :chunkSize => @options[:chunk_size],
    :fileSize  => File.open(filename).stat.size,
    :base      => ''
  }
  begin
    response = access_token.post(fs(:prepare_put), JSON.dump(params), {'Content-Type' => 'text/plain' })
    raise ResponseError.new(response) if response.code != '200'
  rescue ResponseError => e
    if e.response.code != '409'
      puts "[PREPARE_PUT] meet #{e.message}, retry"
      retry
    else
      raise
    end
  end
  info = JSON.parse(response.body)
  raise "bad response: #{info}" if info["required"].nil?
  File.open(filename) do |f|
    info["required"].each do |x|
      begin
        puts "upload block ##{x["index"]}"
        f.seek(x["index"] * @options[:chunk_size])
        code, response = http_request x['url'], f.read(@options[:chunk_size]), :method => :put
        if code != 200
          raise code.to_s
        end
      rescue => e
        puts "[UPLOAD_BLOCK] meet #{e.class}: #{e.message}, retry"
        retry
      end
    end
  end


  ftime = (Time.now.to_i * 1000 * 1000 * 10).to_s
  params = params.merge :editTime => ftime, :mimeType => 'application/octet-stream'
  code, response = access_token.post(fs(:commit_put), params.to_json, {'Content-Type' => 'text/plain'})
  pp code, response
rescue ResponseError
  raise
rescue => e
  puts "[UPLOAD_FILE] meet #{e.class}: #{e.message}, retry"
  retry
end

#upload_path(path) ⇒ Object



408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/everbox_client/runner.rb', line 408

def upload_path(path)
  local_path = File.expand_path_unix(path)
  remote_path = @options[:pwd]

  jobs = []
  jobs << [remote_path, local_path]
  until jobs.empty?
    remote_path, local_path = jobs.pop
    if File.directory? local_path
      puts "upload dir: #{local_path}"
      new_remote_path = File.expand_path_unix(File.basename(local_path), remote_path)
      begin
        make_remote_path(new_remote_path, :ignore_conflict=>true)
      rescue => e
        puts "[MAKE_REMOTE_PATH] meet #{e.class}: #{e}, retry"
        retry
      end

      remote_filenames = []
      data = {:path => new_remote_path}
      response = access_token.post(fs(:get), JSON.dump(data), {'Content-Type' => 'text/plain' })
      fail response.inspect if response.code != "200"
      info = JSON.parse(response.body)
      info["entries"].each do |entry|
        entry = PathEntry.new(entry)
        remote_filenames << entry.basename if entry.file?
      end

      Dir.entries(local_path).each do |filename|
        next if ['.', '..'].include? filename
        if remote_filenames.include? filename
          puts "file already exist, ignored: #{filename}"
          next
        end
        x = "#{local_path}/#{filename}"
        if ! File.symlink? x and (File.directory? x or File.file? x)
          jobs << [new_remote_path, x]
        end
      end
    elsif File.file? local_path and ! File.symlink? local_path
      puts "uploading #{local_path}"
      begin
        upload_file(local_path, remote_path)
      rescue ResponseError => e
        if e.response.code == "409"
          puts "file already exist, ignored"
        else
          raise e
        end
      end
    end
  end
end

#user_infoObject



495
496
497
498
499
500
501
502
503
504
505
506
507
508
# File 'lib/everbox_client/runner.rb', line 495

def 
  response = access_token.get '/api/1/user_info'
  if response.code == '200'
    res = JSON.parse(response.body)
    if res["code"] == 0
      user = User.new(res["user"])
      puts user
      return
    end
  end
  puts "fetch user info failed"
  puts "  code: #{response.code}"
  puts "  body: #{response.body}"
end