ソースコード公開
今年初めにリリースしましたRetroTubeですが、ソースコード公開すると宣言しつつ、お化粧直しする余裕が取れなくてこれまでズルズルときてしまいました。
気が付けばもう6月…これではいかんということで、一念発起してなんとか皆様のお目に触れても恥ずかしくない程度にまで整形することができました。
ですので、本日より数日間に渡って公開していきたいと思います。GPLで公開します。
- RetroTubeのシステムは、バッチ部分とオンライン部分の二つに大きく分かれているので、まずはバッチ部分から掲載します。
- リスト1〜4は、それぞれ音楽・アニメ・邦画・洋画の各カテゴリについて、YouTubeから一括検索して結果を出力するプログラムです。
- リスト5は、上記検索結果から新着情報を出力するものです。
あと、ここからは超重要なお知らせになってしまうのですが、日経ソフトウエア8月号(6/23発売)から、短期集中連載という形で「RetroTube開発記」を執筆させていただくことになっています。
(写真は8月号の予告で、「COBOLプログラマによるRubyプログラミング挑戦記」とありますが、これ実は、私のことです。)
単にソースコードの解説に留まらず、開発のきっかけとなった出来事から、開発中に考えていた事、公開後の世間からの反応など、様々な角度から楽しんでいただける内容になっていますので、是非大勢の方に読んでいただきたいと思っています。
よろしくお願いします。
それでは以下より、ソースコード垂れ流していきます。
【リスト1】search_music.rb
##################################### #【RetroTube】一括検索プログラム(邦楽) ##################################### require "youtube" require "kconv" require "fastercsv" #YouTubeAPIアクセス用ライブラリ初期化 youtube = YouTube.new '(DeveloperID)' #前回検索時の結果を退避(newdataフォルダからolddataフォルダへ) File.rename("newdata/music.csv","olddata/music.old.csv") #ここから検索処理 musicout = open("newdata/music.csv",'w') beforeartist = nil FasterCSV.foreach("source/source_music.csv") { |line| #アーティスト名がキーブレイクした時、タグ検索(最大300件)を行い、$getdataに格納する if line[3] != beforeartist $getdata = Array.new i = 1 loop { begin videos = youtube.videos_by_tag(Kconv.toutf8(line[3]),i,100) if videos == [] break end videos.each { |video| targettitle = Kconv.tosjis(video.title) convtitle = targettitle.gsub(',',' ') $getdata.push([convtitle,video.id,video.thumbnail_url,video.url]) } rescue break end i += 1 if i > 3 break end } end #アーティスト名でタグ検索した結果($getdataに格納されている)から、 #さらに曲タイトルを含むものを検索し、結果を出力 $getdata.each { |getline| compare = Kconv.toutf8(line[4]) pattern = Regexp.new(compare.upcase) compare2 = Kconv.toutf8(getline[0]) if pattern =~ compare2.upcase musicout.puts line[0] + ',' + line[1] + ',' + line[2] + ',' + getline[1] + ',' + getline[2] + ',' + getline[3] break end } beforeartist = line[3] } musicout.close
【リスト2】search_anime.rb
##################################### #【RetroTube】一括検索プログラム(アニメ) ##################################### require "kconv" require "net/http" require "fastercsv" #前回検索時の結果を退避(newdataフォルダからolddataフォルダへ) File.rename("newdata/anime.csv","olddata/anime.old.csv") #ここから検索処理 address = "www.youtube.com" path = "/api2_rest?method=youtube.videos.list_by_category_and_tag&dev_id=6BQYABg1UFM&category_id=1&page=1&per_page=20&tag=" newdataout = open("newdata/anime.csv",'w') FasterCSV.foreach("source/source_anime.csv") { |line| p line begin searchword_utf8 = line[4].kconv(Kconv::UTF8, Kconv::SJIS) searchpath = path + searchword_utf8 body = Net::HTTP.get( address , searchpath ) re = %r|<id>(.*?)</id>|u searchresult_id = body.scan(re) re = %r|<title>(.*?)</title>|u searchresult_title = body.scan(re) re = %r|<thumbnail_url>(.*?)</thumbnail_url>|u searchresult_thumbnail_url = body.scan(re) re = %r|<url>(.*?)</url>|u searchresult_url = body.scan(re) opflag = 0 edflag = 0 re_op = %r|op|u re_ed = %r|ed|u re_end = %r|end|u searchresult_id.zip(searchresult_title,searchresult_thumbnail_url,searchresult_url){|zip_id,zip_title,zip_thumbnail_url,zip_url| if opflag == 0 if zip_title[0].downcase =~ re_op newdataout.print line[0] + "," + line[1] + "," + line[2] + "," + zip_id[0] + "," + zip_thumbnail_url[0] + "," + zip_url[0] + ",OP" + "\n" opflag = 1 if zip_title[0].downcase =~ re_ed || zip_title[0].downcase =~ re_end edflag = 1 end end end if edflag == 0 if zip_title[0].downcase =~ re_ed || zip_title[0].downcase =~ re_end newdataout.print line[0] + "," + line[1] + "," + line[2] + "," + zip_id[0] + "," + zip_thumbnail_url[0] + "," + zip_url[0] + ",ED" + "\n" edflag = 1 end end if opflag == 1 && edflag == 1 break end } if opflag == 0 && edflag == 0 if searchresult_id != [] newdataout.print line[0] + "," + line[1] + "," + line[2] + "," + searchresult_id[0][0] + "," + searchresult_thumbnail_url[0][0] + "," + searchresult_url[0][0] + ",NA" + "\n" end end rescue p 'timeout err' end } newdataout.close
【リスト3】search_movie_j.rb
##################################### #【RetroTube】一括検索プログラム(邦画) ##################################### require "kconv" require "net/http" require "fastercsv" #前回検索時の結果を退避(newdataフォルダからolddataフォルダへ) File.rename("newdata/movie_j.csv","olddata/movie_j.old.csv") #ここから検索処理 address = "www.youtube.com" path = "/api2_rest?method=youtube.videos.list_by_category_and_tag&dev_id=6BQYABg1UFM&category_id=1&page=1&per_page=1&tag=" newdataout = open("newdata/movie_j.csv",'w') FasterCSV.foreach("source/source_movie.csv") { |line| begin re_japan = %r|日本|s if re_japan =~ line[6] p line[0] + " " + line[1] + " " + line[5] if line[5].split(//s).length > 10 searchword = line[5] else searchword = line[5] + " " + line[4] end searchword_utf8 = searchword.kconv(Kconv::UTF8, Kconv::SJIS) searchpath = path + searchword_utf8 convpath = searchpath.gsub(' ','%20') body = Net::HTTP.get( address , convpath ) re = %r|<id>(.*?)</id>|u result_id = body.scan(re) re = %r|<thumbnail_url>(.*?)</thumbnail_url>|u result_thumbnail_url = body.scan(re) re = %r|<url>(.*?)</url>|u result_url = body.scan(re) result_id.zip(result_thumbnail_url,result_url) { |zip_id,zip_thumbnail_url,zip_url| newdataout.print line[0] + ',"' + line[2].gsub('"','""') + '","' + line[3].gsub('"','""') + '","' + zip_id[0] + '","' + zip_thumbnail_url[0] + '","' + zip_url[0] + '"' + "\n" } end rescue p 'err' end } newdataout.close
【リスト4】search_movie_a.rb
##################################### #【RetroTube】一括検索プログラム(洋画) ##################################### require "kconv" require "net/http" require "fastercsv" #前回検索時の結果を退避(newdataフォルダからolddataフォルダへ) File.rename("newdata/movie_a.csv","olddata/movie_a.old.csv") #ここから検索処理 address = "www.youtube.com" path = "/api2_rest?method=youtube.videos.list_by_category_and_tag&dev_id=6BQYABg1UFM&category_id=1&page=1&per_page=1&tag=" newdataout = open("newdata/movie_a.csv",'w') FasterCSV.foreach("source/source_movie.csv") { |line| begin re_japan = %r|日本|s if re_japan =~ line[6] else p line[0] + " " + line[1] + " " + line[5] searchword = line[5] + " " + line[4] searchword_utf8 = searchword.kconv(Kconv::UTF8, Kconv::SJIS) searchpath = path + searchword_utf8 convpath = searchpath.gsub(' ','%20') body = Net::HTTP.get( address , convpath ) re = %r|<id>(.*?)</id>|u result_id = body.scan(re) re = %r|<thumbnail_url>(.*?)</thumbnail_url>|u result_thumbnail_url = body.scan(re) re = %r|<url>(.*?)</url>|u result_url = body.scan(re) result_id.zip(result_thumbnail_url,result_url) { |zip_id,zip_thumbnail_url,zip_url| newdataout.print line[0] + ',"' + line[2].gsub('"','""') + '","' + line[3].gsub('"','""') + '","' + zip_id[0] + '","' + zip_thumbnail_url[0] + '","' + zip_url[0] + '"' + "\n" } end rescue p 'err' end } newdataout.close
【リスト5】compare.rb
##################################### #【RetroTube】新着情報出力プログラム ##################################### require "kconv" require "fastercsv" #前回検索時の結果を退避(newdataフォルダからolddataフォルダへ) File.rename("newdata/compare.csv","olddata/compare.old.csv") File.rename("newdata/rss.rdf","olddata/rss.old.rdf") #前回検索結果と今回検索結果を比較し、差分を新着情報として出力(邦楽) compareout = open("newdata/compare.csv",'w') $olddata = Array.new FasterCSV.foreach("olddata/music.old.csv") { |line| $olddata.push(line) } FasterCSV.foreach("newdata/music.csv") { |line| $hitsw = 0 $olddata.each { |oldline| if oldline[1] == line[1] && oldline[2] == line[2] $hitsw = 1 break end } if $hitsw == 0 compareout.puts line[0] + ',' + line[1] + ',' + line[2] + ',' + line[3] + ',' + line[4] + ',' + line[5] + ',music' end } #前回検索結果と今回検索結果を比較し、差分を新着情報として出力(アニメ) $olddata = Array.new FasterCSV.foreach("olddata/anime.old.csv") { |line| $olddata.push(line) } FasterCSV.foreach("newdata/anime.csv") { |line| $hitsw = 0 $olddata.each { |oldline| if oldline[1] == line[1] && oldline[2] == line[2] && oldline[6] == line[6] $hitsw = 1 break end } if $hitsw == 0 if line[6] == "NA" compareout.puts line[0] + ',' + line[1] + ',' + line[2] + ',' + line[3] + ',' + line[4] + ',' + line[5] + ',anime' else compareout.puts line[0] + ',' + line[1] + ',' + line[2] + ' ' + line[6] + ',' + line[3] + ',' + line[4] + ',' + line[5] + ',anime' end end } #前回検索結果と今回検索結果を比較し、差分を新着情報として出力(邦画) $olddata = Array.new FasterCSV.foreach("olddata/movie_j.old.csv") { |line| $olddata.push(line) } FasterCSV.foreach("newdata/movie_j.csv") { |line| $hitsw = 0 $olddata.each { |oldline| if oldline[1] == line[1] && oldline[2] == line[2] $hitsw = 1 break end } if $hitsw == 0 compareout.puts line[0] + ',' + line[1] + ',' + line[2] + ',' + line[3] + ',' + line[4] + ',' + line[5] + ',movie_j' end } #前回検索結果と今回検索結果を比較し、差分を新着情報として出力(洋画) $olddata = Array.new FasterCSV.foreach("olddata/movie_a.old.csv") { |line| $olddata.push(line) } FasterCSV.foreach("newdata/movie_a.csv") { |line| $hitsw = 0 $olddata.each { |oldline| if oldline[1] == line[1] && oldline[2] == line[2] $hitsw = 1 break end } if $hitsw == 0 compareout.puts line[0] + ',' + line[1] + ',' + line[2] + ',' + line[3] + ',' + line[4] + ',' + line[5] + ',movie_a' end } compareout.close #新着情報(compare.csv)からRSS出力 rssout = open("newdata/rss.rdf",'w') rssout.puts '<?xml version="1.0" encoding="utf-8" ?> <rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="ja"> <channel rdf:about="http://www.retro-tube.com/rss.rdf"> <title>RetroTube</title> <link>http://www.retro-tube.com/</link> <description>RetroTube</description> <items> <rdf:Seq>' FasterCSV.foreach("newdata/compare.csv") { |rssline| rssout.puts ' <rdf:li rdf:resource="http://www.retro-tube.com/detail.cgi?movieid=' + Kconv.toutf8(rssline[3]) + '&category=' + rssline[6] + '"/>' } rssout.puts ' </rdf:Seq> </items> </channel>' FasterCSV.foreach("newdata/compare.csv") { |rssline| rssout.puts ' <item rdf:about="http://www.retro-tube.com/detail.cgi?movieid=' + Kconv.toutf8(rssline[3]) + '&category=' + rssline[6] + '">' case rssline[6] when "music" categoryname = Kconv.toutf8('邦楽') when "anime" categoryname = Kconv.toutf8('アニメ') when "movie_j" categoryname = Kconv.toutf8('邦画') when "movie_a" categoryname = Kconv.toutf8('洋画') end rssout.puts ' <title>[' + categoryname + '] ' + Kconv.toutf8(rssline[2].gsub('&','&')) + ' - ' + Kconv.toutf8(rssline[1].gsub('&','&')) + ' (' + rssline[0] +')</title>' rssout.puts ' <link>http://www.retro-tube.com/detail.cgi?movieid=' + Kconv.toutf8(rssline[3]) + '&category=' + rssline[6] + '</link>' rssout.puts ' </item>' } rssout.print '</rdf:RDF>' rssout.close