doubanfm 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/doubanfm +13 -0
- data/doubanfm.gemspec +5 -3
- data/lib/doubanfm/constant.rb +50 -0
- data/lib/doubanfm/doubanfm.rb +111 -0
- data/lib/doubanfm/player.rb +84 -0
- data/lib/doubanfm/version.rb +2 -2
- data/lib/doubanfm.rb +3 -3
- metadata +24 -5
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3861716d30ebf71c7ca9da1eff05c42655ee5165
         | 
| 4 | 
            +
              data.tar.gz: 1cf80994a730ed474c4cf8556c764980f14a297a
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 95cbf1b10fe56afae46919d2bde967e83a775051a4faaa2504daefe055fd1e3750e0798c86c20a29400afb0911d84af76f25ba39a2eac803f06a28232e4f59f8
         | 
| 7 | 
            +
              data.tar.gz: 5017daacf5e6f2521707c98bf207c027e1973b2dee3c2dc777056969173c76d9c64fcb8540fc1cf6b8274d270b8fc2caa3969bfbffc1b9102b0e4720d07a127e
         | 
    
        data/bin/doubanfm
    ADDED
    
    | @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'pathname'
         | 
| 4 | 
            +
            ROOT_DIR = Pathname.new(__FILE__).dirname.parent
         | 
| 5 | 
            +
            $:.push(ROOT_DIR.join('lib'))
         | 
| 6 | 
            +
            load ROOT_DIR.join('lib/doubanfm.rb')
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            player = DoubanFM::Player.new
         | 
| 9 | 
            +
            begin
         | 
| 10 | 
            +
              player.start
         | 
| 11 | 
            +
            rescue
         | 
| 12 | 
            +
              puts 'Bye~'
         | 
| 13 | 
            +
            end
         | 
    
        data/doubanfm.gemspec
    CHANGED
    
    | @@ -5,11 +5,11 @@ require 'doubanfm/version' | |
| 5 5 |  | 
| 6 6 | 
             
            Gem::Specification.new do |spec|
         | 
| 7 7 | 
             
              spec.name          = "doubanfm"
         | 
| 8 | 
            -
              spec.version       =  | 
| 8 | 
            +
              spec.version       = DoubanFM::VERSION
         | 
| 9 9 | 
             
              spec.authors       = ["Edgar Wang"]
         | 
| 10 10 | 
             
              spec.email         = ["edgar.xiaoxiangyu@gmail.com"]
         | 
| 11 | 
            -
              spec.description   = %q{ | 
| 12 | 
            -
              spec.summary       = %q{ | 
| 11 | 
            +
              spec.description   = %q{Just another DoubanFM client.}
         | 
| 12 | 
            +
              spec.summary       = %q{A cmdline DoubanFM client. Need mpg123 to play songs.}
         | 
| 13 13 | 
             
              spec.homepage      = ""
         | 
| 14 14 | 
             
              spec.license       = "MIT"
         | 
| 15 15 |  | 
| @@ -18,6 +18,8 @@ Gem::Specification.new do |spec| | |
| 18 18 | 
             
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         | 
| 19 19 | 
             
              spec.require_paths = ["lib"]
         | 
| 20 20 |  | 
| 21 | 
            +
              spec.add_dependency "highline"
         | 
| 22 | 
            +
             | 
| 21 23 | 
             
              spec.add_development_dependency "bundler", "~> 1.3"
         | 
| 22 24 | 
             
              spec.add_development_dependency "rake"
         | 
| 23 25 | 
             
            end
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            require 'uri'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module DoubanFM
         | 
| 4 | 
            +
              module Constants
         | 
| 5 | 
            +
                # 豆瓣FM的API服务器通过`app_name`和`version`来确定
         | 
| 6 | 
            +
                # 客户端的合法性
         | 
| 7 | 
            +
                APP_NAME = 'radio_desktop_win'
         | 
| 8 | 
            +
                APP_VERSION = 100
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                # 此URI用于用户登录
         | 
| 11 | 
            +
                # 返回的用户信息是JSON格式的
         | 
| 12 | 
            +
                LOGIN_URI = URI('http://www.douban.com/j/app/login')
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # 此URI用于获取所有可用的兆赫(channel)
         | 
| 15 | 
            +
                # 返回值为JSON格式
         | 
| 16 | 
            +
                CHANNELS_URI = URI('http://www.douban.com/j/app/radio/channels')
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                # 我的私人兆赫所对应的ID
         | 
| 19 | 
            +
                PERSONAL_CHANNEL_ID = 0
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                # 我的红心兆赫所对应的ID
         | 
| 22 | 
            +
                RED_HEART_CHANNEL_ID = -3
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                # 当前兆赫(channel)未设定时的默认值
         | 
| 25 | 
            +
                INVALID_CHANNEL_ID = -1
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              module ActionType
         | 
| 29 | 
            +
                # 不再播放
         | 
| 30 | 
            +
                BYE = 'b'
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # 标记当前歌曲为喜欢
         | 
| 33 | 
            +
                RATE = 'r'
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                # 取消当前歌曲的喜欢标记
         | 
| 36 | 
            +
                UNRATE = 'u'
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                # 当前歌曲播放完毕,但是歌曲队列中还有歌曲
         | 
| 39 | 
            +
                FINISH = 'e'
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                # 没有歌曲播放,歌曲队列也没有任何歌曲,需要返回新播放列表
         | 
| 42 | 
            +
                NEW_PLAYLIST = 'n'
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                # 歌曲正在播放,队列中还有歌曲,需要返回新的播放列表
         | 
| 45 | 
            +
                NEXT_PLAYLIST = 'p'
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                # 跳过当前歌曲
         | 
| 48 | 
            +
                SKIP = 's'
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
| @@ -0,0 +1,111 @@ | |
| 1 | 
            +
            require 'net/http'
         | 
| 2 | 
            +
            require 'json'
         | 
| 3 | 
            +
            require 'uri'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module DoubanFM
         | 
| 6 | 
            +
              class Client
         | 
| 7 | 
            +
                include Constants
         | 
| 8 | 
            +
                include ActionType
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                attr_reader :channels, :current_channel, :current_song
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def initialize(email='', password='')
         | 
| 13 | 
            +
                  @client = Net::HTTP
         | 
| 14 | 
            +
                  @current_channel = INVALID_CHANNEL_ID
         | 
| 15 | 
            +
                  @current_song = nil
         | 
| 16 | 
            +
                  @user_info = {user_id: '', expire: '', token: ''}
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  if !email.empty? && !password.empty?
         | 
| 19 | 
            +
                    login(email, password)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  fetch_channels
         | 
| 23 | 
            +
                  new_playlist
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def login(email='', password='')
         | 
| 27 | 
            +
                  resp = @client.post_form(LOGIN_URI,
         | 
| 28 | 
            +
                                            app_name: APP_NAME,
         | 
| 29 | 
            +
                                            version: APP_VERSION,
         | 
| 30 | 
            +
                                            email: email,
         | 
| 31 | 
            +
                                            password: password)
         | 
| 32 | 
            +
                  @user_info = JSON.parse(
         | 
| 33 | 
            +
                    resp.body,
         | 
| 34 | 
            +
                    symbolize_names: true
         | 
| 35 | 
            +
                  )
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  if @user_info[:r] == 1
         | 
| 38 | 
            +
                    raise @user_info[:err]
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def user_logged?
         | 
| 43 | 
            +
                  if @user_info[:user_id].empty?
         | 
| 44 | 
            +
                    return false
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  return true
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def new_playlist
         | 
| 51 | 
            +
                  fetch_playlist(NEW_PLAYLIST)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def next_playlist
         | 
| 55 | 
            +
                  fetch_playlist(NEXT_PLAYLIST)
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def next_song
         | 
| 59 | 
            +
                  if @current_song == @playlist.last
         | 
| 60 | 
            +
                    next_playlist
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  next_song_pos = @playlist.index(@current_song) + 1
         | 
| 64 | 
            +
                  @current_song = @playlist[next_song_pos]
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                private
         | 
| 68 | 
            +
                  def fetch_channels
         | 
| 69 | 
            +
                    @channels = JSON.parse(
         | 
| 70 | 
            +
                      @client.get(CHANNELS_URI),
         | 
| 71 | 
            +
                      symbolize_names: true
         | 
| 72 | 
            +
                    )[:channels]
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def fetch_playlist(type)
         | 
| 76 | 
            +
                    if @current_channel == INVALID_CHANNEL_ID
         | 
| 77 | 
            +
                      channel_id = select_random_channel
         | 
| 78 | 
            +
                    else
         | 
| 79 | 
            +
                      channel_id = @current_channel
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    sid = @current_song ? @current_song[:sid] : ''
         | 
| 83 | 
            +
                    params = {
         | 
| 84 | 
            +
                      app_name: APP_NAME,
         | 
| 85 | 
            +
                      version: APP_VERSION,
         | 
| 86 | 
            +
                      user_id: @user_info[:user_id],
         | 
| 87 | 
            +
                      expire: @user_info[:expire],
         | 
| 88 | 
            +
                      token: @user_info[:token],
         | 
| 89 | 
            +
                      channel: channel_id,
         | 
| 90 | 
            +
                      sid: sid,
         | 
| 91 | 
            +
                      type: type
         | 
| 92 | 
            +
                    }
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    fetch_playlist_uri = URI('http://www.douban.com/j/app/radio/people')
         | 
| 95 | 
            +
                    fetch_playlist_uri.query = URI.encode_www_form(params)
         | 
| 96 | 
            +
                    @playlist = JSON.parse(
         | 
| 97 | 
            +
                      @client.get(fetch_playlist_uri),
         | 
| 98 | 
            +
                      symbolize_names: true
         | 
| 99 | 
            +
                    )[:song]
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    @current_song = @playlist.first
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  def select_random_channel
         | 
| 105 | 
            +
                    first_channel = user_logged? ? 1 : 0
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                    which = Random.new.rand(first_channel ... @channels.size)
         | 
| 108 | 
            +
                    @channels[which][:channel_id]
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
              end
         | 
| 111 | 
            +
            end
         | 
| @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            require 'highline/import'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module DoubanFM
         | 
| 4 | 
            +
              class Player
         | 
| 5 | 
            +
                def initialize
         | 
| 6 | 
            +
                  @client = Client.new
         | 
| 7 | 
            +
                  @semaphore = Mutex.new
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def start
         | 
| 11 | 
            +
                  loop do
         | 
| 12 | 
            +
                    cmd = ask("DoubanFM> ", %w{login play skip quit}) do |q|
         | 
| 13 | 
            +
                      q.readline = true
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    case cmd
         | 
| 17 | 
            +
                    when 'login'
         | 
| 18 | 
            +
                      login
         | 
| 19 | 
            +
                    when 'play'
         | 
| 20 | 
            +
                      play
         | 
| 21 | 
            +
                    when 'skip'
         | 
| 22 | 
            +
                      skip
         | 
| 23 | 
            +
                    when 'quit'
         | 
| 24 | 
            +
                      kill_player_thread
         | 
| 25 | 
            +
                      break
         | 
| 26 | 
            +
                    else
         | 
| 27 | 
            +
                      puts 'Nothing to do'
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def play
         | 
| 33 | 
            +
                  Thread.new do
         | 
| 34 | 
            +
                    player_proc = proc do |waiting|
         | 
| 35 | 
            +
                      if waiting
         | 
| 36 | 
            +
                        @client.next_song
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                        play_current_song do |wait|
         | 
| 39 | 
            +
                          player_proc.call(wait)
         | 
| 40 | 
            +
                        end
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                    player_proc.call(true)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def skip
         | 
| 48 | 
            +
                  @client.next_song
         | 
| 49 | 
            +
                  kill_player_thread
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def login
         | 
| 53 | 
            +
                  return if @client.user_logged?
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  email = ask("邮箱: ")
         | 
| 56 | 
            +
                  password = ask("密码: ") do |q|
         | 
| 57 | 
            +
                    q.echo = false
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  @client.login(email, password)
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                private
         | 
| 64 | 
            +
                  def play_current_song
         | 
| 65 | 
            +
                      @semaphore.lock
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      @waiting = false
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                      @player_pid = spawn("mpg123 #{@client.current_song[:url]} > /dev/null 2>&1")
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                      Process.wait
         | 
| 72 | 
            +
                      @waiting = true
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                      @semaphore.unlock
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                      yield @waiting if block_given?
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def kill_player_thread
         | 
| 80 | 
            +
                    Process.kill('KILL',  @player_pid)
         | 
| 81 | 
            +
                    Process.kill('KILL',  @player_pid + 1)
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
    
        data/lib/doubanfm/version.rb
    CHANGED
    
    | @@ -1,3 +1,3 @@ | |
| 1 | 
            -
            module  | 
| 2 | 
            -
              VERSION = "0.0. | 
| 1 | 
            +
            module DoubanFM
         | 
| 2 | 
            +
              VERSION = "0.0.3"
         | 
| 3 3 | 
             
            end
         | 
    
        data/lib/doubanfm.rb
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 1 | 
             
            require "doubanfm/version"
         | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 2 | 
            +
            require "doubanfm/constant"
         | 
| 3 | 
            +
            require "doubanfm/doubanfm"
         | 
| 4 | 
            +
            require "doubanfm/player"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,29 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: doubanfm
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Edgar Wang
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2013-08- | 
| 11 | 
            +
            date: 2013-08-13 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: highline
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - '>='
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - '>='
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 13 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 28 | 
             
              name: bundler
         | 
| 15 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -38,10 +52,11 @@ dependencies: | |
| 38 52 | 
             
                - - '>='
         | 
| 39 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 54 | 
             
                    version: '0'
         | 
| 41 | 
            -
            description:  | 
| 55 | 
            +
            description: Just another DoubanFM client.
         | 
| 42 56 | 
             
            email:
         | 
| 43 57 | 
             
            - edgar.xiaoxiangyu@gmail.com
         | 
| 44 | 
            -
            executables: | 
| 58 | 
            +
            executables:
         | 
| 59 | 
            +
            - doubanfm
         | 
| 45 60 | 
             
            extensions: []
         | 
| 46 61 | 
             
            extra_rdoc_files: []
         | 
| 47 62 | 
             
            files:
         | 
| @@ -50,8 +65,12 @@ files: | |
| 50 65 | 
             
            - LICENSE.txt
         | 
| 51 66 | 
             
            - README.md
         | 
| 52 67 | 
             
            - Rakefile
         | 
| 68 | 
            +
            - bin/doubanfm
         | 
| 53 69 | 
             
            - doubanfm.gemspec
         | 
| 54 70 | 
             
            - lib/doubanfm.rb
         | 
| 71 | 
            +
            - lib/doubanfm/constant.rb
         | 
| 72 | 
            +
            - lib/doubanfm/doubanfm.rb
         | 
| 73 | 
            +
            - lib/doubanfm/player.rb
         | 
| 55 74 | 
             
            - lib/doubanfm/version.rb
         | 
| 56 75 | 
             
            homepage: ''
         | 
| 57 76 | 
             
            licenses:
         | 
| @@ -76,5 +95,5 @@ rubyforge_project: | |
| 76 95 | 
             
            rubygems_version: 2.0.6
         | 
| 77 96 | 
             
            signing_key: 
         | 
| 78 97 | 
             
            specification_version: 4
         | 
| 79 | 
            -
            summary:  | 
| 98 | 
            +
            summary: A cmdline DoubanFM client. Need mpg123 to play songs.
         | 
| 80 99 | 
             
            test_files: []
         |