piliv2 2.0.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.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +224 -0
- data/Rakefile +3 -0
- data/example/example.rb +111 -0
- data/lib/pili/auth.rb +48 -0
- data/lib/pili/client.rb +48 -0
- data/lib/pili/config.rb +27 -0
- data/lib/pili/exception.rb +41 -0
- data/lib/pili/hub.rb +93 -0
- data/lib/pili/rpc.rb +60 -0
- data/lib/pili/stream.rb +115 -0
- data/lib/pili/version.rb +3 -0
- data/lib/pili.rb +11 -0
- data/pili.gemspec +23 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 698dc78d73f17a61df99562bafa0147e5b0a03cc
|
4
|
+
data.tar.gz: 805206f6c3a86feeaf724fae2b3ef338a831dba8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 71ff1db811bc913d6982f2a75785342747db9737c9a0e53961d48dfc774b908dc47320822689e8f12f5ff1d81ff1235449b3834aa5bd820dab6f0b25b36772a3
|
7
|
+
data.tar.gz: 1517952e3cfd76a6e3eb9500fe02a6e974dea49d9661c31f1675188c0c3be476e3d995ffb95cfc7ab04bb49ce76a7492f0232a79d5e5e2dc3ae01ab019c69648
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2.5
|
4
|
+
gemfile:
|
5
|
+
- Gemfile
|
6
|
+
deploy:
|
7
|
+
provider: rubygems
|
8
|
+
api_key:
|
9
|
+
secure: frhK5TRyrplKjwEktkjx4nGyXBhELae7B/rfDTWUqLrwX4Gmx2XTf0j3VSwKphf2PclW2Phs1o029JFjivgjCB0gYpb4/lgPLxXAjYoUv7cbyZ1SdvIkhJsxilPgkPOr/t6XZ4OB1WU0yLJxDKNyJCh6fHJ47OlMfy7neGPtFKI=
|
10
|
+
gemspec: pili.gemspec
|
11
|
+
on:
|
12
|
+
tags: true
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 pili.qiniu.com
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
# Pili Streaming Cloud server-side library for Ruby
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
- URL
|
6
|
+
- [x] RTMP推流地址: rtmp_publish_url(domain, hub, stream_key, mac, expire_after_seconds)
|
7
|
+
- [x] RTMP直播地址: rtmp_play_url(domain, hub, stream_key)
|
8
|
+
- [x] HLS直播地址: hls_play_url(domain, hub, stream_key)
|
9
|
+
- [x] HDL直播地址: hdl_play_url(domain, hub, stream_key)
|
10
|
+
- [x] 截图直播地址: snapshot_play_url(domain, hub, stream_key)
|
11
|
+
- Hub
|
12
|
+
- [x] 创建流: hub.create(stream_key)
|
13
|
+
- [x] 获得流: hub.stream(stream_key)
|
14
|
+
- [x] 列出流: hub.list(...)
|
15
|
+
- [x] 列出正在直播的流: hub.list_live(...)
|
16
|
+
- Stream
|
17
|
+
- [x] 流信息: stream.info()
|
18
|
+
- [x] 禁用流: stream.disable()
|
19
|
+
- [x] 启用流: stream.enable()
|
20
|
+
- [x] 查询直播状态: stream.live_status()
|
21
|
+
- [x] 保存直播回放: stream.save(...)
|
22
|
+
- [x] 查询直播历史: stream.history_activity(...)
|
23
|
+
|
24
|
+
## Contents
|
25
|
+
|
26
|
+
- [Installation](#installation)
|
27
|
+
- [Usage](#usage)
|
28
|
+
- [Configuration](#configuration)
|
29
|
+
- [URL](#url)
|
30
|
+
- [Generate RTMP publish URL](#generate-rtmp-publish-url)
|
31
|
+
- [Generate RTMP play URL](#generate-rtmp-play-url)
|
32
|
+
- [Generate HLS play URL](#generate-hls-play-url)
|
33
|
+
- [Generate HDL play URL](#generate-hdl-play-url)
|
34
|
+
- [Generate Snapshot play URL](#generate-snapshot-play-url)
|
35
|
+
- [Hub](#hub)
|
36
|
+
- [Instantiate a Pili Hub object](#instantiate-a-pili-hub-object)
|
37
|
+
- [Create a new Stream](#create-a-new-stream)
|
38
|
+
- [Get a Stream](#get-a-stream)
|
39
|
+
- [List Streams](#list-streams)
|
40
|
+
- [List live Streams](#list-live-streams)
|
41
|
+
- [Stream](#stream)
|
42
|
+
- [Get Stream info](#get-stream-info)
|
43
|
+
- [Disable a Stream](#disable-a-stream)
|
44
|
+
- [Enable a Stream](#enable-a-stream)
|
45
|
+
- [Get Stream live status](#get-stream-live-status)
|
46
|
+
- [Get Stream history activity](#get-stream-history-activity)
|
47
|
+
- [Save Stream live playback](#save-stream-live-playback)
|
48
|
+
|
49
|
+
## Installation
|
50
|
+
|
51
|
+
Add this line to your application's Gemfile:
|
52
|
+
|
53
|
+
gem 'piliv2'
|
54
|
+
|
55
|
+
And then execute:
|
56
|
+
|
57
|
+
$ bundle
|
58
|
+
|
59
|
+
Or install it yourself as:
|
60
|
+
|
61
|
+
$ gem install piliv2
|
62
|
+
|
63
|
+
|
64
|
+
## Usage
|
65
|
+
|
66
|
+
### Configuration
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
require "piliv2"
|
70
|
+
|
71
|
+
access_key = "<QINIU ACCESS KEY>" # 替换成自己 Qiniu 账号的 AccessKey.
|
72
|
+
secret_key = "<QINIU SECRET KEY>" # 替换成自己 Qiniu 账号的 SecretKey.
|
73
|
+
hub_name = "<PILI HUB NAME>" # Hub 必须事先存在.
|
74
|
+
|
75
|
+
mac = Pili::Mac.new(access_key, secret_key)
|
76
|
+
client = Pili::Client.new(mac)
|
77
|
+
hub = client.hub(hub_name)
|
78
|
+
```
|
79
|
+
|
80
|
+
### URL
|
81
|
+
|
82
|
+
#### Generate RTMP publish URL
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
url = Pili.rtmp_publish_url("publish-rtmp.test.com", "PiliSDKTest", "streamkey", 60)
|
86
|
+
puts(url)
|
87
|
+
# rtmp://publish-rtmp.test.com/PiliSDKTest/streamkey?e=1463023142&token=7O7hf7Ld1RrC_fpZdFvU8aCgOPuhw2K4eapYOdII:-5IVlpFNNGJHwv-2qKwVIakC0ME=
|
88
|
+
```
|
89
|
+
|
90
|
+
#### Generate RTMP play URL
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
url = Pili.rtmp_play_url("live-rtmp.test.com", "PiliSDKTest", "streamkey")
|
94
|
+
puts(url)
|
95
|
+
# rtmp://live-rtmp.test.com/PiliSDKTest/streamkey
|
96
|
+
```
|
97
|
+
|
98
|
+
#### Generate HLS play URL
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
url = Pili.hls_play_url("live-hls.test.com", "PiliSDKTest", "streamkey")
|
102
|
+
puts(url)
|
103
|
+
# http://live-hls.test.com/PiliSDKTest/streamkey.m3u8
|
104
|
+
```
|
105
|
+
|
106
|
+
#### Generate HDL play URL
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
url = Pili.hdl_play_url("live-hdl.test.com", "PiliSDKTest", "streamkey")
|
110
|
+
puts(url)
|
111
|
+
# http://live-hdl.test.com/PiliSDKTest/streamkey.flv
|
112
|
+
```
|
113
|
+
|
114
|
+
#### Generate Snapshot play URL
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
url = Pili.snapshot_play_url("live-snapshot.test.com", "PiliSDKTest", "streamkey")
|
118
|
+
puts(url)
|
119
|
+
# http://live-snapshot.test.com/PiliSDKTest/streamkey.jpg
|
120
|
+
```
|
121
|
+
|
122
|
+
### Hub
|
123
|
+
|
124
|
+
#### Instantiate a Pili Hub object
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
mac = Pili::Mac.new(access_key, secret_key)
|
128
|
+
client = Pili::Client.new(mac)
|
129
|
+
hub = client.hub("PiliSDKTest")
|
130
|
+
```
|
131
|
+
|
132
|
+
#### Create a new Stream
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
stream = hub.create("streamkey")
|
136
|
+
puts(stream.info.to_json)
|
137
|
+
# {"hub":"PiliSDKTest","key":"streamkey","disabled":false}
|
138
|
+
```
|
139
|
+
|
140
|
+
#### Get a Stream
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
stream = hub.stream("streamkey")
|
144
|
+
puts(stream.info.to_json)
|
145
|
+
# {"hub":"PiliSDKTest","key":"streamkey","disabled":false}
|
146
|
+
```
|
147
|
+
|
148
|
+
#### List Streams
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
keys, marker = hub.list(:prefix=>"str", :limit=>10)
|
152
|
+
puts keys.to_s, marker
|
153
|
+
# [<keys...>], <marker>
|
154
|
+
```
|
155
|
+
|
156
|
+
#### List live Streams
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
keys, marker = hub.list_live(:prefix=>"str", :limit=>10)
|
160
|
+
puts keys.to_s, marker
|
161
|
+
# [<keys...>], <marker>
|
162
|
+
```
|
163
|
+
|
164
|
+
### Stream
|
165
|
+
|
166
|
+
#### Get Stream info
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
stream = hub.stream(key)
|
170
|
+
puts(stream.info.to_json)
|
171
|
+
# {"hub": "PiliSDKTest", "key": "streamkey", "disabled": false}
|
172
|
+
```
|
173
|
+
|
174
|
+
#### Disable a Stream
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
stream = hub.stream("streamkey")
|
178
|
+
puts("before disable: #{stream.info.to_json}")
|
179
|
+
|
180
|
+
stream.disable()
|
181
|
+
|
182
|
+
stream = hub.stream("streamkey")
|
183
|
+
puts("after disable: #{stream.info.to_json}")
|
184
|
+
# before disable: {"hub":"PiliSDKTest","key":"streamkey","disabled":false}
|
185
|
+
# after disable: {"hub":"PiliSDKTest","key":"streamkey","disabled":true}
|
186
|
+
```
|
187
|
+
|
188
|
+
#### Enable a Stream
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
stream = hub.stream("streamkey")
|
192
|
+
puts("before enable: #{stream.info.to_json}")
|
193
|
+
|
194
|
+
stream.enable()
|
195
|
+
|
196
|
+
stream = hub.get("streamkey")
|
197
|
+
puts("after enable: #{stream.info.to_json}")
|
198
|
+
# before disable: {"hub":"PiliSDKTest","key":"streamkey","disabled":true}
|
199
|
+
# after disable: {"hub":"PiliSDKTest","key":"streamkey","disabled":false}
|
200
|
+
```
|
201
|
+
|
202
|
+
#### Get Stream live status
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
status = stream.live_status()
|
206
|
+
puts(status.to_json)
|
207
|
+
# {"startAt":1463022236,"clientIP":"222.73.202.226","bps":248,"fps":{"audio":45,"vedio":28,"data":0}}
|
208
|
+
```
|
209
|
+
|
210
|
+
#### Get Stream history activity
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
activity = stream.history_activity()
|
214
|
+
puts(activity.to_json)
|
215
|
+
# [{"start":1463022236,"end":1463022518}]
|
216
|
+
```
|
217
|
+
|
218
|
+
#### Save Stream live playback
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
fname = stream.save()
|
222
|
+
puts(fname)
|
223
|
+
# recordings/z1.PiliSDKTest.streamkey/1463156847_1463157463.m3u8
|
224
|
+
```
|
data/Rakefile
ADDED
data/example/example.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'pili'
|
2
|
+
|
3
|
+
access_key = ENV["PILI_ACCESS_KEY"]
|
4
|
+
secret_key = ENV["PILI_SECRET_KEY"]
|
5
|
+
|
6
|
+
hub_name = ENV["PILI_HUB_NAME"]
|
7
|
+
|
8
|
+
if access_key == nil || secret_key == nil
|
9
|
+
raise 'access_key = #{access_key} secret_key = #{secret_key}'
|
10
|
+
end
|
11
|
+
|
12
|
+
srand
|
13
|
+
stream_key_prefix = "ruby-sdkexample-#{rand 1000000000}"
|
14
|
+
|
15
|
+
puts "初始化 client."
|
16
|
+
mac = Pili::Mac.new access_key, secret_key
|
17
|
+
client = Pili::Client.new mac
|
18
|
+
hub = client.hub hub_name
|
19
|
+
|
20
|
+
puts "获得不存在的流."
|
21
|
+
key_a = stream_key_prefix + "-a"
|
22
|
+
begin
|
23
|
+
stream_a = hub.stream key_a
|
24
|
+
stream_a.info
|
25
|
+
rescue Pili::ResourceNotExist => e
|
26
|
+
puts e
|
27
|
+
end
|
28
|
+
|
29
|
+
puts "创建流."
|
30
|
+
stream_a = hub.create(key_a)
|
31
|
+
puts stream_a.info
|
32
|
+
|
33
|
+
puts "获得流."
|
34
|
+
stream_a = hub.stream(key_a)
|
35
|
+
puts "to_json: #{stream_a.info.to_json}"
|
36
|
+
|
37
|
+
puts "创建重复的流."
|
38
|
+
begin
|
39
|
+
hub.create(key_a)
|
40
|
+
rescue Pili::ResourceConflict => e
|
41
|
+
puts e
|
42
|
+
end
|
43
|
+
|
44
|
+
puts "创建另一路流."
|
45
|
+
key_b = stream_key_prefix + "-b"
|
46
|
+
stream_b = hub.create(key_b)
|
47
|
+
puts stream_b.info
|
48
|
+
|
49
|
+
puts "列出所有流."
|
50
|
+
begin
|
51
|
+
keys, marker = hub.list(:prefix=>stream_key_prefix)
|
52
|
+
puts keys.to_s, marker
|
53
|
+
rescue Pili::ResourceNotExist => e
|
54
|
+
puts e
|
55
|
+
end
|
56
|
+
|
57
|
+
puts "列出正在直播的流."
|
58
|
+
begin
|
59
|
+
keys, marker = hub.list_live(:prefix=>stream_key_prefix)
|
60
|
+
puts keys.to_s, marker
|
61
|
+
rescue Pili::ResourceNotExist => e
|
62
|
+
puts e
|
63
|
+
end
|
64
|
+
|
65
|
+
puts "禁用流."
|
66
|
+
stream_a.disable()
|
67
|
+
puts stream_a.info
|
68
|
+
|
69
|
+
puts "启用流."
|
70
|
+
stream_a.enable()
|
71
|
+
puts stream_a.info
|
72
|
+
|
73
|
+
puts "查询直播状态."
|
74
|
+
begin
|
75
|
+
status = stream_a.live_status()
|
76
|
+
puts keys, marker
|
77
|
+
rescue Pili::ResourceNotExist => e
|
78
|
+
puts e
|
79
|
+
end
|
80
|
+
|
81
|
+
puts "查询推流历史."
|
82
|
+
activity = stream_a.history_activity()
|
83
|
+
puts activity
|
84
|
+
|
85
|
+
puts "保存直播数据."
|
86
|
+
begin
|
87
|
+
fname = stream_a.save()
|
88
|
+
puts fname
|
89
|
+
rescue Exception => e
|
90
|
+
puts e
|
91
|
+
end
|
92
|
+
|
93
|
+
puts "RTMP 推流地址."
|
94
|
+
url = Pili.rtmp_publish_url("publish-rtmp.test.com", hub_name, key_a, mac, 3600)
|
95
|
+
puts url
|
96
|
+
|
97
|
+
puts "RTMP 直播放址."
|
98
|
+
url = Pili.rtmp_play_url("live-rtmp.test.com", hub_name, key_a)
|
99
|
+
puts url
|
100
|
+
|
101
|
+
puts "HLS 直播地址."
|
102
|
+
url = Pili.hls_play_url("live-hls.test.com", hub_name, key_a)
|
103
|
+
puts url
|
104
|
+
|
105
|
+
puts "HDL 直播地址."
|
106
|
+
url = Pili.hdl_play_url("live-hdl.test.com", hub_name, key_a)
|
107
|
+
puts url
|
108
|
+
|
109
|
+
puts "截图直播地址"
|
110
|
+
url = Pili.snapshot_play_url("live-snapshot.test.com", hub_name, key_a)
|
111
|
+
puts url
|
data/lib/pili/auth.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'digest/hmac'
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Pili
|
6
|
+
class Mac
|
7
|
+
|
8
|
+
attr_reader :access_key, :secret_key
|
9
|
+
|
10
|
+
def initialize(access_key, secret_key)
|
11
|
+
@access_key = access_key
|
12
|
+
@secret_key = secret_key
|
13
|
+
end
|
14
|
+
|
15
|
+
def sign(data)
|
16
|
+
signature = Base64.urlsafe_encode64(Digest::HMAC.digest(data, @secret_key, Digest::SHA1))
|
17
|
+
"#{@access_key}:#{signature}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def inc_body(req, content_type)
|
21
|
+
type_ok = content_type != nil && content_type != "application/octet-stream"
|
22
|
+
length_ok = req.content_length != nil && req.content_length > 0 && req.content_length < 1024*1024
|
23
|
+
req.body != nil && type_ok && length_ok
|
24
|
+
end
|
25
|
+
|
26
|
+
def sign_request(req)
|
27
|
+
data = "#{req.method} #{req.path}"
|
28
|
+
|
29
|
+
# FIXME: how to deal with port, because default uri.port is 80
|
30
|
+
data += "\nHost: #{req.uri.host}"
|
31
|
+
|
32
|
+
content_type = req["Content-Type"]
|
33
|
+
if content_type != nil
|
34
|
+
data += "\nContent-Type: #{content_type}"
|
35
|
+
end
|
36
|
+
|
37
|
+
data += "\n\n"
|
38
|
+
|
39
|
+
if inc_body(req, content_type)
|
40
|
+
data += "#{req.body}"
|
41
|
+
end
|
42
|
+
|
43
|
+
sign(data)
|
44
|
+
end
|
45
|
+
|
46
|
+
private :inc_body
|
47
|
+
end
|
48
|
+
end
|
data/lib/pili/client.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Pili
|
4
|
+
class Client
|
5
|
+
attr_reader :mac, :rpc
|
6
|
+
|
7
|
+
def initialize(mac)
|
8
|
+
@mac = mac
|
9
|
+
@rpc = RPC.new mac
|
10
|
+
end
|
11
|
+
|
12
|
+
# hub 初始化一个 Hub.
|
13
|
+
def hub(hub_name)
|
14
|
+
Hub.new hub_name, self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# 生成 RTMP 推流地址.
|
20
|
+
# +expire_after_seconds+ 表示 URL 在多久之后失效.
|
21
|
+
def rtmp_publish_url(domain, hub, stream_key, mac, expire_after_seconds)
|
22
|
+
expire = Time.new.to_i + expire_after_seconds
|
23
|
+
path = "/#{hub}/#{stream_key}?e=#{expire}"
|
24
|
+
token = mac.sign(path)
|
25
|
+
"rtmp://#{domain}#{path}&token=#{token}"
|
26
|
+
end
|
27
|
+
|
28
|
+
# 生成 RTMP 直播地址.
|
29
|
+
def rtmp_play_url(domain, hub, stream_key)
|
30
|
+
"rtmp://#{domain}/#{hub}/#{stream_key}"
|
31
|
+
end
|
32
|
+
|
33
|
+
# 生成 HLS 直播地址.
|
34
|
+
def hls_play_url(domain, hub, stream_key)
|
35
|
+
"http://#{domain}/#{hub}/#{stream_key}.m3u8"
|
36
|
+
end
|
37
|
+
|
38
|
+
# 生成 HDL 直播地址.
|
39
|
+
def hdl_play_url(domain, hub, stream_key)
|
40
|
+
"http://#{domain}/#{hub}/#{stream_key}.flv"
|
41
|
+
end
|
42
|
+
|
43
|
+
# 生成截图直播地址.
|
44
|
+
def snapshot_play_url(domain, hub, stream_key)
|
45
|
+
"http://#{domain}/#{hub}/#{stream_key}.jpg"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/pili/config.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Pili
|
3
|
+
module Config
|
4
|
+
class << self
|
5
|
+
|
6
|
+
@@settings = {
|
7
|
+
:api_scheme => "http",
|
8
|
+
:api_host => "pili.qiniuapi.com",
|
9
|
+
:api_version => "v2",
|
10
|
+
:api_user_agent => "pili-sdk-ruby/v2-#{Pili::VERSION} Ruby/#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
|
11
|
+
}
|
12
|
+
|
13
|
+
def init(options = {})
|
14
|
+
@@settings.merge!(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def api_base_url
|
18
|
+
"#{@@settings[:api_scheme]}://#{@@settings[:api_host]}/#{@@settings[:api_version]}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def user_agent
|
22
|
+
@@settings[:api_user_agent]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Pili
|
6
|
+
|
7
|
+
class RPCError < Exception
|
8
|
+
|
9
|
+
attr_reader :resp
|
10
|
+
|
11
|
+
def initialize(message, resp)
|
12
|
+
@resp = resp
|
13
|
+
super(message)
|
14
|
+
end
|
15
|
+
|
16
|
+
def code
|
17
|
+
resp.code
|
18
|
+
end
|
19
|
+
|
20
|
+
def reqid
|
21
|
+
resp["X-Reqid"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def message
|
25
|
+
to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"#{super.to_s}: #{@resp.body}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# 资源已存在冲突错误
|
34
|
+
class ResourceConflict < RPCError
|
35
|
+
end
|
36
|
+
|
37
|
+
# 资源不存在错误
|
38
|
+
class ResourceNotExist < RPCError
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/lib/pili/hub.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Pili
|
3
|
+
# Hub 表示一个 Hub, 一个 Hub 包含多个 Stream
|
4
|
+
class Hub
|
5
|
+
|
6
|
+
attr_reader :hub
|
7
|
+
|
8
|
+
def initialize(hub, client)
|
9
|
+
@hub = hub
|
10
|
+
@client = client
|
11
|
+
@base_url = "#{Config.api_base_url}/hubs/#{hub}"
|
12
|
+
end
|
13
|
+
|
14
|
+
# 创建一个流对象.
|
15
|
+
# 使用一个合法 rtmp_publish_url 发起推流就会自动创建流对象.
|
16
|
+
# 一般情况下不需要调用这个 API, 除非是想提前对这一个流做一些特殊配置.
|
17
|
+
def create(stream_key)
|
18
|
+
@client.rpc.call_with_json("POST", "#{@base_url}/streams", {:key => stream_key})
|
19
|
+
Stream.new(@hub, stream_key, @client)
|
20
|
+
end
|
21
|
+
|
22
|
+
# 初始化一个流对象.
|
23
|
+
def stream(stream_key)
|
24
|
+
Stream.new(@hub, stream_key, @client)
|
25
|
+
end
|
26
|
+
|
27
|
+
def plist(opt = {})
|
28
|
+
opt[:liveonly] ||= false
|
29
|
+
opt[:prefix] ||= ""
|
30
|
+
opt[:limit] ||= 0
|
31
|
+
opt[:marker] ||= ""
|
32
|
+
|
33
|
+
ret = @client.rpc.call_with_json("GET", "#{@base_url}/streams?#{URI.encode_www_form opt}", nil)
|
34
|
+
[ret["items"].map{|x| x["key"]}, ret["marker"]]
|
35
|
+
end
|
36
|
+
private :plist
|
37
|
+
|
38
|
+
# list 根据 prefix 遍历 Hub 的流列表.
|
39
|
+
#
|
40
|
+
# 参数:
|
41
|
+
#
|
42
|
+
# limit 限定了一次最多可以返回的流个数, 实际返回的流个数可能小于这个 limit 值.
|
43
|
+
# marker 是上一次遍历得到的流标.
|
44
|
+
# omarker 记录了此次遍历到的游标, 在下次请求时应该带上, 如果 omarker 为 "" 表示已经遍历完所有流.
|
45
|
+
#
|
46
|
+
# For example:
|
47
|
+
#
|
48
|
+
# marker = ""
|
49
|
+
# while true
|
50
|
+
# keys, marker = hub.list(:marker=> marker)
|
51
|
+
# # do something with keys.
|
52
|
+
# if marker == ""
|
53
|
+
# break
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
def list(opt = {})
|
57
|
+
opt[:liveonly] = false
|
58
|
+
plist(opt)
|
59
|
+
end
|
60
|
+
|
61
|
+
# list 根据 prefix 遍历 Hub 正在播放的流列表.
|
62
|
+
#
|
63
|
+
# 参数:
|
64
|
+
#
|
65
|
+
# limit 限定了一次最多可以返回的流个数, 实际返回的流个数可能小于这个 limit 值.
|
66
|
+
# marker 是上一次遍历得到的流标.
|
67
|
+
# omarker 记录了此次遍历到的游标, 在下次请求时应该带上, 如果 omarker 为 "" 表示已经遍历完所有流.
|
68
|
+
#
|
69
|
+
# For example:
|
70
|
+
#
|
71
|
+
# marker = ""
|
72
|
+
# while true
|
73
|
+
# keys, marker = hub.list_live(:marker=> marker)
|
74
|
+
# # do something with keys.
|
75
|
+
# if marker == ""
|
76
|
+
# break
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
def list_live(opt = {})
|
80
|
+
opt[:liveonly] = true
|
81
|
+
plist(opt)
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_s
|
85
|
+
"#<#{self.class} #{@hub}>"
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_json
|
89
|
+
{:hub=>@hub}.to_json
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
data/lib/pili/rpc.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require "json"
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
module Pili
|
6
|
+
class RPC
|
7
|
+
def initialize(mac)
|
8
|
+
@mac = mac
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_request(method, url)
|
12
|
+
case method
|
13
|
+
when "POST"
|
14
|
+
req = Net::HTTP::Post.new(URI(url))
|
15
|
+
when "GET"
|
16
|
+
req = Net::HTTP::Get.new(URI(url))
|
17
|
+
else
|
18
|
+
raise "unsupport RPC method #{method}"
|
19
|
+
end
|
20
|
+
req["User-Agent"] = Config.user_agent()
|
21
|
+
req
|
22
|
+
end
|
23
|
+
|
24
|
+
def request(req)
|
25
|
+
req["Authorization"] = "Qiniu #{@mac.sign_request req}"
|
26
|
+
Net::HTTP.start(req.uri.host, req.uri.port) {|http| http.request req}
|
27
|
+
end
|
28
|
+
|
29
|
+
def call_with_json(method, url, params)
|
30
|
+
req = new_request(method, url)
|
31
|
+
|
32
|
+
if method == "POST" && params != nil
|
33
|
+
req.body = params.to_json
|
34
|
+
req["Content-Type"] = "application/json"
|
35
|
+
req["Content-Length"] = req.body.length
|
36
|
+
end
|
37
|
+
resp = request(req)
|
38
|
+
if resp.code.start_with? "2"
|
39
|
+
if resp["Content-Type"] == "application/json"
|
40
|
+
JSON.load resp.body
|
41
|
+
else
|
42
|
+
resp.body
|
43
|
+
end
|
44
|
+
else
|
45
|
+
msg = "call #{url} failed: #{resp.code}"
|
46
|
+
case resp.code
|
47
|
+
when '612', '619'
|
48
|
+
e = ResourceNotExist.new msg, resp
|
49
|
+
when '614'
|
50
|
+
e = ResourceConflict.new msg, resp
|
51
|
+
else
|
52
|
+
e = RPCError.new msg, resp
|
53
|
+
end
|
54
|
+
raise e
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private_constant :RPC
|
60
|
+
end
|
data/lib/pili/stream.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Pili
|
6
|
+
class StreamInfo
|
7
|
+
attr_reader :hub, :key
|
8
|
+
|
9
|
+
# 表示禁用结束的时间, 0 表示不禁用, -1 表示永久禁用.
|
10
|
+
attr_reader :disabled_till
|
11
|
+
|
12
|
+
def initialize(hub, key, disabled_till)
|
13
|
+
@hub = hub
|
14
|
+
@key = key
|
15
|
+
@disabled_till = disabled_till
|
16
|
+
end
|
17
|
+
|
18
|
+
def disabled?
|
19
|
+
@disabled_till == -1 || @disabled_till > Time.new.to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"#<#{self.class} #{@hub}/#{@key} disabled:#{disabled?}>"
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_json
|
27
|
+
{:hub=>@hub, :key=>@key, :disabled=>disabled?}.to_json
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Stream
|
32
|
+
attr_reader :hub, :key
|
33
|
+
|
34
|
+
def initialize(hub, key, client)
|
35
|
+
@hub = hub
|
36
|
+
@key = key
|
37
|
+
|
38
|
+
ekey = Base64.urlsafe_encode64(key)
|
39
|
+
@base_url = "#{Config.api_base_url}/hubs/#{hub}/streams/#{ekey}"
|
40
|
+
|
41
|
+
@client = client
|
42
|
+
end
|
43
|
+
|
44
|
+
# Info 获得流信息.
|
45
|
+
def info
|
46
|
+
ret = @client.rpc.call_with_json("GET", @base_url, nil)
|
47
|
+
StreamInfo.new @hub, @key, ret["disabledTill"]
|
48
|
+
end
|
49
|
+
|
50
|
+
# 禁用一个流.
|
51
|
+
def disable
|
52
|
+
@client.rpc.call_with_json("POST", "#{@base_url}/disabled", {:disabledTill=>-1})
|
53
|
+
end
|
54
|
+
|
55
|
+
# 启用一个流.
|
56
|
+
def enable
|
57
|
+
@client.rpc.call_with_json("POST", "#{@base_url}/disabled", {:disabledTill=>0})
|
58
|
+
end
|
59
|
+
|
60
|
+
# 查询直播状态.
|
61
|
+
#
|
62
|
+
# 返回
|
63
|
+
#
|
64
|
+
# {
|
65
|
+
# "startAt" => <Integer>, # 直播开始的 Unix 时间戳, 0 表示当前没在直播.
|
66
|
+
# "clientIp" => <String>, # 直播的客户端 IP.
|
67
|
+
# "bps" => <Integer>, # 直播的码率、帧率信息.
|
68
|
+
# "fps" => {
|
69
|
+
# "audio" => <Integer>,
|
70
|
+
# "video" => <Integer>,
|
71
|
+
# "data" => <Integer>
|
72
|
+
# }
|
73
|
+
# }
|
74
|
+
def live_status
|
75
|
+
@client.rpc.call_with_json("GET", "#{@base_url}/live", nil)
|
76
|
+
end
|
77
|
+
|
78
|
+
# 保存直播回放.
|
79
|
+
#
|
80
|
+
# 参数: start, end 是 Unix 时间戳, 限定了保存的直播的时间范围, 0 值表示不限定, 系统会默认保存最近一次直播的内容.
|
81
|
+
#
|
82
|
+
# 返回保存的文件名, 由系统生成.
|
83
|
+
def save(opt = {})
|
84
|
+
ret = @client.rpc.call_with_json("POST", "#{@base_url}/saveas", opt)
|
85
|
+
ret["fname"]
|
86
|
+
end
|
87
|
+
|
88
|
+
# 查询直播历史.
|
89
|
+
#
|
90
|
+
# 参数: start, end 是 Unix 时间戳, 限定了查询的时间范围, 0 值表示不限定, 系统会返回所有时间的直播历史.
|
91
|
+
#
|
92
|
+
# 返回:
|
93
|
+
#
|
94
|
+
# {
|
95
|
+
# "start" => <Integer>,
|
96
|
+
# "end" => <Integer>
|
97
|
+
# }
|
98
|
+
def history_activity(opt = {})
|
99
|
+
url = "#{@base_url}/historyactivity"
|
100
|
+
if !opt.empty?
|
101
|
+
url += "?#{URI.encode_www_form opt}"
|
102
|
+
end
|
103
|
+
ret = @client.rpc.call_with_json("GET", url, nil)
|
104
|
+
ret["items"]
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_s
|
108
|
+
"#<#{self.class} #{@hub}/#{@key}>"
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_json
|
112
|
+
{:hub=>@hub, :key=>@key}.to_json
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/pili/version.rb
ADDED
data/lib/pili.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require_relative "pili/version"
|
5
|
+
require_relative "pili/config"
|
6
|
+
require_relative "pili/exception"
|
7
|
+
require_relative "pili/auth"
|
8
|
+
require_relative "pili/rpc"
|
9
|
+
require_relative "pili/client"
|
10
|
+
require_relative "pili/hub"
|
11
|
+
require_relative "pili/stream"
|
data/pili.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pili/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "piliv2"
|
8
|
+
spec.version = Pili::VERSION
|
9
|
+
spec.authors = ["Pili Engineer"]
|
10
|
+
spec.email = ["pili@qiniu.com"]
|
11
|
+
spec.summary = %q{Pili Streaming Cloud Server-Side Library For Ruby.}
|
12
|
+
spec.description = %q{Pili Streaming Cloud Server-Side Library For Ruby.}
|
13
|
+
spec.homepage = "https://github.com/pili-engineering/pili-sdk-ruby"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: piliv2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pili Engineer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Pili Streaming Cloud Server-Side Library For Ruby.
|
42
|
+
email:
|
43
|
+
- pili@qiniu.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- ".travis.yml"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- example/example.rb
|
55
|
+
- lib/pili.rb
|
56
|
+
- lib/pili/auth.rb
|
57
|
+
- lib/pili/client.rb
|
58
|
+
- lib/pili/config.rb
|
59
|
+
- lib/pili/exception.rb
|
60
|
+
- lib/pili/hub.rb
|
61
|
+
- lib/pili/rpc.rb
|
62
|
+
- lib/pili/stream.rb
|
63
|
+
- lib/pili/version.rb
|
64
|
+
- pili.gemspec
|
65
|
+
homepage: https://github.com/pili-engineering/pili-sdk-ruby
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.5.1
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Pili Streaming Cloud Server-Side Library For Ruby.
|
89
|
+
test_files: []
|