douban.fm 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/douban.fm CHANGED
@@ -1,53 +1,131 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'douban.fm'
4
+ require 'optparse'
5
+ require 'ostruct'
4
6
 
5
- unless ARGV.length == 2
6
- p "douban_fm.rb <email> <password>"
7
- exit
8
- end
7
+ def main
8
+ options = OpenStruct.new
9
+ options.verbose = false
10
+
11
+ opts = OptionParser.new do |opts|
12
+ opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [OPTIONS]"
13
+
14
+ opts.on('-u', '--user email',
15
+ 'douban.fm account name, normally an email address',
16
+ 'if not provided, will play anonymous playlist') do |email|
17
+ options.email = email
18
+ end
19
+
20
+ opts.on('-p', '--password [password]',
21
+ 'douban.fm account password',
22
+ 'if not provided, will be asked') do |password|
23
+ options.password = password
24
+ end
25
+
26
+ opts.on('-m', '--mpd', 'do not play by it own, send playlist to Music Player Daemon') do
27
+ options.mpd = true
28
+ end
29
+
30
+ opts.on('-c', '--channel channel',
31
+ 'which channel to play',
32
+ 'if not provided, channel 0 will be selected but who knows what it is') do |channel|
33
+ options.channel = channel
34
+ end
35
+
36
+ opts.on('-l', '--list', 'list all available channels') do
37
+ options.list = true
38
+ end
39
+
40
+ opts.on('-v', '--verbose', 'verbose mode') do
41
+ options.verbose = true
42
+ end
43
+
44
+ opts.on_tail('-h', '--help', 'show this message') do
45
+ puts opts
46
+ exit
47
+ end
48
+ end
49
+
50
+ opts.parse!
51
+
52
+ if options.verbose
53
+ logger = DoubanFM::ConsoleLogger.new
54
+ else
55
+ logger = DoubanFM::DummyLogger.new
56
+ end
57
+
58
+ if options.list
59
+ douban_fm = DoubanFM::DoubanFM.new(logger)
60
+ douban_fm.fetch_channels
61
+ douban_fm.channels['channels'].sort_by { |i| i['channel_id'] }.each do |channel|
62
+ channel_id = channel['channel_id']
63
+ puts "#{channel_id}.#{' ' * (4 - channel_id.to_s.length)}#{channel['name']}"
64
+ end
65
+
66
+ exit
67
+ end
68
+
69
+ if options.channel.nil?
70
+ options.channel = 0
71
+ end
72
+
73
+ if options.email.nil?
74
+ douban_fm = DoubanFM::DoubanFM.new(logger)
75
+
76
+ logger.log('play anonymous playlist')
77
+ else
78
+ if options.password.nil? or options.password.empty?
79
+ require 'highline/import'
80
+ options.password = ask("Enter password: ") { |q| q.echo = false }
81
+ end
82
+
83
+ douban_fm = DoubanFM::DoubanFM.new(logger, options.email, options.password)
84
+ douban_fm.login
85
+
86
+ logger.log("login as user [#{options.email}]")
87
+ end
88
+
89
+ douban_fm.select_channel(options.channel)
90
+
91
+ logger.log("select channel #{options.channel}")
92
+
93
+ if options.mpd.nil?
94
+ play_proc = proc do |waiting|
95
+ if waiting
96
+ begin
97
+ logger.log('fetch next playlist')
98
+
99
+ douban_fm.fetch_next_playlist
100
+ rescue
101
+ logger.log('session expired, relogin')
102
+
103
+ douban_fm.login
104
+
105
+ logger.log('fetch next playlist')
106
+
107
+ douban_fm.fetch_next_playlist
108
+ end
109
+
110
+ logger.log('play current playlist')
111
+
112
+ douban_fm.play do |waiting|
113
+ play_proc.call(waiting)
114
+ end
115
+ end
116
+ end
9
117
 
10
- email = ARGV[0]
11
- password = ARGV[1]
12
- douban_fm = DoubanFM::DoubanFM.new(email, password)
13
- douban_fm.login
14
- douban_fm.get_channels
15
- douban_fm.select_channel(0)
16
-
17
- # stop = false
18
- # go_on = true
19
- # while not stop
20
- # if go_on
21
- # p "================="
22
- # go_on = false
23
- # douban_fm.get_next_playlist
24
- # douban_fm.play_current_playlist do |waiting|
25
- # unless waiting
26
- # stop = true
27
- # break
28
- # else
29
- # go_on = true
30
- # end
31
- # end
32
- # else
33
- # sleep 10
34
- # end
35
- # end
36
-
37
- play_proc = proc do |waiting|
38
- if waiting
39
- begin
40
- douban_fm.get_next_playlist
41
- rescue
42
- douban_fm.login
43
- end
44
-
45
- douban_fm.play_current_playlist do |waiting|
46
- play_proc.call(waiting)
118
+ play_proc.call(true)
119
+ else
120
+ # there are a lot more chances to get stack overflow in this mode,
121
+ # so can't use the proc way
122
+ while true
123
+ douban_fm.add_to_mpd
124
+ sleep 10
47
125
  end
48
126
  end
49
127
  end
50
128
 
51
- play_proc.call(true)
129
+ main
52
130
 
53
131
  sleep
data/douban.fm.gemspec CHANGED
@@ -15,4 +15,7 @@ Gem::Specification.new do |gem|
15
15
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
16
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
17
  gem.require_paths = ["lib"]
18
+
19
+ gem.add_dependency 'ruby-mpd'
20
+ gem.add_dependency 'highline'
18
21
  end
data/lib/douban.fm.rb CHANGED
@@ -1,2 +1,3 @@
1
1
  require 'douban.fm/version'
2
2
  require 'douban.fm/douban_fm'
3
+ require 'douban.fm/logger'
@@ -1,15 +1,21 @@
1
1
  module DoubanFM
2
2
  require 'net/http'
3
3
  require 'json'
4
+ require 'ruby-mpd'
4
5
 
5
6
  class DoubanFM
6
- attr_reader :waiting
7
+ DOUBAN_FM_MPD_PLAYLIST = 'douban.fm'
8
+ MIN_SONGS_IN_DOUBAN_FM_MPD_PLAYLIST = 10
7
9
 
8
- def initialize(email, password)
10
+ attr_reader :waiting, :channels, :current_playlist
11
+
12
+ def initialize(logger = DummyLogger.new, email = '', password = '')
13
+ @logger = logger
9
14
  @email = email
10
15
  @password = password
11
16
  @semaphore = Mutex.new
12
- @waiting = false # read this to determin whether to get one more playlist
17
+ @waiting = false # read this to determin whether to fetch one more playlist
18
+ @user_info = {'user_id' => '', 'expire' => '', 'token' => ''}
13
19
  end
14
20
 
15
21
  def login
@@ -25,17 +31,19 @@ module DoubanFM
25
31
  end
26
32
  end
27
33
 
28
- def get_channels
34
+ def fetch_channels
29
35
  uri = URI('http://www.douban.com/j/app/radio/channels')
30
- res = Net::HTTP.get(URI('http://www.douban.com/j/app/radio/channels'))
36
+ res = Net::HTTP.get(uri)
31
37
  @channels = JSON.parse(res)
38
+
39
+ @logger.log("raw channel list #{channels}")
32
40
  end
33
41
 
34
42
  def select_channel(channel_num)
35
43
  @current_channel = channel_num
36
44
  end
37
45
 
38
- def get_next_playlist
46
+ def fetch_next_playlist
39
47
  uri = URI('http://www.douban.com/j/app/radio/people')
40
48
  params = {
41
49
  :app_name => 'radio_desktop_mac',
@@ -53,12 +61,14 @@ module DoubanFM
53
61
 
54
62
  @current_playlist = JSON.parse(res.body)
55
63
 
64
+ @logger.log("raw playlist #{current_playlist}")
65
+
56
66
  unless @current_playlist['err'].nil?
57
67
  raise @current_playlist
58
68
  end
59
69
  end
60
70
 
61
- def play_current_playlist
71
+ def play
62
72
  @continue = true
63
73
 
64
74
  Thread.new do
@@ -72,10 +82,12 @@ module DoubanFM
72
82
  break
73
83
  end
74
84
 
75
- @player_pid = spawn("mpg123 #{song['url']}")
85
+ @player_pid = spawn("mpg123 #{song['url']} > /dev/null 2>&1")
76
86
 
77
87
  @semaphore.unlock
78
88
 
89
+ @logger.log("playing song \"#{song['title']}\"")
90
+
79
91
  Process.wait
80
92
  end
81
93
 
@@ -84,12 +96,58 @@ module DoubanFM
84
96
  end
85
97
  end
86
98
 
99
+ def add_to_mpd(host = 'localhost', port = 6600)
100
+ mpd = MPD.new(host, port)
101
+ mpd.connect
102
+
103
+ begin
104
+ songs = mpd.send_command(:listplaylistinfo, DOUBAN_FM_MPD_PLAYLIST)
105
+ if songs.is_a? String
106
+ total = 1
107
+ else
108
+ total = songs.length
109
+ end
110
+ rescue
111
+ total = 0
112
+ end
113
+
114
+ @logger.log("current total number of songs in mpd #{total}")
115
+
116
+ if total < MIN_SONGS_IN_DOUBAN_FM_MPD_PLAYLIST
117
+ douban_fm_playlist = MPD::Playlist.new(mpd, {:playlist => DOUBAN_FM_MPD_PLAYLIST})
118
+
119
+ begin
120
+ @logger.log('fetch next playlist')
121
+
122
+ fetch_next_playlist
123
+ rescue
124
+ @logger.log('session expired, relogin')
125
+
126
+ login
127
+
128
+ @logger.log('fetch next playlist')
129
+
130
+ fetch_next_playlist
131
+ end
132
+
133
+ @logger.log("add more songs to mpd")
134
+
135
+ @current_playlist['song'].each do |song|
136
+ @logger.log("send [#{song['url'].gsub('\\', '')}] to mpd")
137
+
138
+ douban_fm_playlist.add(song['url'].gsub('\\', ''))
139
+ end
140
+ end
141
+
142
+ mpd.disconnect
143
+ end
144
+
87
145
  def stop
88
146
  @semaphore.synchronize do
89
147
  @continue = false
90
148
 
91
149
  begin
92
- Process.kill(9, @player_pid)
150
+ Process.kill(9, @player_pid) unless @player_pid.nil?
93
151
  rescue Errno::ESRCH
94
152
  end
95
153
  end
@@ -0,0 +1,11 @@
1
+ module DoubanFM
2
+ class DummyLogger
3
+ def log(message); end
4
+ end
5
+
6
+ class ConsoleLogger
7
+ def log(message)
8
+ puts message
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module DoubanFM
2
- VERSION = '0.0.2'
2
+ VERSION = '0.1.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: douban.fm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,29 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
  date: 2013-01-17 00:00:00.000000000 Z
13
- dependencies: []
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ruby-mpd
16
+ requirement: &70182454448200 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70182454448200
25
+ - !ruby/object:Gem::Dependency
26
+ name: highline
27
+ requirement: &70182454447640 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70182454447640
14
36
  description: douban.fm
15
37
  email:
16
38
  - hxliang1982@gmail.com
@@ -28,6 +50,7 @@ files:
28
50
  - douban.fm.gemspec
29
51
  - lib/douban.fm.rb
30
52
  - lib/douban.fm/douban_fm.rb
53
+ - lib/douban.fm/logger.rb
31
54
  - lib/douban.fm/version.rb
32
55
  homepage: https://github.com/honnix/douban.fm
33
56
  licenses: []
@@ -49,8 +72,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
72
  version: '0'
50
73
  requirements: []
51
74
  rubyforge_project:
52
- rubygems_version: 1.8.24
75
+ rubygems_version: 1.8.15
53
76
  signing_key:
54
77
  specification_version: 3
55
78
  summary: douban.fm
56
79
  test_files: []
80
+ has_rdoc: