mdisc 0.1.1 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7f5547a564f2f00c77ae5e07ab4f343553ad39d
4
- data.tar.gz: ae58b382a6e761fbf450c7f9777f74aae550d04b
3
+ metadata.gz: acbc08f6f64182d057b78a6234572c33b09d65a4
4
+ data.tar.gz: d6ba36291042a95a9623a044894632523e680d15
5
5
  SHA512:
6
- metadata.gz: 05887319f06a3465625493599c567434b43a663085902e42fb1b6bdd2feba05e29722ebc9f72577c8ebc488dc76d66fda73633efd2889eb3b4d656c2a874b3a3
7
- data.tar.gz: b58337cc0d810e8e08bf7ce9cd8b8abc4e199346e818ba2357a0147d6908e0aae38fdbd8fe8fe85b64534024baed77a103fda2ff3c62c67b88b5d241516e6779
6
+ metadata.gz: 1e2841dd8b0cea3d0bc97409972624aed93409e0923fce9193acabc7d564ba7d8b487e688977a127158d9fe2fdb3c464d8eb6dc6b247ddc371d251a0fa3a91dc
7
+ data.tar.gz: d45dcb453da9a6c249ad90dc05894074bb73e02e6cdb8e80275bacb436f157bf154760f79f52e3b7423ab13468ba655163a697d79ce9a19a1ee2ee78e0856c05
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Mdisc
2
2
 
3
+ ![Mdisc](http://cl.ly/Y6Qz/mdisc.png)
4
+
3
5
  Mdisc, built with Ruby 2.1, is a command line music player that wirelessly plugs in Netease music(http://music.163.com).
4
6
 
5
7
  ## Installation
@@ -17,7 +19,7 @@ After finishing the installation, open your terminal and input `mdisc`. Music's
17
19
 
18
20
  Sorry, I do not test Mdisc in Linux. If you try it in Linux and catch some problem, please issue me. Thanks!
19
21
 
20
- ## Shortcuts
22
+ ## Shortcut
21
23
 
22
24
  | Key | Explanation | 中文释义 |
23
25
  | :---|:---------------------|:---------------------|
@@ -45,13 +47,13 @@ Sorry, I do not test Mdisc in Linux. If you try it in Linux and catch some probl
45
47
 
46
48
  Mdisc will make a new directory `~/.mdisc` and touch a file to store user's data for the first time.
47
49
 
48
- ## Big Thanks
50
+ ## Thanks
49
51
 
50
52
  [NetEase-MusicBox](https://github.com/bluetomlee/NetEase-MusicBox)
51
53
 
52
54
  [网易云音乐API分析](https://github.com/yanunon/NeteaseCloudMusic/wiki/网易云音乐API分析)
53
55
 
54
- Their great projects inspire me. Thanks!
56
+ Their great projects inspired me. Thanks!
55
57
 
56
58
  ## License
57
59
 
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
 
data/bin/mdisc CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.expand_path('../../lib/mdisc', __FILE__)
3
+ require_relative '../lib/mdisc'
4
4
 
5
5
  main = Menu.new
6
6
  main.start
@@ -1,5 +1,6 @@
1
- require "mdisc/version"
2
- require File.expand_path('../mdisc/menu', __FILE__)
3
- require File.expand_path('../mdisc/api', __FILE__)
4
- require File.expand_path('../mdisc/player', __FILE__)
5
- require File.expand_path('../mdisc/ui', __FILE__)
1
+ require_relative 'mdisc/version'
2
+ require_relative 'mdisc/menu'
3
+ require_relative 'mdisc/api'
4
+ require_relative 'mdisc/player'
5
+ require_relative 'mdisc/ui'
6
+ require_relative 'mdisc/screen'
@@ -3,63 +3,48 @@ require 'json'
3
3
  require 'digest'
4
4
 
5
5
  class NetEase
6
+ TIMEOUT = 10
7
+
6
8
  def initialize
7
9
  @header = {
8
- "Accept" => "*/*",
10
+ "Accept" => "*/*",
9
11
  "Accept-Encoding" => "gzip,deflate,sdch",
10
12
  "Accept-Language" => "zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4",
11
- "Connection" => "keep-alive",
12
- "Content-Type" => "application/x-www-form-urlencoded",
13
- "Host" => "music.163.com",
14
- "Referer" => "http://music.163.com/",
15
- "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36"
16
- }
17
-
18
- @cookies = {
19
- "appver" => "2.0.2"
13
+ "Connection" => "keep-alive",
14
+ "Content-Type" => "application/x-www-form-urlencoded",
15
+ "Host" => "music.163.com",
16
+ "Referer" => "http://music.163.com/",
17
+ "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36"
20
18
  }
21
19
 
22
- @default_timeout = 10
23
- Unirest.timeout @default_timeout
24
- end
25
-
26
- def http_request(method, action, query = nil)
27
- connection =
28
- if method == 'GET'
29
- url = (query.nil? ? action : "#{action}?#{query}")
30
- Unirest.get(url, headers: @header)
31
- elsif method == 'POST'
32
- Unirest.post(action, headers: @header, parameters: query)
33
- end
34
-
35
- connection.body
20
+ Unirest.timeout TIMEOUT
36
21
  end
37
22
 
38
23
  # Log in
39
24
  def login(username, password)
40
- action = "http://music.163.com/api/login/"
25
+ action = 'http://music.163.com/api/login/'
41
26
  query = {
42
27
  "username" => username,
43
28
  "password" => Digest::MD5.hexdigest(password),
44
29
  "rememberLogin" => "true"
45
30
  }
46
- begin
47
- return http_request('POST', action, query)
48
- rescue => e
49
- return {"code" => 501}
50
- end
31
+
32
+ http_request('POST', action, query)
33
+ rescue => e
34
+ {"code" => 501}
51
35
  end
52
36
 
53
37
  # User's playlists
54
38
  def user_playlists(uid, offset = 0, limit = 100)
55
39
  action = "http://music.163.com/api/user/playlist/?offset=#{offset}&limit=#{limit}&uid=#{uid}"
56
40
  data = http_request('GET', action)
41
+
57
42
  data['playlist']
58
43
  end
59
44
 
60
45
  # Search song(1),artist(100),album(10),playlist(1000),user(1002)
61
46
  def search(s, stype = 1, offset = 0, limit = 100)
62
- action = "http://music.163.com/api/search/get/web"
47
+ action = 'http://music.163.com/api/search/get/web'
63
48
  query = {
64
49
  "s" => s,
65
50
  "type" => stype,
@@ -67,6 +52,7 @@ class NetEase
67
52
  "total" => true,
68
53
  "limit" => limit
69
54
  }
55
+
70
56
  http_request('POST', action, query)
71
57
  end
72
58
 
@@ -75,25 +61,27 @@ class NetEase
75
61
  def new_albums(offset=0, limit=50)
76
62
  action = "http://music.163.com/api/album/new?area=ALL&offset=#{offset}&total=true&limit=#{limit}"
77
63
  data = http_request('GET', action)
64
+
78
65
  data['albums']
79
66
  end
80
67
 
81
68
  # Top playlists
82
69
  # hot||new http://music.163.com/#/discover/playlist/
83
-
84
70
  # '全部' => '%E5%85%A8%E9%83%A8'
85
71
  def top_playlists(category = '%E5%85%A8%E9%83%A8', order = 'hot', offset = 0, limit = 100)
86
- flag = (offset > 0 ? true : false)
72
+ flag = offset > 0 ? true : false
87
73
  action = "http://music.163.com/api/playlist/list?cat=#{category}&order=#{order}&offset=#{offset}&total=#{flag}&limit=#{limit}"
88
74
  data = http_request('GET', action)
89
- return data['playlists']
75
+
76
+ data['playlists']
90
77
  end
91
78
 
92
79
  # Playlist's details
93
80
  def playlist_detail(playlist_id)
94
81
  action = "http://music.163.com/api/playlist/detail?id=#{playlist_id}"
95
82
  data = http_request('GET', action)
96
- return data['result']['tracks']
83
+
84
+ data['result']['tracks']
97
85
  end
98
86
 
99
87
  # Top artists
@@ -101,61 +89,70 @@ class NetEase
101
89
  def top_artists(offset = 0, limit = 100)
102
90
  action = "http://music.163.com/api/artist/top?offset=#{offset}&total=false&limit=#{limit}"
103
91
  data = http_request('GET', action)
104
- return data['artists']
92
+
93
+ data['artists']
105
94
  end
106
95
 
107
96
  # Top songlist
108
97
  # http://music.163.com/#/discover/toplist 100
109
98
  def top_songlist
110
- action = "http://music.163.com/discover/toplist"
99
+ action = 'http://music.163.com/discover/toplist'
111
100
  connection = http_request('GET', action)
112
- songids = connection.scan(/\/song\?id=(\d+)/)
113
- return [] if songids == []
114
- return songs_detail(songids.uniq)
101
+ songids = connection.scan(/\/song\?id=(\d+)/).uniq
102
+
103
+ songs_detail songids
115
104
  end
116
105
 
117
106
  # Songs to which a artist belongs.
118
107
  def artists(artist_id)
119
108
  action = "http://music.163.com/api/artist/#{artist_id}"
120
109
  data = http_request('GET', action)
121
- return data['hotSongs']
110
+
111
+ data['hotSongs']
122
112
  end
123
113
 
124
114
  # album id -> song id set
125
115
  def album(album_id)
126
116
  action = "http://music.163.com/api/album/#{album_id}"
127
117
  data = http_request('GET', action)
128
- return data['album']['songs']
118
+
119
+ data['album']['songs']
129
120
  end
130
121
 
131
122
  # song ids -> song urls (details)
132
- def songs_detail(ids, offset=0)
123
+ def songs_detail(ids, offset = 0)
124
+ return [] if ids.empty?
125
+
133
126
  tmpids = ids[offset, 100]
134
127
  action = "http://music.163.com/api/song/detail?ids=[#{tmpids.join(',')}]"
135
128
  data = http_request('GET', action)
136
- return data['songs']
129
+
130
+ data['songs']
137
131
  end
138
132
 
139
133
  # song id -> song url (details)
140
- def song_detail(music_id)
141
- id = music_id.join(',')
134
+ def song_detail(song_id)
135
+ id = song_id.join(',')
142
136
  action = "http://music.163.com/api/song/detail/?id=#{id}&ids=[#{id}]"
143
137
  data = http_request('GET', action)
144
- return data['songs']
138
+
139
+ data['songs']
145
140
  end
146
141
 
147
142
  # DJ channels: hot today(0), week(10), history(20), new(30)
148
143
  def djchannels(stype = 0, offset = 0, limit = 50)
149
144
  action = "http://music.163.com/discover/djchannel?type=#{stype}&offset=#{offset}&limit=#{limit}"
150
145
  connection = http_request('GET', action)
151
- channelids = connection.scan(/\/dj\?id=(\d+)/) || []
152
- return [] if channelids.empty?
153
- return channel_detail(channelids.uniq)
146
+ channelids = connection.scan(/\/dj\?id=(\d+)/).uniq || []
147
+
148
+ channel_detail channelids
154
149
  end
155
150
 
156
151
  # DJchannel (id, channel_name) ids -> song urls (details)
157
152
  # channels -> songs
158
153
  def channel_detail(channelids)
154
+ return [] if channelids.empty?
155
+
159
156
  channels = []
160
157
 
161
158
  # ["xxxxxx"] -> "xxxxxx"
@@ -164,7 +161,7 @@ class NetEase
164
161
  begin
165
162
  data = http_request('GET', action)
166
163
  channel = dig_info(data['program']['mainSong'], 'channels')
167
- channels.push(channel)
164
+ channels.push channel
168
165
  rescue => e
169
166
  next
170
167
  end
@@ -175,6 +172,7 @@ class NetEase
175
172
 
176
173
  def dig_info(data, dig_type)
177
174
  tmp = []
175
+
178
176
  case dig_type
179
177
  when 'songs'
180
178
  data.each do |song|
@@ -190,9 +188,8 @@ class NetEase
190
188
  song_info['artist'] = song['artist'].join('')
191
189
  elsif song.include? 'artists'
192
190
  song['artists'].each do |artist|
193
- song_info['artist'].push(artist['name'].strip)
191
+ song_info['artist'].push artist['name'].strip
194
192
  end
195
- song_info['artist'].join(',')
196
193
  else
197
194
  song_info['artist'] = '未知艺术家'
198
195
  end
@@ -244,4 +241,19 @@ class NetEase
244
241
 
245
242
  tmp
246
243
  end
244
+
245
+ private
246
+
247
+ def http_request(method, action, query = nil)
248
+ connection =
249
+ if method == 'GET'
250
+ Unirest.get(action, headers: @header)
251
+ elsif method == 'POST'
252
+ Unirest.post(action, headers: @header, parameters: query)
253
+ end
254
+
255
+ connection.body
256
+ rescue => e
257
+ []
258
+ end
247
259
  end
@@ -24,6 +24,8 @@ SHORTCUT = [
24
24
  ]
25
25
 
26
26
  class Menu
27
+ WAIT_TIME = 0.1
28
+
27
29
  attr_accessor :player, :ui, :netease, :screen
28
30
 
29
31
  def initialize
@@ -44,8 +46,6 @@ class Menu
44
46
  @collection = []
45
47
  @playlists = []
46
48
  @account = {}
47
-
48
- @wait = 0.1
49
49
  @carousel = ->(left, right, x){x < left ? right : (x > right ? left : x)}
50
50
 
51
51
  read_data
@@ -53,7 +53,7 @@ class Menu
53
53
 
54
54
  def start
55
55
  ui.build_menu(@datatype, @title, @datalist, @offset, @index, @step)
56
- @stack.push([@datatype, @title, @datalist, @offset, @index, @step])
56
+ @stack.push [@datatype, @title, @datalist, @offset, @index, @step]
57
57
 
58
58
  loop do
59
59
  datatype = @datatype
@@ -92,7 +92,8 @@ class Menu
92
92
  @offset = @offset + step
93
93
  @index = (index + step).divmod(step)[0] * step
94
94
 
95
- # Forward
95
+ # If highlighted item is a menu or playlists, just enter it.
96
+ # If highlighted item is a song or an dj channel, just play it.
96
97
  when 'l'
97
98
  next if @datatype == 'help'
98
99
  if @datatype == 'songs' || @datatype == 'djchannels'
@@ -100,7 +101,7 @@ class Menu
100
101
  @present_songs = [datatype, title, datalist, offset, index]
101
102
  else
102
103
  ui.build_loading
103
- dispatch_enter(idx)
104
+ dispatch_enter idx
104
105
  @index = 0
105
106
  @offset = 0
106
107
  end
@@ -118,22 +119,22 @@ class Menu
118
119
  # Next song
119
120
  when ']'
120
121
  player.next
121
- sleep @wait
122
+ sleep WAIT_TIME
122
123
 
123
124
  # Previous song
124
125
  when '['
125
126
  player.prev
126
- sleep @wait
127
+ sleep WAIT_TIME
127
128
 
128
129
  # Play or pause a song.
129
130
  when ' '
130
131
  player.play(datatype, datalist, idx)
131
- sleep @wait
132
+ sleep WAIT_TIME
132
133
 
133
134
  # Load present playlist.
134
135
  when 'p'
135
136
  next if @present_songs.empty?
136
- @stack.push([datatype, title, datalist, offset, index])
137
+ @stack.push [datatype, title, datalist, offset, index]
137
138
  @datatype, @title, @datalist, @offset, @index = @present_songs[0], @present_songs[1], @present_songs[2], @present_songs[3], @present_songs[4]
138
139
 
139
140
  # Star a song, a playlist or an album.
@@ -151,7 +152,7 @@ class Menu
151
152
 
152
153
  # Load favorite playlists.
153
154
  when 't'
154
- @stack.push([datatype, title, datalist, offset, index])
155
+ @stack.push [datatype, title, datalist, offset, index]
155
156
  @datatype = 'playlists'
156
157
  @title = '网易云音乐 > 收藏精选歌单'
157
158
  @datalist = @playlists
@@ -160,25 +161,25 @@ class Menu
160
161
 
161
162
  # Load favorite songs.
162
163
  when 'c'
163
- @stack.push([datatype, title, datalist, offset, index])
164
+ @stack.push [datatype, title, datalist, offset, index]
164
165
  @datatype = 'songs'
165
166
  @title = '网易云音乐 > 收藏歌曲列表'
166
167
  @datalist = @collection
167
168
  @offset = 0
168
169
  @index = 0
169
170
 
170
- # Load favorite albums
171
+ # Load favorite albums.
171
172
  when 'a'
172
- @stack.push([datatype, title, datalist, offset, index])
173
+ @stack.push [datatype, title, datalist, offset, index]
173
174
  @datatype = 'albums'
174
175
  @title = '网易云音乐 > 收藏专辑'
175
176
  @datalist = @albums
176
177
  @offset = 0
177
178
  @index = 0
178
179
 
179
- # Load favorite dj channels
180
+ # Load favorite dj channels.
180
181
  when 'z'
181
- @stack.push([datatype, title, datalist, offset, index])
182
+ @stack.push [datatype, title, datalist, offset, index]
182
183
  @datatype = 'djchannels'
183
184
  @title = '网易云音乐 > 收藏 DJ 节目'
184
185
  @datalist = @djs
@@ -187,19 +188,17 @@ class Menu
187
188
 
188
189
  # Remove an entry from the present list.
189
190
  when 'r'
190
- if (datatype != 'main') && !datalist.empty?
191
- @datalist.delete_at(idx)
192
- @index = @carousel[@offset, [datalist.size, offset+step].min - 1, idx]
193
- end
191
+ next if datatype == 'main' || datalist.empty?
192
+ @datalist.delete_at idx
193
+ @index = @carousel[@offset, [datalist.size, offset+step].min - 1, idx]
194
194
 
195
- # Main menu.
195
+ # Main menu
196
196
  when 'm'
197
- if datatype != 'main'
198
- @stack.push([datatype, title, datalist, offset, index])
199
- @datatype, @title, @datalist = @stack[0][0], @stack[0][1], @stack[0][2]
200
- @offset = 0
201
- @index = 0
202
- end
197
+ next if datatype == 'main'
198
+ @stack.push [datatype, title, datalist, offset, index]
199
+ @datatype, @title, @datalist = @stack[0][0], @stack[0][1], @stack[0][2]
200
+ @offset = 0
201
+ @index = 0
203
202
 
204
203
  end
205
204
 
@@ -212,13 +211,12 @@ class Menu
212
211
  end
213
212
 
214
213
  def dispatch_enter(idx)
215
- # netease = @netease
216
214
  datatype = @datatype
217
215
  title = @title
218
216
  datalist = @datalist
219
217
  offset = @offset
220
218
  index = @index
221
- @stack.push([datatype, title, datalist, offset, index])
219
+ @stack.push [datatype, title, datalist, offset, index]
222
220
 
223
221
  case datatype
224
222
  when 'main'
@@ -226,24 +224,24 @@ class Menu
226
224
 
227
225
  # Hot songs to which a artist belongs.
228
226
  when 'artists'
229
- artist_id = datalist[idx]['artist_id']
230
- songs = netease.artists(artist_id)
227
+ id = datalist[idx]['artist_id']
228
+ songs = netease.artists id
231
229
  @datatype = 'songs'
232
230
  @datalist = netease.dig_info(songs, 'songs')
233
231
  @title += " > #{datalist[idx]['aritsts_name']}"
234
232
 
235
233
  # All songs to which an album belongs.
236
234
  when 'albums'
237
- album_id = datalist[idx]['album_id']
238
- songs = netease.album(album_id)
235
+ id = datalist[idx]['album_id']
236
+ songs = netease.album id
239
237
  @datatype = 'songs'
240
238
  @datalist = netease.dig_info(songs, 'songs')
241
239
  @title += " > #{datalist[idx]['albums_name']}"
242
240
 
243
241
  # All songs to which a playlist belongs.
244
242
  when 'playlists'
245
- playlist_id = datalist[idx]['playlist_id']
246
- songs = netease.playlist_detail(playlist_id)
243
+ id = datalist[idx]['playlist_id']
244
+ songs = netease.playlist_detail id
247
245
  @datatype = 'songs'
248
246
  @datalist = netease.dig_info(songs, 'songs')
249
247
  @title += " > #{datalist[idx]['playlists_name']}"
@@ -251,10 +249,7 @@ class Menu
251
249
  end
252
250
 
253
251
  def choice_channel(idx)
254
- # netease = @netease
255
-
256
252
  case idx
257
-
258
253
  # Top
259
254
  when 0
260
255
  songs = netease.top_songlist
@@ -286,21 +281,10 @@ class Menu
286
281
  # My playlist
287
282
  when 4
288
283
  # Require user's account before fetching his playlists.
289
- if !@userid
290
- user_info = netease.login(@account[0], @account[1]) unless @account.empty?
291
-
292
- if @account == {} || user_info['code'] != 200
293
- data = ui.build_login
294
- return if data == -1
295
- user_info, @account = data[0], data[1]
296
- end
297
-
298
- @username = user_info['profile']['nickname']
299
- @userid = user_info['account']['id']
300
- end
284
+ check_user_id
301
285
 
302
286
  # Fetch this user's all playlists while he logs in successfully.
303
- my_playlist = netease.user_playlists(@userid)
287
+ my_playlist = netease.user_playlists @userid
304
288
  @datalist = netease.dig_info(my_playlist, 'playlists')
305
289
  @datatype = 'playlists'
306
290
  @title += " > #{@username} 的歌单"
@@ -311,7 +295,7 @@ class Menu
311
295
  @title += ' > DJ 节目'
312
296
  @datalist = netease.djchannels
313
297
 
314
- # Favorite things.
298
+ # Favorite things
315
299
  when 6
316
300
  favorite
317
301
 
@@ -331,17 +315,15 @@ class Menu
331
315
  end
332
316
 
333
317
  def favorite
334
- # ui = @ui
335
318
  x = ui.build_favorite_menu
336
319
 
337
320
  if (1..4).include? x.to_i
338
- @stack.push([@datatype, @title, @datalist, @offset, @index])
321
+ @stack.push [@datatype, @title, @datalist, @offset, @index]
339
322
  @index = 0
340
323
  @offset = 0
341
324
  end
342
325
 
343
326
  case x
344
-
345
327
  when '1'
346
328
  @datatype = 'songs'
347
329
  @datalist = @collection
@@ -366,48 +348,50 @@ class Menu
366
348
  end
367
349
 
368
350
  def search
369
- # ui = @ui
370
351
  x = ui.build_search_menu
371
352
 
372
353
  if (1..4).include? x.to_i
373
- @stack.push([@datatype, @title, @datalist, @offset, @index])
354
+ @stack.push [@datatype, @title, @datalist, @offset, @index]
374
355
  @index = 0
375
356
  @offset = 0
376
357
  end
377
358
 
378
359
  case x
379
-
380
360
  when '1'
381
361
  @datatype = 'songs'
382
- @datalist = ui.build_search('songs')
362
+ @datalist = ui.build_search @datatype
383
363
  @title = '歌曲搜索列表'
384
364
 
385
365
  when '2'
386
366
  @datatype = 'artists'
387
- @datalist = ui.build_search('artists')
367
+ @datalist = ui.build_search @datatype
388
368
  @title = '艺术家搜索列表'
389
369
 
390
370
  when '3'
391
371
  @datatype = 'albums'
392
- @datalist = ui.build_search('albums')
372
+ @datalist = ui.build_search @datatype
393
373
  @title = '专辑搜索列表'
394
374
 
395
375
  when '4'
396
376
  @datatype = 'playlists'
397
- @datalist = ui.build_search('playlists')
377
+ @datalist = ui.build_search @datatype
398
378
  @title = '精选歌单搜索列表'
399
379
  end
400
380
  end
401
381
 
402
382
  private
403
383
 
404
- def check_mdisc_dir
405
- Dir.mkdir File.expand_path("~/.mdisc") unless Dir.exist? File.expand_path("~/.mdisc")
384
+ def check_dir
385
+ dir = "#{ENV['HOME']}/.mdisc"
386
+ unless Dir.exist? dir
387
+ Dir.mkdir dir
388
+ end
406
389
  end
407
390
 
408
391
  def read_data
409
- check_mdisc_dir
410
- user_file = File.expand_path("~/.mdisc/flavor.json")
392
+ check_dir
393
+
394
+ user_file = "#{ENV['HOME']}/.mdisc/flavor.json"
411
395
  return unless File.exist? user_file
412
396
 
413
397
  data = JSON.parse(File.read(user_file))
@@ -419,7 +403,10 @@ class Menu
419
403
  end
420
404
 
421
405
  def write_data
422
- user_file = File.expand_path("~/.mdisc/flavor.json")
406
+ check_dir
407
+
408
+ user_file = "#{ENV['HOME']}/.mdisc/flavor.json"
409
+
423
410
  data = {
424
411
  :account => @account,
425
412
  :collection => @collection,
@@ -432,4 +419,19 @@ class Menu
432
419
  f.write(JSON.generate(data))
433
420
  end
434
421
  end
422
+
423
+ def check_user_id
424
+ if !@userid
425
+ user_info = netease.login(@account[0], @account[1]) unless @account.empty?
426
+
427
+ if @account == {} || user_info['code'] != 200
428
+ data = ui.build_login
429
+ return if data == -1
430
+ user_info, @account = data[0], data[1]
431
+ end
432
+
433
+ @username = user_info['profile']['nickname']
434
+ @userid = user_info['account']['id']
435
+ end
436
+ end
435
437
  end
@@ -1,6 +1,8 @@
1
1
  require 'open4'
2
2
 
3
3
  class Player
4
+ WAIT_TIME = 0.5
5
+
4
6
  attr_accessor :ui
5
7
 
6
8
  def initialize
@@ -12,7 +14,6 @@ class Player
12
14
  @pause_flag = false
13
15
  @songs = []
14
16
  @idx = 0
15
- @wait = 0.5
16
17
  @carousel = ->(left, right, x){x < left ? right : (x > right ? left : x)}
17
18
  end
18
19
 
@@ -48,7 +49,7 @@ class Player
48
49
 
49
50
  def switch
50
51
  stop
51
- sleep @wait
52
+ sleep WAIT_TIME
52
53
  recall
53
54
  end
54
55
 
@@ -83,7 +84,7 @@ class Player
83
84
 
84
85
  def next
85
86
  stop
86
- sleep @wait
87
+ sleep WAIT_TIME
87
88
 
88
89
  @idx = @carousel[0, @songs.size - 1, @idx + 1]
89
90
  recall
@@ -91,7 +92,7 @@ class Player
91
92
 
92
93
  def prev
93
94
  stop
94
- sleep @wait
95
+ sleep WAIT_TIME
95
96
 
96
97
  @idx = @carousel[0, @songs.size - 1, @idx - 1]
97
98
  recall
@@ -0,0 +1,46 @@
1
+ require 'curses'
2
+
3
+ # The curses library only provides limit functions.
4
+ # Thus we need to add more wrapper functions based on curses.
5
+
6
+ class Screen
7
+ def initialize(height = 25, width = 80)
8
+ Curses.init_screen
9
+ Curses.start_color
10
+ Curses.cbreak
11
+ Curses.stdscr.keypad true
12
+ Curses.init_pair(1, Curses::COLOR_BLUE, Curses::COLOR_BLACK)
13
+ Curses.init_pair(2, Curses::COLOR_CYAN, Curses::COLOR_BLACK)
14
+ Curses.init_pair(3, Curses::COLOR_RED, Curses::COLOR_BLACK)
15
+ Curses.init_pair(4, Curses::COLOR_MAGENTA, Curses::COLOR_BLACK)
16
+ # height, width, top, left
17
+ @draw = Curses::Window.new(height, width, 0, 0)
18
+ end
19
+
20
+ def line(y, x, string, num = 0)
21
+ color = Curses.color_pair num
22
+ @draw.setpos(y, x)
23
+ @draw.clrtoeol
24
+ @draw.attrset(color)
25
+ @draw.addstr(string)
26
+ end
27
+
28
+ def clear(top, bottom)
29
+ (top..bottom).each do |i|
30
+ @draw.setpos(i, 0)
31
+ @draw.clrtoeol
32
+ end
33
+ end
34
+
35
+ def refresh
36
+ @draw.refresh
37
+ end
38
+
39
+ def getch
40
+ @draw.getch
41
+ end
42
+
43
+ def setpos(*args)
44
+ @draw.setpos *args
45
+ end
46
+ end
@@ -44,132 +44,86 @@ class Ui
44
44
  PLAYER_POINTER_X = PLAYER_X - 3
45
45
 
46
46
  def initialize
47
- Curses.init_screen
48
- Curses.start_color
49
- Curses.cbreak
50
- Curses.stdscr.keypad(true)
51
- Curses.init_pair(1, Curses::COLOR_BLUE, Curses::COLOR_BLACK)
52
- Curses.init_pair(2, Curses::COLOR_CYAN, Curses::COLOR_BLACK)
53
- Curses.init_pair(3, Curses::COLOR_RED, Curses::COLOR_BLACK)
54
- Curses.init_pair(4, Curses::COLOR_MAGENTA, Curses::COLOR_BLACK)
55
-
56
- # height, width, top, left
57
- self.screen = Curses::Window.new(SCREEN_HEIGHT, SCREEN_WIDTH, 0, 0)
58
-
47
+ self.screen = Screen.new(SCREEN_HEIGHT, SCREEN_WIDTH)
59
48
  self.netease = NetEase.new
60
49
  end
61
50
 
62
51
  def build_playinfo(song_name, artist, pause = false)
63
52
  if pause
64
- putstr(screen, PLAYER_STATUS_Y, PLAYER_NOTE_X, '■', Curses.color_pair(3))
53
+ screen.line(PLAYER_STATUS_Y, PLAYER_NOTE_X, '■', 3)
65
54
  else
66
- putstr(screen, PLAYER_STATUS_Y, PLAYER_NOTE_X, '▶', Curses.color_pair(3))
55
+ screen.line(PLAYER_STATUS_Y, PLAYER_NOTE_X, '▶', 3)
67
56
  end
68
57
 
69
- sn = pretty_format(song_name, 0, 32)
70
- at = pretty_format(artist, 0, 28)
58
+ sn = pretty(song_name, 0, 32)
59
+ at = pretty(artist, 0, 28)
71
60
  info = "#{sn} - #{at}"
72
- putstr(screen, PLAYER_STATUS_Y, PLAYER_X, info, Curses.color_pair(4))
61
+ screen.line(PLAYER_STATUS_Y, PLAYER_X, info, 4)
73
62
  screen.refresh
74
63
  end
75
64
 
76
65
  def build_loading
77
- clear_to_bottom(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
78
- putstr(screen, PLAYER_CONTENT_Y, PLAYER_X, 'loading...', Curses.color_pair(1))
66
+ screen.clear(PLAYER_CONTENT_Y, SCREEN_HEIGHT)
67
+ screen.line(PLAYER_CONTENT_Y, PLAYER_X, 'loading...', 1)
79
68
  screen.refresh
80
69
  end
81
70
 
82
71
  def build_menu(datatype, title, datalist, offset, index, step)
83
- title = pretty_format(title, 0, 52)
72
+ title = pretty(title, 0, 52)
84
73
 
85
- clear_to_bottom(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
86
- putstr(screen, PLAYER_TITLE_Y, PLAYER_X, title, Curses.color_pair(1))
74
+ screen.clear(PLAYER_CONTENT_Y, SCREEN_HEIGHT)
75
+ screen.line(PLAYER_TITLE_Y, PLAYER_X, title, 1)
87
76
 
88
77
  if datalist.size == 0
89
- putstr(screen, PLAYER_CONTENT_Y, PLAYER_X, '没有内容 Orz')
78
+ screen.line(PLAYER_CONTENT_Y, PLAYER_X, '没有内容 Orz')
90
79
  else
80
+ entries = offset...[datalist.length, offset + step].min
81
+
91
82
  case datatype
92
83
  when 'main'
93
- (offset...[datalist.length, offset + step].min).each do |i|
94
- if i == index
95
- info = "♩ #{i}. #{datalist[i]}"
96
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_POINTER_X, info, Curses.color_pair(2))
97
- else
98
- info = "#{i}. #{datalist[i]}"
99
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
100
- end
84
+ show(entries, index, offset, datalist) do |i, datalist|
85
+ "#{i}. #{datalist[i]}"
101
86
  end
102
87
 
103
- putstr(screen, PLAYER_INFO_Y, PLAYER_X, 'Crafted with ❤ by cosmtrek', Curses.color_pair(3))
88
+ screen.line(PLAYER_INFO_Y, PLAYER_X, 'Crafted with ❤ by cosmtrek', 3)
104
89
 
105
90
  when 'songs'
106
- (offset...[datalist.length, offset + step].min).each do |i|
107
- sn = pretty_format(datalist[i]['song_name'], 0, 32)
108
- at = pretty_format(datalist[i]['artist'], 0, 28)
109
-
110
- if i == index
111
- info = "♩ #{i}. #{sn} - #{at}"
112
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_POINTER_X, info, Curses.color_pair(2))
113
- else
114
- info = "#{i}. #{sn} - #{at}"
115
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
116
- end
91
+ show(entries, index, offset, datalist) do |i, datalist|
92
+ sn = pretty(datalist[i]['song_name'], 0, 32)
93
+ at = pretty(datalist[i]['artist'], 0, 28)
94
+ "#{i}. #{sn} - #{at}"
117
95
  end
118
96
 
119
97
  when 'artists'
120
- (offset...[datalist.length, offset + step].min).each do |i|
121
- an = pretty_format(datalist[i]['artists_name'], 0, 32)
122
- if i == index
123
- info = "♩ #{i}. #{an}"
124
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_POINTER_X, info, Curses.color_pair(2))
125
- else
126
- info = "#{i}. #{an}"
127
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
128
- end
98
+ show(entries, index, offset, datalist) do |i, datalist|
99
+ an = pretty(datalist[i]['artists_name'], 0, 32)
100
+ "#{i}. #{an}"
129
101
  end
130
102
 
131
103
  when 'albums'
132
- (offset...[datalist.length, offset + step].min).each do |i|
133
- al = pretty_format(datalist[i]['albums_name'], 0, 32)
134
- an = pretty_format(datalist[i]['artists_name'], 0, 28)
135
- if i == index
136
- info = "♩ #{i}. #{al} - #{an}"
137
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_POINTER_X, info, Curses.color_pair(2))
138
- else
139
- info = "#{i}. #{al} - #{an}"
140
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
141
- end
104
+ show(entries, index, offset, datalist) do |i, datalist|
105
+ al = pretty(datalist[i]['albums_name'], 0, 32)
106
+ an = pretty(datalist[i]['artists_name'], 0, 28)
107
+ "#{i}. #{al} - #{an}"
142
108
  end
143
109
 
144
110
  when 'playlists'
145
- (offset...[datalist.length, offset + step].min).each do |i|
146
- pn = pretty_format(datalist[i]['playlists_name'], 0, 32);
147
- cn = pretty_format(datalist[i]['creator_name'], 0, 28);
148
- if i == index
149
- info = "♩ #{i}. #{pn} - #{cn}"
150
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_POINTER_X, info, Curses.color_pair(2))
151
- else
152
- info = "#{i}. #{pn} - #{cn}"
153
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
154
- end
111
+ show(entries, index, offset, datalist) do |i, datalist|
112
+ pn = pretty(datalist[i]['playlists_name'], 0, 32);
113
+ cn = pretty(datalist[i]['creator_name'], 0, 28);
114
+ "#{i}. #{pn} - #{cn}"
155
115
  end
156
116
 
157
117
  when 'djchannels'
158
- (offset...[datalist.length, offset + step].min).each do |i|
159
- sn = pretty_format(datalist[i][0]['song_name'], 0, 32)
160
- if i == index
161
- info = "♩ #{i}. #{sn}"
162
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_POINTER_X, info, Curses.color_pair(2))
163
- else
164
- info = "#{i}. #{sn}"
165
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
166
- end
118
+ show(entries, index, offset, datalist) do |i, datalist|
119
+ sn = pretty(datalist[i][0]['song_name'], 0, 32)
120
+ "#{i}. #{sn}"
167
121
  end
168
122
 
169
123
  when 'help'
170
- (offset...[datalist.length, offset + step].min).each do |i|
124
+ entries.each do |i|
171
125
  info = "#{i}. #{datalist[i][0]} #{datalist[i][1]} #{datalist[i][2]}"
172
- putstr(screen, i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
126
+ screen.line(i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
173
127
  end
174
128
  end
175
129
  end
@@ -219,28 +173,31 @@ class Ui
219
173
  end
220
174
 
221
175
  end
176
+
177
+ # If no results, then just return empty array.
178
+ []
222
179
  end
223
180
 
224
181
  def build_favorite_menu
225
- clear_to_bottom(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
226
- putstr(screen, PLAYER_CONTENT_Y, PLAYER_X, '选择收藏条目类型:', Curses.color_pair(1))
227
- putstr(screen, PLAYER_CONTENT_Y + 1, PLAYER_X, '1 - 歌曲')
228
- putstr(screen, PLAYER_CONTENT_Y + 2, PLAYER_X, '2 - 精选歌单')
229
- putstr(screen, PLAYER_CONTENT_Y + 3, PLAYER_X, '3 - 专辑')
230
- putstr(screen, PLAYER_CONTENT_Y + 4, PLAYER_X, '4 - DJ 节目')
231
- putstr(screen, PLAYER_CONTENT_Y + 6, PLAYER_X, '请键入对应数字:', Curses.color_pair(2))
182
+ screen.clear(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
183
+ screen.line(PLAYER_CONTENT_Y, PLAYER_X, '选择收藏条目类型:', 1)
184
+ screen.line(PLAYER_CONTENT_Y + 1, PLAYER_X, '1 - 歌曲')
185
+ screen.line(PLAYER_CONTENT_Y + 2, PLAYER_X, '2 - 精选歌单')
186
+ screen.line(PLAYER_CONTENT_Y + 3, PLAYER_X, '3 - 专辑')
187
+ screen.line(PLAYER_CONTENT_Y + 4, PLAYER_X, '4 - DJ 节目')
188
+ screen.line(PLAYER_CONTENT_Y + 6, PLAYER_X, '请键入对应数字:', 2)
232
189
  screen.refresh
233
190
  screen.getch
234
191
  end
235
192
 
236
193
  def build_search_menu
237
- clear_to_bottom(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
238
- putstr(screen, PLAYER_CONTENT_Y, PLAYER_X, '选择搜索类型:', Curses.color_pair(1))
239
- putstr(screen, PLAYER_CONTENT_Y + 1, PLAYER_X, '1 - 歌曲')
240
- putstr(screen, PLAYER_CONTENT_Y + 2, PLAYER_X, '2 - 艺术家')
241
- putstr(screen, PLAYER_CONTENT_Y + 3, PLAYER_X, '3 - 专辑')
242
- putstr(screen, PLAYER_CONTENT_Y + 4, PLAYER_X, '4 - 精选歌单')
243
- putstr(screen, PLAYER_CONTENT_Y + 6, PLAYER_X, '请键入对应数字:', Curses.color_pair(2))
194
+ screen.clear(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
195
+ screen.line(PLAYER_CONTENT_Y, PLAYER_X, '选择搜索类型:', 1)
196
+ screen.line(PLAYER_CONTENT_Y + 1, PLAYER_X, '1 - 歌曲')
197
+ screen.line(PLAYER_CONTENT_Y + 2, PLAYER_X, '2 - 艺术家')
198
+ screen.line(PLAYER_CONTENT_Y + 3, PLAYER_X, '3 - 专辑')
199
+ screen.line(PLAYER_CONTENT_Y + 4, PLAYER_X, '4 - 精选歌单')
200
+ screen.line(PLAYER_CONTENT_Y + 6, PLAYER_X, '请键入对应数字:', 2)
244
201
  screen.refresh
245
202
  screen.getch
246
203
  end
@@ -260,18 +217,18 @@ class Ui
260
217
  end
261
218
 
262
219
  def build_login_error
263
- clear_to_bottom(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
264
- putstr(screen, PLAYER_CONTENT_Y + 1, PLAYER_X, 'oh,出现错误 Orz', Curses.color_pair(2))
265
- putstr(screen, PLAYER_CONTENT_Y + 2, PLAYER_X, '1 - 再试一次')
266
- putstr(screen, PLAYER_CONTENT_Y + 3, PLAYER_X, '2 - 稍后再试')
267
- putstr(screen, PLAYER_CONTENT_Y + 5, PLAYER_X, '请键入对应数字:', Curses.color_pair(2))
220
+ screen.clear(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
221
+ screen.line(PLAYER_CONTENT_Y + 1, PLAYER_X, 'oh,出现错误 Orz', 2)
222
+ screen.line(PLAYER_CONTENT_Y + 2, PLAYER_X, '1 - 再试一次')
223
+ screen.line(PLAYER_CONTENT_Y + 3, PLAYER_X, '2 - 稍后再试')
224
+ screen.line(PLAYER_CONTENT_Y + 5, PLAYER_X, '请键入对应数字:', 2)
268
225
  screen.refresh
269
226
  screen.getch
270
227
  end
271
228
 
272
229
  def get_param(prompt_str)
273
- clear_to_bottom(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
274
- putstr(screen, PLAYER_CONTENT_Y, PLAYER_X, prompt_str, Curses.color_pair(1))
230
+ screen.clear(screen, PLAYER_CONTENT_Y,SCREEN_HEIGHT)
231
+ screen.line(PLAYER_CONTENT_Y, PLAYER_X, prompt_str, 1)
275
232
  screen.setpos(PLAYER_CONTENT_Y + 2, PLAYER_X)
276
233
  params = screen.getstr
277
234
  if params.strip.nil?
@@ -283,25 +240,27 @@ class Ui
283
240
 
284
241
  private
285
242
 
286
- def putstr(screen, y, x, string, color = Curses.color_pair(0))
287
- screen.setpos(y, x)
288
- screen.clrtoeol
289
- screen.attrset(color)
290
- screen.addstr(string)
243
+ def pretty(info, start, length)
244
+ if info.size >= length
245
+ "#{info[start, length]}..."
246
+ else
247
+ info
248
+ end
291
249
  end
292
250
 
293
- def clear_to_bottom(screen, top, bottom)
294
- (top..bottom).each do |i|
295
- screen.setpos(i, 0)
296
- screen.clrtoeol
251
+ def highlight_or_not(i, index, offset, info)
252
+ if i == index
253
+ highlight = "♩ #{info}"
254
+ screen.line(i-offset+PLAYER_CONTENT_Y, PLAYER_POINTER_X, highlight, 2)
255
+ else
256
+ screen.line(i-offset+PLAYER_CONTENT_Y, PLAYER_X, info)
297
257
  end
298
258
  end
299
259
 
300
- def pretty_format(info, start, length)
301
- if info.size >= length
302
- "#{info[start, length]}..."
303
- else
304
- info
260
+ def show(entries, index, offset, datalist)
261
+ entries.each do |i|
262
+ info = yield(i, datalist) # Get custom info.
263
+ highlight_or_not(i, index, offset, info)
305
264
  end
306
265
  end
307
266
  end
@@ -1,3 +1,3 @@
1
1
  module Mdisc
2
- VERSION = "0.1.1"
2
+ VERSION = '0.1.2'
3
3
  end
@@ -1,23 +1,24 @@
1
1
  # coding: utf-8
2
+
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'mdisc/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "mdisc"
8
+ spec.name = 'mdisc'
8
9
  spec.version = Mdisc::VERSION
9
- spec.authors = ["Rick Yu"]
10
- spec.email = ["cosmtrek@gmail.com"]
10
+ spec.authors = ['Rick Yu']
11
+ spec.email = ['cosmtrek@gmail.com']
11
12
  spec.summary = %q{A local music player based on Netease music.}
12
13
  spec.description = %q{}
13
- spec.homepage = "https://github.com/cosmtrek/mdisc"
14
- spec.license = "MIT"
14
+ spec.homepage = 'https://github.com/cosmtrek/mdisc'
15
+ spec.license = 'MIT'
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0")
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
20
21
 
21
- spec.add_development_dependency "bundler", "~> 1.7"
22
- spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency 'bundler', '~> 1.7'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
23
24
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mdisc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Yu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-18 00:00:00.000000000 Z
11
+ date: 2014-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -56,6 +56,7 @@ files:
56
56
  - lib/mdisc/api.rb
57
57
  - lib/mdisc/menu.rb
58
58
  - lib/mdisc/player.rb
59
+ - lib/mdisc/screen.rb
59
60
  - lib/mdisc/ui.rb
60
61
  - lib/mdisc/version.rb
61
62
  - mdisc.gemspec