Advertisement
riocampos

gogakuondemand.rb (v3.4)

Aug 9th, 2013
169
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 6.79 KB | None | 0 0
  1. #!/usr/bin/env ruby
  2. # coding: utf-8
  3.  
  4. # 全講座DLで約20分、約557MB(mp4からmp3への変換はPC能力に依存)
  5.  
  6. require 'nokogiri'
  7. require 'net/https'
  8. require 'fileutils'
  9. require 'kconv'
  10.  
  11. def check_ffmpeg_has_(re)
  12.   IO.popen('ffmpeg -version 2>&1') do |pipe|
  13.     status = pipe.inject('') { |total, line| total << line }
  14.     true if status =~ re
  15.   end
  16. end
  17.  
  18. def is_win?
  19.   RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|cygwin|bccwin/ ? true : false
  20. end
  21.  
  22. def load_pref
  23.   set_script_dir
  24.   load_pref_file
  25.   load_subjects_file
  26.   set_save_file_dest_dir
  27. end
  28.  
  29. def set_script_dir
  30.   @script_dir = File.dirname(File.expand_path(__FILE__))
  31.   push_dir(@script_dir)
  32. end
  33.  
  34. def load_pref_file
  35.   load 'pref.rb'
  36.   @select_subjects_jp = []
  37.   pref.each do |k,v|
  38.     @select_subjects_jp << k if v
  39.   end
  40.   @audio_format = audio_format.to_sym
  41. end
  42.  
  43. def load_subjects_file
  44.   load 'subjects.rb'
  45.   set_audio_format(audio_formats)
  46.   @select_subjects = []
  47.   @subject_urls = subject_urls
  48.   @subjects_jp  = subjects_jp
  49.   @select_subjects_jp.each do |sub_jp|
  50.     @select_subjects << @subjects_jp[sub_jp]
  51.   end
  52. end
  53.  
  54. def set_audio_format(formats)
  55.   @ext                  = formats[@audio_format][:ext]
  56.   @audio_output_option  = formats[@audio_format][:audio_output_option]
  57.   if ARGV[0]
  58.     formats.each do |k, v|
  59.       if ARGV[0].downcase =~ /#{k}/
  60.         @ext                  = v[:ext]
  61.         @audio_output_option  = v[:audio_output_option]
  62.         break
  63.       end
  64.     end
  65.   end
  66. end
  67.  
  68. def set_save_file_dest_dir
  69.   if save_file_dest_dir
  70.     working_dir = File.expand_path(File.path(save_file_dest_dir))
  71.   else
  72.     working_dir = File.expand_path(File.dirname('.'))
  73.   end
  74.   Dir.mkdir(working_dir) unless File.directory?(working_dir)
  75.   push_dir(working_dir)
  76. end
  77.  
  78. def print_download_subjects
  79.   download_subjects = @select_subjects_jp * "、"
  80.   print "ダウンロードする語学講座:#{download_subjects}\n"
  81.   print "ファイルフォーマット:#{@ext.sub(/\./, "")}\n"
  82. end
  83.  
  84. def https_body(url)
  85.   uri = URI.parse(url)
  86.   https = Net::HTTP.new(uri.host, uri.port)
  87.   https.use_ssl = true
  88.   https.ssl_version = :TLSv1
  89.   https.verify_mode = OpenSSL::SSL::VERIFY_NONE
  90.   if uri.query
  91.     https.get(uri.path + '?' + uri.query).body
  92.   else
  93.     https.get(uri.path).body
  94.   end
  95. end
  96.  
  97. def get_source_urls
  98.   print "ダウンロード先確認中.."
  99.   data_hash ={}
  100.   @select_subjects.each do |sub|
  101.     url = "https://cgi2.nhk.or.jp/gogaku/#{@subject_urls[sub]}/listdataflv.xml"
  102.     doc = Nokogiri.XML(https_body(url))
  103.     data_array = doc.xpath("/musicdata/music")
  104.     date = data_array.map { |d| d["hdate"].scan(/(\d+)(\d+)/)} \
  105.           .map{ |dt| "#{record_year(dt[0][0].to_i)}_%02d_%02d" % dt.flatten}
  106.     data_hash[sub] = {kouza: data_array[0].values[2]}
  107.     data_array.each_with_index do |d, i|
  108.       data_hash[sub][{date: date[i]}] = data_array[i].values[4]
  109.     end
  110.     print ".."
  111.   end
  112.   print "完了\n"
  113.   data_hash
  114. end
  115.  
  116. def record_year(record_month)
  117.   now = Time.now
  118.   this_month = now.month
  119.   this_year = now.year
  120.   @record_year = this_month - record_month < 0 ? this_year - 1 : this_year
  121. end
  122.  
  123. def prepare_download_each_subject(key, value)
  124.   @subject = value[:kouza]
  125.   print "\n講座名:#{@subject}\n"
  126.   subject_dir = File.join(current_dir, key.to_s + "/")
  127.   Dir.mkdir(subject_dir) unless File.directory?(subject_dir)
  128.   subject_dir
  129. end
  130.  
  131. def each_date_process(key, value)
  132.   value.each do |k, v|
  133.     next if k == :kouza
  134.     @metadata = {}
  135.     date = k[:date]
  136.     @metadata[:subject]   = @subject
  137.     @metadata[:title]     = "#{key}_#{date}"
  138.     @metadata[:title_jp]  = "#{@subject}_#{date}"
  139.     @metadata[:genre]     = "Speech"
  140.     @metadata[:create]    = "NHK"
  141.     @metadata[:year]      = @record_year.to_s
  142.     puts "日付:#{date}"
  143.     download_ondemand_file(v)
  144.   end
  145. end
  146.  
  147. def download_ondemand_file(subject_code)
  148.   master_m3u8 = "https://nhk-vh.akamaihd.net/i/gogaku-stream/mp4/#{subject_code}/master.m3u8"
  149.   title_path_jp = File.join(current_dir, @metadata[:title_jp])
  150.   if File.exists?("#{title_path_jp}#{@ext}") || File.exists?("#{title_path_jp}#{@ext}".tosjis)
  151.     puts "ダウンロード済み"
  152.     return
  153.   end
  154.   command_ffmpeg = %Q[ffmpeg -y -i #{master_m3u8} #{@audio_output_option} -metadata album="#{@metadata[:subject]}" -metadata title="#{@metadata[:title_jp]}" -metadata genre="#{@metadata[:genre]}" -metadata artist="#{@metadata[:create]}" -metadata date="#{@metadata[:year]}" -id3v2_version 3 "#{title_path_jp}#{@ext}" 2>&1]
  155.   command_ffmpeg = command_ffmpeg.tosjis if is_win?
  156.   print "ダウンロード中...\n"
  157.   $stdout.sync = true
  158.   IO.popen(command_ffmpeg) do |pipe|
  159.     duration = nil
  160.     progress = 0
  161.     pipe.each("r") do |each_line|
  162.       line =  is_win? ?
  163.               each_line.tosjis :
  164.               each_line
  165.       counter_str = '(\d{2}):(\d{2}):(\d{2}).(\d{2})'
  166.       counter_duration_match = line.scan(/: #{counter_str},/)
  167.       unless counter_duration_match == []
  168.         duration = time_counter_to_sec(counter_duration_match)
  169.       end
  170.       counter_progress_match = line.scan(/time=#{counter_str}/)
  171.       unless counter_progress_match == []
  172.         progress = time_counter_to_sec(counter_progress_match)
  173.         progress = duration if duration && progress > duration
  174.       end
  175.       if duration
  176.         progress_bar(progress, duration, "秒")
  177.       end
  178.     end
  179.     print "\n"
  180.   end
  181. end
  182.  
  183. def time_counter_to_sec(counter)
  184.   ((counter[0][0].to_i * 360000 + counter[0][1].to_i * 6000 + counter[0][2].to_i * 100 + counter[0][3].to_i) / 100.0).round
  185. end
  186.  
  187. def progress_bar(progress, max, unit)
  188.   max_digits = max.to_s.size
  189.   print "\r" + "[#{'%-50s' % ('*' * (progress.to_f / max * 50).to_i)}] #{'%*d' % [max_digits, progress]} / #{max} #{unit}"
  190. end
  191.  
  192. def current_dir
  193.   @pwd
  194. end
  195.  
  196. def push_dir(dir)
  197.   @pwds ||= []
  198.   @pwds.push(dir)
  199.   @pwd = @pwds.last
  200.   Dir::chdir(@pwd)
  201.   @pwd
  202. end
  203.  
  204. def pop_dir
  205.   @pwds.pop
  206.   @pwd = @pwds.last
  207.   Dir::chdir(@pwd)
  208.   @pwd
  209. end
  210.  
  211. def gogaku_on_demand
  212.   unless check_ffmpeg_has_(/openssl|gnutls/)
  213.     print "gogakuondemand.rbを実行するには\nOpenSSLまたはGnuTLSに対応しているFFmpegが必要です。\nインストール済みのFFmpegを確認してください。\n"
  214.     return
  215.   end
  216.   load_pref
  217.   if @ext == '.ogg'
  218.     unless check_ffmpeg_has_(/libvorbis/)
  219.       print "Ogg Vorbisフォーマットで保存するには\nlibvorbisライブラリに対応しているFFmpegが必要です。\nインストール済みのFFmpegを確認してください。\n"
  220.       return
  221.     end
  222.   end
  223.   print_download_subjects
  224.   data_hash = get_source_urls
  225.   data_hash.each do |key, value|
  226.     subject_dir = prepare_download_each_subject(key, value)
  227.     push_dir(subject_dir)
  228.     each_date_process(key, value)
  229.     pop_dir
  230.   end
  231.   puts "作業終了"
  232. end
  233.  
  234. gogaku_on_demand
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement