MP3Tunes Locker から MP3 をアルバム単位でダウンロードするアプリ
MP3Tunes Locker が何やらおもしろそうだったので
MP3 をひたすらアップロードしている。
このサービスにはちゃんとした API が存在している。
ファイルごとにストリーム/ダウンロード用の URL があったりして
結構おもしろいことができそうな予感がしてるけど
何にも思いつかなかったのでとりあえず
アルバム単位で MP3 をダウンロードできるアプリを書いてみた。
VisualRuby の習作だったりする。
アルバム単位のダウンロード自体は LockerSync 3.0 の
「Get Music by Artist, Album or Playlist」からできる。
もちろん、コードを書いたあとに気づいたorz
ちょっと長いけどソースを貼り付けておく。
mp3tunes.rb
#!ruby require 'net/https' require 'open-uri' require 'rexml/document' EMAIL = "YOUR_EMAIL_ADDRESS" PASSWORD = "YOUR_PASSWORD" PARTNER_TOKEN = "9999999999" # get from http://www.mp3tunes.com/partner/cb/tokens/ class MP3Tunes def initialize(email = EMAIL, password = PASSWORD, partner_token = PARTNER_TOKEN) @email, @password, @partner_token = email, password, partner_token self end def login src = REXML::Source.new(opens("https://shop.mp3tunes.com/api/v1/login"+ "?output=xml&username=#{@email}&password=#{@password}&partner_token=#{@partner_token}"))#, "UTF-8") @session_id = REXML::Document.new(src).get_elements('//session_id')[0].text puts @session_id self end def artists doc = REXML::Document.new(open("http://ws.mp3tunes.com/api/v1/lockerData" + "?output=xml&type=artist&sid=#{@session_id}")) doc.get_elements("//item").map do |item| [item.get_elements("./artistName")[0].text, item.get_elements("./artistId")[0].text] end end def albums(artist_id) print "artist_id=" puts artist_id doc = REXML::Document.new(open("http://ws.mp3tunes.com/api/v1/lockerData" + "?output=xml&type=album&sid=#{@session_id}&artist_id=#{artist_id}")) doc.get_elements("//item").map do |item| [item.get_elements("./albumTitle")[0].text, item.get_elements("./albumId")[0].text] end end def tracks(album_id) print "album_id=" puts album_id doc = REXML::Document.new(open("http://ws.mp3tunes.com/api/v1/lockerData" + "?output=xml&type=track&sid=#{@session_id}&album_id=#{album_id}")) doc.get_elements("//item").map do |item| {:trackId => item.get_elements("./trackId")[0].text, :trackTitle => item.get_elements("./trackTitle")[0].text, :trackNumber => item.get_elements("./trackNumber")[0].text, :trackFileSize => item.get_elements("./trackFileSize")[0].text, :downloadURL => item.get_elements("./downloadURL")[0].text + @partner_token, :playURL => item.get_elements("./playURL")[0].text + @partner_token, :albumTitle => item.get_elements("./albumTitle")[0].text, :artistName => item.get_elements("./artistName")[0].text, } end end private def opens(url) ar = URI.split(url) http = Net::HTTP.new(ar[2], '443') http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE response = nil http.start do |w| response = w.get(ar[5] + "?" + ar[7]) end response.body end end if __FILE__ == $0 puts MP3Tunes.new.login.artists.size end
treelist.rb
#!ruby require 'vr/vrcontrol' require 'vr/vrcomctl' require 'vr/vrtwopane' require 'vr/rscutil' require 'vr/vrdialog' require 'kconv' require 'mp3tunes' # http://vruby.sourceforge.net/cgi-bin/wiki.cgi?ProgressDialogSampel class Modaldlg1 < VRModalDialog include VRContainersSet include VRContainersSet def construct self.caption = '進捗状況' self.move(395,192,247,139) addControl(VRStatic,'static1',"static1",24,0,96,24) addControl(VRProgressbar,'prgrssBar1',"prgrssBar1",24,32,192,16) addControl(VRButton,'button1',"キャンセル",88,72,80,24) end include VRUserMessageUseable @@arg = nil def self.event(arg) @@arg = arg end def self_created registerUserMessage(0x11,'progress_state') Thread.new do loop do sleep 0.2 userMessage('progress_state') end end end def self_progress_state(wparem,lparam) @static1.caption = @@arg.to_s @prgrssBar1.position = @@arg.to_i close 0 if @@arg.to_i > 99 end def button1_clicked close 1 end end class TrackListview < VRListview def vrinit super addColumn("No",20) addColumn("Name",150) addColumn("Size",80) end def set_tracks(tracks) clearItems tracks.each do |t| addItem([t[:trackNumber], t[:trackTitle].tosjis, t[:trackFileSize]]) end end end class ArtistTreeview < VRTreeview def set_artists(artists) artists.each do |a| p = addItem(root, a[0].tosjis, a[1].intern) addItem(p, "!", 0) # dummy node end end def add_albums(hitem, albums) c = getChildOf(hitem) if getItemLParamOf(c) == 0 deleteItem(c) albums.each do |a| addItem(hitem, a[0].tosjis, a[1].intern) end end end end module MainForm include VRMenuUseable include VRHorizTwoPane def vrinit super @mp3tunes = MP3Tunes.new.login end def construct self.move(10,10,620,420) self.caption = "MP3Tunes Explorer" addPanedControl(ArtistTreeview,"tv1","tv") addPanedControl(TrackListview,"lv1","lv") setMenu newMenu.set( [["&File",[ ["&Download","downloadc"], ["&Exit","exitc"] ]] ] ) @artists = @mp3tunes.artists @tv1.set_artists(@artists) end ## notify ## def tv1_selchanged(hitem,lparam) album_id = @tv1.getItemLParamOf(hitem).to_sym.to_s @lv1.set_tracks(@mp3tunes.tracks(album_id)) if @tv1.getParentOf(hitem) end def tv1_itemexpanded(hitem,state,lparam) album_id = @tv1.getItemLParamOf(hitem).to_sym.to_s @tv1.add_albums(hitem, @mp3tunes.albums(album_id)) end ## command ## def downloadc_clicked if @tv1.selectedItem && @tv1.getParentOf(@tv1.selectedItem) unless @dir @dir = selectDirectory @dir = @dir.split("\\").join("/") if @dir end return unless @dir album_id = @tv1.getItemLParamOf(@tv1.selectedItem).to_sym.to_s tracks = @mp3tunes.tracks(album_id) @canceled = 0 Thread.new do i = 0 tracks.each do |t| i += 1 path = "#{@dir}/#{t[:artistName].tosjis}/#{t[:albumTitle].tosjis}"+ "/#{t[:trackNumber]}.#{t[:trackTitle].tosjis}.mp3" puts t[:downloadURL] puts path mkdirp(path) ar = URI.split(t[:downloadURL]) dest = open(path, "wb") read_size = 0 Net::HTTP.new(ar[2]).start do |w| w.get(ar[5] + "?" + ar[7]) do |buf| read_size += buf.size dest.write buf Modaldlg1.event(100 * i / tracks.size * read_size / t[:trackFileSize].to_i) Thread.stop if @canceled == 1 end end end end @canceled = VRLocalScreen.modalform(self, nil, Modaldlg1) else messageBox "アルバムを選択してください" end end def exitc_clicked exit 0 end private # http://araistudy.g.hatena.ne.jp/czk-htn/20061024/p2 def mkdirp(path) dir = File::dirname(path) dir.scan %r{[^/]+/?} do |d| test(?d,d) or Dir.mkdir d Dir.chdir d end end end if __FILE__ == $0 frm = VRLocalScreen.newform frm.extend MainForm frm.create.show VRLocalScreen.messageloop end
所感
- VisualRuby スゲー
- PyQt で書かれた LockerSync 3.0 はもっとスゲー
- MP3Tunes Player がリニューアルして使いにくくなった
- 登録したときは 25GB まで無料だったのが途中から 50GB まで無料になった
- すでにあるものを気づかずに作るのが多いをなんとかしたい…