Module: Sfdown::CLI

Defined in:
lib/sfdown.rb

Overview

Command-line interface: wires the stages together and drives the status bar.

Class Method Summary collapse

Class Method Details

.dump_tree(node, depth = 0) ⇒ Object

Debug helper: indented tree listing.



519
520
521
522
# File 'lib/sfdown.rb', line 519

def dump_tree(node, depth = 0)
  puts "#{'  ' * depth}#{node.name}#{node.dir? ? '/' : ''}"
  node.children.each { |c| dump_tree(c, depth + 1) }
end

.main(argv) ⇒ Object



574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
# File 'lib/sfdown.rb', line 574

def main(argv)
  config = Options.parse(argv)
  http = Http.new(timeout: config.timeout, sleep: config.sleep)
  parser = Parser.new(config.project)
  bar = StatusBar.new
  mapper = Mapper.new(config.project, http, parser, log: bar.method(:log))

  start = Time.now
  root = mapper.map do |node|
    bar.update("Parsing #{node.path}/", stage1_line(mapper, Time.now - start))
  end

  summary = format("Stage 1: %d folders, %d files, %s total in %s",
                   mapper.folders, mapper.files, Fmt.size(mapper.total_size),
                   Fmt.duration(Time.now - start))
  summary += " (#{mapper.failures} failures)" if mapper.failures.positive?
  bar.log(summary)

  unless config.no
    dest = File.join(config.output, config.project)
    downloader = Downloader.new(config.project, http, dest, log: bar.method(:log))
    run_stage2(downloader, root, bar, start)
  end

  bar.finish
end

.render_stage2(bar, dl, global_start, stage2_start) ⇒ Object



538
539
540
541
542
543
544
545
# File 'lib/sfdown.rb', line 538

def render_stage2(bar, dl, global_start, stage2_start)
  now = Time.now
  s2 = now - stage2_start
  speed = s2.positive? ? dl.bytes_done / s2 : 0
  remaining = [dl.total_bytes - dl.bytes_done, 0].max
  eta = speed.positive? ? remaining / speed : 0
  bar.update("Fetching #{dl.current}", stage2_line(dl, now - global_start, speed, (now - global_start) + eta))
end

.run_stage2(downloader, root, bar, global_start) ⇒ Object

Drive stage 2: prepare the tree, download files while a ~1s ticker refreshes the bar, apply folder timestamps, then log the stage summary.



549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
# File 'lib/sfdown.rb', line 549

def run_stage2(downloader, root, bar, global_start)
  downloader.prepare(root)
  stage2_start = Time.now
  ticker = Thread.new do
    loop do
      render_stage2(bar, downloader, global_start, stage2_start)
      sleep 1
    end
  end

  downloader.download_all
  ticker.kill
  ticker.join
  render_stage2(bar, downloader, global_start, stage2_start) # final 100% frame
  downloader.apply_folder_times(root)

  s2 = Time.now - stage2_start
  avg = s2.positive? ? downloader.bytes_done / s2 : 0
  summary = format("Stage 2: %d/%d files, %s in %s (avg %s/s)",
                   downloader.files_done, downloader.total_files,
                   Fmt.size(downloader.bytes_done), Fmt.duration(s2), Fmt.size(avg))
  summary += " (#{downloader.failures} failures)" if downloader.failures.positive?
  bar.log(summary)
end

.stage1_line(mapper, elapsed) ⇒ Object

Stage-1 analytics line: starts with the stage number, ends with elapsed time.



525
526
527
528
# File 'lib/sfdown.rb', line 525

def stage1_line(mapper, elapsed)
  format("[1] folders: %d | files: %d | size: %s | elapsed: %s",
         mapper.folders, mapper.files, Fmt.size(mapper.total_size), Fmt.duration(elapsed))
end

.stage2_line(dl, global_elapsed, speed, total_est) ⇒ Object

Stage-2 analytics line: elapsed is the global (both-stage) time, complemented by an estimated total = elapsed + remaining_bytes / speed.



532
533
534
535
536
# File 'lib/sfdown.rb', line 532

def stage2_line(dl, global_elapsed, speed, total_est)
  format("[2] files: %d/%d | %s/%s | speed: %s/s | elapsed: %s / ~%s",
         dl.files_done, dl.total_files, Fmt.size(dl.bytes_done), Fmt.size(dl.total_bytes),
         Fmt.size(speed), Fmt.duration(global_elapsed), Fmt.duration(total_est))
end