xiami_radio 1.1.0 → 1.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 +4 -4
- data/README.md +61 -0
- data/bin/xiami_radio +2 -2
- data/lib/xiami_radio/downloader.rb +20 -19
- data/lib/xiami_radio/player.rb +29 -19
- data/lib/xiami_radio/radio.rb +3 -3
- data/lib/xiami_radio/track.rb +1 -1
- data/lib/xiami_radio/user.rb +4 -6
- data/lib/xiami_radio/version.rb +1 -1
- data/lib/xiami_radio/view/player.rb +75 -73
- data/lib/xiami_radio.rb +18 -3
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82320db1fc6fa1004688356887db8a53f38683f6
|
4
|
+
data.tar.gz: a34ba7934dc3dd9a40da5bb4d0e5c6cf4808a1d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1a0d25276bc5dafd2ffffe19659aa72f342cfedab660373a222ea8132620a3ab7b703016f29c133d2d0e6f7ee763d093b87f6518e8af080167b56b3e8dbe3c1
|
7
|
+
data.tar.gz: add1a386b190a5bf46720ffe232e9dd727b3fb65bdd3e3e83ca0cacd6e6a272f87f7a402dad1ab47ede33917d83526d71b826b7187c6f010c93f04bc26db287b
|
data/README.md
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
Xiami_Radio
|
2
|
+
======================================
|
3
|
+
|
4
|
+
A Xiami radio player by command-line on ruby.
|
5
|
+
|
6
|
+
鉴于 V2ex 上有复数个豆瓣的各种版本各种功能的播放工具,我大虾米拿去一比真是冷清得不行。
|
7
|
+
|
8
|
+
( **Pia!(o ‵-′)ノಥ_ಥ ** 你丫忒夸张了吧,仔细看看还是有的 )
|
9
|
+
|
10
|
+
之前也在 V2 表示会折腾个 Ruby 版的虾米,于是就有了这个 ruby 的 **虾米电台** ,虽然好像已经过了很久了
|
11
|
+
|
12
|
+
## Features
|
13
|
+
|
14
|
+
* 支持用户登录
|
15
|
+
* 支持选择电台
|
16
|
+
* 支持 320k (无需会员)
|
17
|
+
* 支持收藏到虾米音乐库
|
18
|
+
* 播放记录上传虾米
|
19
|
+
* 附带自动签到脚本一份
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```
|
24
|
+
$ gem install xiami_radio
|
25
|
+
```
|
26
|
+
|
27
|
+
and then
|
28
|
+
|
29
|
+
```
|
30
|
+
$ xiaomi_radio
|
31
|
+
```
|
32
|
+
|
33
|
+
Enjoy yourself ~
|
34
|
+
|
35
|
+
## Requirements
|
36
|
+
|
37
|
+
* Portaudio >= 19
|
38
|
+
* Mpg123 >= 1.14
|
39
|
+
* Audite
|
40
|
+
|
41
|
+
## OSX Install
|
42
|
+
|
43
|
+
```
|
44
|
+
brew install portaudio
|
45
|
+
brew install mpg123
|
46
|
+
```
|
47
|
+
|
48
|
+
|
49
|
+
## Debian / Ubuntu Install
|
50
|
+
```
|
51
|
+
apt-get install libjack0 libjack-dev
|
52
|
+
apt-get install libportaudiocpp0 portaudio19-dev libmpg123-dev
|
53
|
+
```
|
54
|
+
|
55
|
+
## Audite
|
56
|
+
|
57
|
+
使用了 [Audite][1] 作为播放组件
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
[1]: https://github.com/georgi/audite
|
data/bin/xiami_radio
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
require 'xiami_radio'
|
5
5
|
require 'io/console'
|
6
6
|
|
7
|
-
user = XiamiRadio::User.
|
7
|
+
user = XiamiRadio::User.instance
|
8
8
|
|
9
9
|
email = unless user.login?
|
10
10
|
puts 'Plz keep empty if you don\'t want to login'
|
@@ -35,4 +35,4 @@ radio_option = case gets.chomp[0]
|
|
35
35
|
{type: (type || 8).to_i, oid: oid.to_i}
|
36
36
|
end
|
37
37
|
|
38
|
-
XiamiRadio::Player.play
|
38
|
+
XiamiRadio::Player.play XiamiRadio::Radio.new(radio_option)
|
@@ -10,11 +10,10 @@ module XiamiRadio
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
attr_reader :track, :
|
13
|
+
attr_reader :track, :file
|
14
14
|
|
15
15
|
def initialize(track)
|
16
16
|
@track = track
|
17
|
-
@uri = URI @track.location
|
18
17
|
end
|
19
18
|
|
20
19
|
def filename
|
@@ -26,30 +25,32 @@ module XiamiRadio
|
|
26
25
|
end
|
27
26
|
|
28
27
|
def start
|
29
|
-
@thread = Thread.start
|
30
|
-
Net::HTTP.get_response @uri do |res|
|
31
|
-
if res.code == '302'
|
32
|
-
@uri = URI res.header['Location']
|
33
|
-
start
|
34
|
-
else
|
35
|
-
@progress, @total = 0, res.header['Content-Length'].to_i
|
36
|
-
@file = File.open(filename, 'w')
|
37
|
-
res.read_body do |chunk|
|
38
|
-
@file << chunk
|
39
|
-
@progress += chunk.size
|
40
|
-
@file.close unless @progress < @total
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
28
|
+
@thread = Thread.start { request URI(@track.location) }
|
45
29
|
sleep 0.1 until @progress.to_i > 0
|
46
30
|
end
|
47
31
|
|
48
32
|
def stop
|
49
33
|
@thread&.exit
|
50
34
|
@thread = nil
|
51
|
-
File.delete(@file)
|
35
|
+
File.delete(@file) if @file
|
52
36
|
end
|
53
37
|
|
38
|
+
private
|
39
|
+
|
40
|
+
def request(uri)
|
41
|
+
Net::HTTP.get_response uri do |res|
|
42
|
+
if res.code == '302'
|
43
|
+
request URI(res.header['Location'])
|
44
|
+
else
|
45
|
+
@progress, @total = 0, res.header['Content-Length'].to_i
|
46
|
+
@file = File.open(filename, 'w')
|
47
|
+
res.read_body do |chunk|
|
48
|
+
@file << chunk
|
49
|
+
@progress += chunk.size
|
50
|
+
@file.close unless @progress < @total
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
54
55
|
end
|
55
56
|
end
|
data/lib/xiami_radio/player.rb
CHANGED
@@ -4,16 +4,16 @@ module XiamiRadio
|
|
4
4
|
# There is a player as you saw
|
5
5
|
class Player
|
6
6
|
|
7
|
-
attr_reader :track, :next_track, :radio
|
7
|
+
attr_reader :track, :next_track, :radio
|
8
8
|
|
9
9
|
def initialize(radio:, _playlist: nil)
|
10
|
-
@radio
|
10
|
+
@radio = radio
|
11
11
|
@player = Audite.new
|
12
|
-
@view = View::Player.new
|
12
|
+
@view = View::Player.new
|
13
13
|
|
14
14
|
@player.events.on :position_change, &method(:position_change)
|
15
15
|
@player.events.on :complete, &method(:complete)
|
16
|
-
@view.listen_on
|
16
|
+
@view.listen_on self
|
17
17
|
end
|
18
18
|
|
19
19
|
def play
|
@@ -25,11 +25,9 @@ module XiamiRadio
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def next
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
@track, @next_track = @next_track, nil
|
28
|
+
@track.downloader.stop
|
29
|
+
preload! unless preload?
|
30
|
+
unperload!
|
33
31
|
@player.request_next_song
|
34
32
|
end
|
35
33
|
|
@@ -48,19 +46,31 @@ module XiamiRadio
|
|
48
46
|
private
|
49
47
|
|
50
48
|
def position_change(position)
|
51
|
-
@view.refresh position
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@player.queue @next_track.file_path
|
58
|
-
end
|
59
|
-
end
|
49
|
+
@view.refresh @track, position unless position > @track.duration
|
50
|
+
|
51
|
+
Thread.start do
|
52
|
+
preload!
|
53
|
+
@track.record
|
54
|
+
end if !preload? && position / @track.duration > 0.7
|
60
55
|
end
|
61
56
|
|
62
57
|
def complete
|
63
|
-
@track, @
|
58
|
+
@track, @preload_track = @preload_track, nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def preload?
|
62
|
+
@preload_flag ||= false
|
63
|
+
end
|
64
|
+
|
65
|
+
def preload!
|
66
|
+
@preload_flag = true
|
67
|
+
@preload_track = @radio.next_track
|
68
|
+
@player.queue @preload_track.file_path
|
69
|
+
end
|
70
|
+
|
71
|
+
def unperload!
|
72
|
+
@preload_flag = false
|
73
|
+
@track, @preload_track = @preload_track, nil
|
64
74
|
end
|
65
75
|
|
66
76
|
def self.play(radio)
|
data/lib/xiami_radio/radio.rb
CHANGED
@@ -6,12 +6,12 @@ module XiamiRadio
|
|
6
6
|
|
7
7
|
XIAMI_CAI = {type: 8, oid: 0}.freeze
|
8
8
|
|
9
|
-
attr_reader :type, :oid, :
|
9
|
+
attr_reader :type, :oid, :nori, :play_queue, :client
|
10
10
|
|
11
|
-
def initialize(type:, oid
|
11
|
+
def initialize(type:, oid:)
|
12
12
|
@type = type
|
13
13
|
@oid = oid
|
14
|
-
@client = user
|
14
|
+
@client = Client.new user: User.instance
|
15
15
|
@nori = Nori.new(:convert_tags_to => -> (tag) { tag.snakecase.to_sym })
|
16
16
|
@play_queue = []
|
17
17
|
end
|
data/lib/xiami_radio/track.rb
CHANGED
data/lib/xiami_radio/user.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'http-cookie'
|
2
|
+
require 'singleton'
|
2
3
|
|
3
4
|
module XiamiRadio
|
4
5
|
# There is a user as you saw
|
5
6
|
class User
|
7
|
+
include Singleton
|
6
8
|
attr_reader :nick_name, :level, :user_id, :sign, :is_vip
|
7
9
|
attr_accessor :cookie_jar
|
8
10
|
|
@@ -12,10 +14,6 @@ module XiamiRadio
|
|
12
14
|
get_user_info
|
13
15
|
end
|
14
16
|
|
15
|
-
def radio(**args)
|
16
|
-
XiamiRadio::Radio.new user: self, **args
|
17
|
-
end
|
18
|
-
|
19
17
|
def login?
|
20
18
|
!user_id.to_s.empty?
|
21
19
|
end
|
@@ -52,12 +50,12 @@ module XiamiRadio
|
|
52
50
|
cookie_jar.cookies.select { |c| c.name == '_xiamitoken' }.first&.value
|
53
51
|
end
|
54
52
|
|
53
|
+
private
|
54
|
+
|
55
55
|
def client
|
56
56
|
@client ||= Client.new user: self
|
57
57
|
end
|
58
58
|
|
59
|
-
private
|
60
|
-
|
61
59
|
def login_client
|
62
60
|
@l_client ||= Client.new user: self, host: Client::LOGIN_HOST
|
63
61
|
end
|
data/lib/xiami_radio/version.rb
CHANGED
@@ -1,90 +1,92 @@
|
|
1
1
|
require 'curses'
|
2
2
|
|
3
|
-
module XiamiRadio
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
3
|
+
module XiamiRadio
|
4
|
+
module View
|
5
|
+
class Player
|
6
|
+
include Curses
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
init_screen
|
10
|
+
noecho
|
11
|
+
stdscr.keypad true
|
12
|
+
curs_set 0
|
13
|
+
start_color
|
14
|
+
init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK)
|
15
|
+
init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK)
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
18
|
+
alias_method :curses_refresh, :refresh
|
19
|
+
def refresh(track, position)
|
20
|
+
render_title_line track
|
21
|
+
render_progress_line (position / track.duration), track.downloader.progress
|
22
|
+
render_info_line track, position
|
23
|
+
render_msg_line
|
24
|
+
Curses.refresh
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
27
|
+
def listen_on(player)
|
28
|
+
Thread.start do
|
29
|
+
while (key = getch)
|
30
|
+
case key
|
31
|
+
when KEY_LEFT
|
32
|
+
player.rewind
|
33
|
+
when KEY_RIGHT
|
34
|
+
player.forward
|
35
|
+
when KEY_DOWN
|
36
|
+
player.next
|
37
|
+
when 'l'
|
38
|
+
XiamiRadio::Notice.push player.track.fav
|
39
|
+
when ' '
|
40
|
+
player.toggle
|
41
|
+
else #
|
42
|
+
end
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
46
|
-
end
|
47
46
|
|
48
|
-
|
47
|
+
private
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
49
|
+
def render_title_line(track)
|
50
|
+
setpos(0, 0)
|
51
|
+
clrtoeol
|
52
|
+
addstr('Now is playing ')
|
53
|
+
attron(color_pair(COLOR_RED)|A_NORMAL) {
|
54
|
+
addstr(" #{track.title} - #{track.artist} ")
|
55
|
+
}
|
56
|
+
if User.instance.login?
|
57
|
+
track.grade? ? addstr(' 已收藏 ') : addstr(' 按"L"加入收藏 ')
|
58
|
+
end
|
58
59
|
end
|
59
|
-
end
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
def render_progress_line(play_rate, dawnload_rate)
|
62
|
+
p = (play_rate * cols).round
|
63
|
+
d = (dawnload_rate * cols).round - p
|
64
|
+
setpos(1, 0)
|
65
|
+
clrtoeol
|
66
|
+
addstr('_' * p + '#' * d + ' ' * (cols - p - d))
|
67
|
+
end
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
69
|
+
def render_info_line(track, position)
|
70
|
+
setpos(2, 0)
|
71
|
+
clrtoeol
|
72
|
+
addstr("#{track.reason.content}")
|
73
|
+
addstr(": #{track.reason.title} ") unless track.reason.title.nil?
|
74
|
+
addstr("- #{track.reason.artist} ") unless track.reason.artist.nil?
|
75
|
+
addstr(" 播放进度: ")
|
76
|
+
attron(color_pair(COLOR_CYAN)|A_NORMAL) {
|
77
|
+
addstr(" #{sec_2_min position} / #{sec_2_min track.duration} ")
|
78
|
+
}
|
79
|
+
end
|
78
80
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
def render_msg_line(msg = XiamiRadio::Notice.shift)
|
82
|
+
setpos(3, 0)
|
83
|
+
clrtoeol
|
84
|
+
addstr(msg || '')
|
85
|
+
end
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
+
def sec_2_min(sec)
|
88
|
+
Time.at(sec).utc.strftime('%M:%S')
|
89
|
+
end
|
87
90
|
end
|
88
|
-
|
89
91
|
end
|
90
92
|
end
|
data/lib/xiami_radio.rb
CHANGED
@@ -11,10 +11,25 @@ require 'tmpdir'
|
|
11
11
|
|
12
12
|
module XiamiRadio
|
13
13
|
TMP_DIR = File.join(Dir.tmpdir, 'xiami_radio').freeze
|
14
|
+
DEBUG = false
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
Thread.abort_on_exception = true
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def init
|
20
|
+
mktmpdir
|
21
|
+
stderr = debug? ? STDOUT : File.join(TMP_DIR, '戊')
|
22
|
+
$stderr.reopen stderr, 'w'
|
23
|
+
end
|
24
|
+
|
25
|
+
def mktmpdir
|
26
|
+
Dir.mkdir TMP_DIR, 0700 unless Dir.exist? TMP_DIR
|
27
|
+
end
|
28
|
+
|
29
|
+
def debug?
|
30
|
+
%w(1 true on).include? ENV.fetch('DEBUG', DEBUG)
|
31
|
+
end
|
17
32
|
end
|
18
33
|
end
|
19
34
|
|
20
|
-
XiamiRadio.
|
35
|
+
XiamiRadio.init
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xiami_radio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Chou
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-10-
|
11
|
+
date: 2017-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: audite
|
@@ -80,7 +80,8 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '9.0'
|
83
|
-
description:
|
83
|
+
description: A Xiami radio player by command-line on ruby, help you listening to the
|
84
|
+
Xiami via a geek way.
|
84
85
|
email:
|
85
86
|
- me@ichou.cn
|
86
87
|
executables:
|
@@ -124,5 +125,5 @@ rubyforge_project:
|
|
124
125
|
rubygems_version: 2.6.11
|
125
126
|
signing_key:
|
126
127
|
specification_version: 4
|
127
|
-
summary: Xiami radio player
|
128
|
+
summary: a Xiami radio player by command-line on ruby
|
128
129
|
test_files: []
|