douban.fm 0.0.2 → 0.1.0

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.
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: