diff --git a/README.md b/README.md index 2e7f31b..f85c0ca 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,20 @@ - Stream - [x] 流信息: stream.refresh() - [x] 禁用流: stream.disable(till) - - [x] 启用流: stream.enable() - [x] 查询直播状态: stream.status() - [x] 保存直播回放: stream.saveas(start, end, options) - [x] 保存直播截图: stream.snapshot(options) - [x] 更改流的实时转码规格: stream.update_converts(profiles) - [x] 查询直播历史: stream.history(start, end) +- Room + - [x] 创建房间: room.create_room(options) + - [x] 生成token: room.roomToken(options) + - [x] 删除房间: room.deleteRoom(roomName) + - [x] 踢除用户: room.kickUser(roomName, userId) + - [x] 查询房间: room.getRoom(roomName) + - [x] 查询在线用户: room.getUser(roomName) + ## Contents @@ -46,12 +53,19 @@ - [Stream](#stream) - [Get Stream info](#get-stream-info) - [Disable a Stream](#disable-a-stream) - - [Enable a Stream](#enable-a-stream) - [Get Stream live status](#get-stream-live-status) - [Save Stream live playback](#save-stream-live-playback) - [Save Stream snapshot](#save-stream-snapshot) - [Update Stream converts](#update-stream-converts) - [Get Stream history activity](#get-stream-history-activity) + - [Room](#stream) + - [Create Room](#create-room) + - [Create Token](#create-token) + - [Delete Room](#delete-room) + - [Kick User](#kick-user) + - [Query Room](#query-room) + - [Query User](#query-user) + ## Installation @@ -75,7 +89,6 @@ secret_key = "" # 替换成自己 Qiniu 账号的 SecretKey hub_name = "" # Hub 必须事先存在 mac = pili.Mac(AccessKey, SecretKey) -client = pili.Client(mac) # ... ``` @@ -127,8 +140,7 @@ print pili.snapshot_play_url("live-snapshot.test.com", hub_name, "streamtitle") ```python mac = pili.Mac(AccessKey, SecretKey) -client = pili.Client(mac) -hub = client.hub("PiliSDKTest") +hub = Hub(mac, hub_name) ``` #### Create a new Stream @@ -198,19 +210,6 @@ print "after sleep 5 seconds:", stream.refresh(), stream.disabled() # after sleep 5 seconds: {"disabledTill": 1488378022, "converts": [], "hub": "PiliSDKTest", "key": "stream23126041129_1"} False ``` -#### Enable a Stream - -```python -stream.disable() -stream = hub.get(stream_title1) -stream.disable() -print "before enable:", stream.refresh(), stream.disabled() -stream.enable() -print "after enable:", stream.refresh(), stream.disabled() -# before enable: {"disabledTill": -1, "converts": [], "hub": "PiliSDKTest", "key": "stream23126041129_1"} True -# after enable: {"disabledTill": 0, "converts": [], "hub": "PiliSDKTest", "key": "stream23126041129_1"} False -``` - #### Get Stream live status ```python @@ -252,7 +251,84 @@ print "after update converts:", stream.refresh() ```python now = int(time.time()) -print "get publish history:" +print "get publish history print stream.history(start_second=now-86400) # [{u'start': 1488359927, u'end': 1488367483}, {u'start': 1488348110, u'end': 1488358759}, {u'start': 1488338678, u'end': 1488340383}, {u'start': 1488333270, u'end': 1488337953}, {u'start': 1488282646, u'end': 1488288321}] ``` + + +### Room + +#### Instantiate a Pili Room + +```python +mac = pili.Mac(AccessKey, SecretKey) +room = RoomClient(mac) +``` + +#### Create Room + +```python +resp = room.create_room('admin_user', 'roomname') +print(resp.status_code) +print(resp.headers) +print(resp.text) +#200 +#{'Content-Length': '24', 'X-Whom': 'bc196', 'Vary': 'Accept-Encoding', 'Server': 'nginx/1.8.0', 'X-Log': 'PILI-RTC:1', 'Connection': 'keep-alive', 'X-Reqid': 'bccmalhRqam8WsO_', 'Date': 'Thu, 29 Mar 2018 08:54:01 GMT', 'Content-Type': 'application/json'} +#{"room_name":"roomname"} +``` + +#### Create Token + +````python +print(room.roomToken('roomname', 'admin_user', 'admin', 3600)) +```` + +#### Delete Room + +````python +resp = room.deleteRoom('roomname') +print(resp.status_code) +print(resp.headers) +print(resp.text) +#200 +#{'Content-Length': '2', 'X-Whom': 'bc197', 'Vary': 'Accept-Encoding', 'Server': 'nginx/1.8.0', 'X-Log': 'PILI-RTC:1', 'Connection': 'keep-alive', 'X-Reqid': 'GRkeCnQZB6q8WkvM', 'Date': 'Thu, 29 Mar 2018 08:55:35 GMT', 'Content-Type': 'application/json'} +#{} +```` + +#### Kick User + +````python +resp = room.kickUser('roomname', 'admin_user') +print(resp.status_code) +print(resp.headers) +print(resp.text) +#200 +#{'Content-Length': '33', 'Server': 'nginx/1.8.0', 'X-Log': 'PILI-RTC:169/500', 'Connection': 'keep-alive', 'X-Reqid': 'DT87e9Y39qq8WrN7, DT87e9Y39qq8WrN7', 'Date': 'Thu, 29 Mar 2018 08:59:34 GMT', 'Content-Type': 'application/json'} +#{} +```` + +#### Query Room + +````python +resp = room.getRoom('roomname') +print(resp.status_code) +print(resp.headers) +print(resp.text) +#200 +#{'Content-Length': '73', 'X-Whom': 'bc197', 'Vary': 'Accept-Encoding', 'Server': 'nginx/1.8.0', 'X-Log': 'PILI-RTC:213', 'Connection': 'keep-alive', 'X-Reqid': 'GRkeCnQZRqu8Wp3R, GRkeCnQZRqu8Wp3R', 'Date': 'Thu, 29 Mar 2018 09:00:55 GMT', 'Content-Type': 'application/json'} +#{"owner_id":"admin","room_name":"roomname","room_status":0,"user_max":10} +```` +#### Query User + +````python +resp = room.getUser('roomname') +print(resp.status_code) +print(resp.headers) +print(resp.text) +#200 +#{'Content-Length': '19', 'X-Whom': 'bc197', 'Vary': 'Accept-Encoding', 'Server': 'nginx/1.8.0', 'X-Log': 'PILI-RTC:197', 'Connection': 'keep-alive', 'X-Reqid': 'bccmalhRdqu8WnnH, bccmalhRdqu8WnnH', 'Date': 'Thu, 29 Mar 2018 09:01:43 GMT', 'Content-Type': 'application/json'} +#{"active_users":[]} +```` + + diff --git a/example.py b/example.py deleted file mode 100644 index 7b74314..0000000 --- a/example.py +++ /dev/null @@ -1,113 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import random -import sys -import time - -import pili - - -def env(key): - if key in os.environ: - return os.environ[key] - else: - return "" - - -if __name__ == "__main__": - if env("PILI_API_HOST") != "": - pili.conf.API_HOST = env("PILI_API_HOST") - - access_key = env("QINIU_ACCESS_KEY") - secret_key = env("QINIU_SECRET_KEY") - if access_key == "" or secret_key == "": - print "need set access_key and secret_key" - sys.exit(1) - - mac = pili.Mac(access_key, secret_key) - - hub_name = "PiliSDKTest" - - client = pili.Client(mac) - hub = client.hub(hub_name) - - stream_pre = "stream2" + str(int(random.random()*1e10)) - stream_title1 = stream_pre + "_1" - stream_title2 = stream_pre + "_2" - stream_title3 = stream_pre + "_3" - - stream = hub.create(stream_title1) - print "create stream:" - print stream - - print "get stream:" - stream = hub.get(stream_title1) - print stream - - print "get stream info:" - stream = hub.get(stream_title1) - print stream.refresh() - - stream = hub.create(stream_title2) - print "create another stream:" - print stream - - hub.create(stream_title3) - - print "list streams:" - print hub.list(prefix=stream_pre, limit=2) - - print "list live streams:" - print hub.list(liveonly=True) - - print "batch query live streams:" - print hub.batch_live_status(["test1", "test2"]) - - stream = hub.get(stream_title1) - print "before disable:", stream, stream.disabled() - stream.disable(int(time.time()) + 5) - print "after call disable:", stream.refresh(), stream.disabled() - time.sleep(5) - print "after sleep 5 seconds:", stream.refresh(), stream.disabled() - - stream = hub.get(stream_title1) - stream.disable() - print "before enable:", stream.refresh(), stream.disabled() - stream.enable() - print "after enable:", stream.refresh(), stream.disabled() - - stream = hub.get(stream_title1) - print "before update converts:", stream.refresh() - stream.update_converts(["480p", "720p"]) - print "after update converts:", stream.refresh() - - stream = hub.get("test1") - print "query stream live status:" - print stream.status() - - now = int(time.time()) - print "save stream playback:" - print stream.saveas(start_second=now-300, fname="test1.m3u8") - - print "save stream snapshot:" - print stream.snapshot(fname="test1.jpg") - - now = int(time.time()) - print "get publish history:" - print stream.history(start_second=now-86400) - - print "RTMP publish URL:" - print pili.rtmp_publish_url("publish-rtmp.test.com", hub_name, "streamtitle", mac, 60) - - print "RTMP play URL:" - print pili.rtmp_play_url("live-rtmp.test.com", hub_name, "streamtitle") - - print "HLS live URL:" - print pili.hls_play_url("live-hls.test.com", hub_name, "streamtitle") - - print "HDL live URL:" - print pili.hdl_play_url("live-hdl.test.com", hub_name, "streamtitle") - - print "snapshot URL:" - print pili.snapshot_play_url("live-snapshot.test.com", hub_name, "streamtitle") diff --git a/interact_micro_example/create_room.py b/interact_micro_example/create_room.py index 2050e39..a8e1c0d 100644 --- a/interact_micro_example/create_room.py +++ b/interact_micro_example/create_room.py @@ -8,11 +8,12 @@ # 替换成自己 Qiniu 账号的 SecretKey secret_key = "..." - mac = Mac(access_key, secret_key) room = RoomClient(mac) -print room.createRoom('admin_user', 'roomname') +resp = room.create_room('admin_user', 'room_name') -# print room.roomToken('roomname', 'admin_user', 'admin', 36000) +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/interact_micro_example/create_token.py b/interact_micro_example/create_token.py new file mode 100644 index 0000000..0007078 --- /dev/null +++ b/interact_micro_example/create_token.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +from pili import RoomClient, Mac + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +mac = Mac(access_key, secret_key) + +room = RoomClient(mac) + +print(room.roomToken('room_name', 'admin_user', 'admin', 3600)) diff --git a/interact_micro_example/delete_room.py b/interact_micro_example/delete_room.py index 61313a0..c63636f 100644 --- a/interact_micro_example/delete_room.py +++ b/interact_micro_example/delete_room.py @@ -12,4 +12,8 @@ room = RoomClient(mac) -print room.deleteRoom('roomname') +resp = room.deleteRoom('room_name') + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/interact_micro_example/kick_user.py b/interact_micro_example/kick_user.py index d452573..4277709 100644 --- a/interact_micro_example/kick_user.py +++ b/interact_micro_example/kick_user.py @@ -12,4 +12,8 @@ room = RoomClient(mac) -print room.kickUser('roomname', 'admin_user') +resp = room.kickUser('room_name', 'admin_user') + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/interact_micro_example/query_room.py b/interact_micro_example/query_room.py index 8b9be60..50c53bf 100644 --- a/interact_micro_example/query_room.py +++ b/interact_micro_example/query_room.py @@ -12,4 +12,8 @@ room = RoomClient(mac) -print room.getRoom('roomname') +resp = room.getRoom('room_name') + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/interact_micro_example/query_user.py b/interact_micro_example/query_user.py index c80b912..4b800a8 100644 --- a/interact_micro_example/query_user.py +++ b/interact_micro_example/query_user.py @@ -12,4 +12,8 @@ room = RoomClient(mac) -print room.getUser('roomname') +resp = room.getUser('room_name') + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/live_examples/bandwidth_count.py b/live_examples/bandwidth_count.py deleted file mode 100644 index 1534bd8..0000000 --- a/live_examples/bandwidth_count.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- - -import pili - -# 替换成自己 Qiniu 账号的 AccessKey -access_key = "..." - - -# 替换成自己 Qiniu 账号的 SecretKey -secret_key = "..." - -hub_name = '...' -mac = pili.Mac(access_key, secret_key) -client = pili.Client(mac) - -hub = client.hub(hub_name) - -print hub.bandwidth_count_now() - -# print hub.bandwidth_count_detail(1512616339) - -# print hub.bandwidth_count_history(1512616339, 1512616439) diff --git a/live_examples/convert.py b/live_examples/convert.py index 8930274..3519ff4 100644 --- a/live_examples/convert.py +++ b/live_examples/convert.py @@ -1,6 +1,12 @@ # -*- coding: utf-8 -*- -import pili +""" +https://developer.qiniu.com/pili/api/2521/modify-the-flow-configuration +修改转码流配置 +""" + + +from pili import Mac, Hub # 替换成自己 Qiniu 账号的 AccessKey access_key = "..." @@ -8,12 +14,17 @@ # 替换成自己 Qiniu 账号的 SecretKey secret_key = "..." -hub_name = '' -mac = pili.Mac(access_key, secret_key) -client = pili.Client(mac) +hub_name = "..." + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) -hub = client.hub(hub_name) +stream = hub.get("123") +resp = stream.update_converts(["480p", "720p"]) -stream = hub.get("") -print(stream.update_converts(["480p", "720p"])) +# 建议打印headers,方便向官方反馈问题,该接口调用成功会返回json为空{} +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/live_examples/create_steam.py b/live_examples/create_steam.py index 889ffbf..c3c4414 100644 --- a/live_examples/create_steam.py +++ b/live_examples/create_steam.py @@ -1,6 +1,12 @@ # -*- coding: utf-8 -*- -import pili +""" +https://developer.qiniu.com/pili/api/2515/create-a-flow +创建流 +""" + + +from pili import Mac, Hub # 替换成自己 Qiniu 账号的 AccessKey access_key = "..." @@ -8,14 +14,18 @@ # 替换成自己 Qiniu 账号的 SecretKey secret_key = "..." +hub_name = "..." + +stream_name = "..." -hub_name = '' -stream_name = '' +mac = Mac(access_key, secret_key) -mac = pili.Mac(access_key, secret_key) +hub = Hub(mac, hub_name) -client = pili.Client(mac) +resp = hub.create(key=stream_name) -hub = client.hub(hub_name) +print(resp.status_code) +print(resp.headers) +print(resp.text) -hub.create(stream_name) +print(hub.get(stream_name)) diff --git a/live_examples/history_record.py b/live_examples/history_record.py index a5aaaa7..9c2985e 100644 --- a/live_examples/history_record.py +++ b/live_examples/history_record.py @@ -1,22 +1,31 @@ # -*- coding: utf-8 -*- -import pili +""" +https://developer.qiniu.com/pili/api/2778/live-history +单个直播流历史查询 +""" + + +from pili import Mac, Hub # 替换成自己 Qiniu 账号的 AccessKey access_key = "..." - # 替换成自己 Qiniu 账号的 SecretKey secret_key = "..." +hub_name = "..." -hub_name = '...' +stream_name = "123" -stream_name = '...' +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) -mac = pili.Mac(access_key, secret_key) -client = pili.Client(mac) -hub = client.hub(hub_name) stream = hub.get(stream_name) -print(stream.history(start_second=0, end_second=0)) +resp = stream.history(start=1513616430, end=1513616400) + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/live_examples/live_stream.py b/live_examples/live_stream.py new file mode 100644 index 0000000..483bdb2 --- /dev/null +++ b/live_examples/live_stream.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +""" +https://developer.qiniu.com/pili/api/2776/live-broadcast-of-real-time-information +单条流实时状态查询 +""" + + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = "..." + +stream_name = "123" + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +stream = hub.get(stream_name) + +resp = stream.status() + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/live_examples/live_streams.py b/live_examples/live_streams.py new file mode 100644 index 0000000..a9095c2 --- /dev/null +++ b/live_examples/live_streams.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +""" +https://developer.qiniu.com/pili/api/3764/batch-query-broadcast-real-time-information +批量获取实时流信息 +""" + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = "..." + +stream_lists = ["123", "abc"] + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +resp = hub.batch_live_status(stream_lists) + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/live_examples/publish_play_url.py b/live_examples/publish_play_url.py index f66a2fb..8a26e24 100644 --- a/live_examples/publish_play_url.py +++ b/live_examples/publish_play_url.py @@ -1,6 +1,16 @@ # -*- coding: utf-8 -*- -import pili +""" +https://developer.qiniu.com/pili/api/2767/the-rtmp-push-flow-address +生成推流拉流及封面地址 + +该生成推流地址方法只适用于v2版本的限时鉴权,v2的无鉴权只需拼接域名,直播空间名和流名,v1版本的鉴权算法见下 +https://developer.qiniu.com/pili/kb/1332/broadcast-authentication-mode +https://developer.qiniu.com/pili/kb/2635/seven-cows-live-push-flow-authentication-md +""" + + +from pili import Mac, Hub, rtmp_play_url, rtmp_publish_url, hdl_play_url, hls_play_url, snapshot_play_url # 替换成自己 Qiniu 账号的 AccessKey access_key = "..." @@ -8,29 +18,26 @@ # 替换成自己 Qiniu 账号的 SecretKey secret_key = "..." -domain = '...' +hub_name = "..." -hub_name = '...' +domain = "..." -stream_title = '...' +stream_name = '123' expire = 3600 -mac = pili.Mac(access_key, secret_key) -client = pili.Client(mac) - -hub = client.hub(hub_name) - +mac = Mac(access_key, secret_key) -stream = hub.get("...") +hub = Hub(mac, hub_name) +stream = hub.get(stream_name) -print pili.rtmp_publish_url(domain, hub_name, stream_title, mac, expire) +print(rtmp_publish_url(domain, hub_name, stream_name, mac, expire)) -print pili.rtmp_play_url(domain, hub_name, stream_title) +print(rtmp_play_url(domain, hub_name, stream_name)) -print pili.hls_play_url(domain, hub_name, stream_title) +print(hls_play_url(domain, hub_name, stream_name)) -print pili.hdl_play_url(domain, hub_name, stream_title) +print(hdl_play_url(domain, hub_name, stream_name)) -print pili.snapshot_play_url(domain, hub_name, stream_title) +print(snapshot_play_url(domain, hub_name, stream_name)) diff --git a/live_examples/save_playback.py b/live_examples/save_playback.py index 4451c85..1e59e6e 100644 --- a/live_examples/save_playback.py +++ b/live_examples/save_playback.py @@ -1,23 +1,33 @@ # -*- coding: utf-8 -*- -import pili +""" +https://developer.qiniu.com/pili/api/2777/save-the-live-playback +录制直播回放 +""" -access_key = "..." +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." # 替换成自己 Qiniu 账号的 SecretKey secret_key = "..." - hub_name = "..." -stream_name = "..." +stream_name = "123" -fname = 'example_fname.mp4' +fname = 'example_fname.m3u8' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) -mac = pili.Mac(access_key, secret_key) -client = pili.Client(mac) -hub = client.hub(hub_name) stream = hub.get(stream_name) -print(stream.saveas(start_second=0, end_second=0, format='mp4', fname=fname)) +resp = stream.saveas(start=0, end=0, format='m3u8', fname=fname) + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/live_examples/snapshot.py b/live_examples/snapshot.py new file mode 100644 index 0000000..81aa953 --- /dev/null +++ b/live_examples/snapshot.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +""" +https://developer.qiniu.com/pili/api/2520/save-the-live-capture +保存直播截图 +""" + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = "..." + +stream_name = "..." + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +stream = hub.get(stream_name) + +# 不填参数或till为0表示不禁播,-1表示永久禁播,其他数字表示禁播到某一时刻的时间戳 +resp = stream.snapshot(time=1521710848, fname="123.jpg", format="jpg") + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/live_examples/stream.py b/live_examples/stream.py new file mode 100644 index 0000000..1d114a6 --- /dev/null +++ b/live_examples/stream.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +""" +https://developer.qiniu.com/pili/api/2773/query-stream +查询流详情 +""" + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = "..." + +stream_name = "..." + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +stream = hub.get(stream_name) + +print(stream) + + diff --git a/live_examples/stream_disable.py b/live_examples/stream_disable.py new file mode 100644 index 0000000..5378366 --- /dev/null +++ b/live_examples/stream_disable.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +""" +https://developer.qiniu.com/pili/api/2775/off-the-air-flow +禁播流 +""" + + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = "..." + +stream_name = "..." + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +stream = hub.get(stream_name) + +# 不填参数或till为0表示不禁播,-1表示永久禁播,其他数字表示禁播到某一时刻的时间戳 +stream.disable(till=1521710848) + +print(stream) diff --git a/live_examples/streams.py b/live_examples/streams.py new file mode 100644 index 0000000..8401925 --- /dev/null +++ b/live_examples/streams.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +""" +https://developer.qiniu.com/pili/api/2774/current-list +查询流列表 +""" + + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = '...' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +liveonly = False + +prefix = "..." + +limit = 100 + +resp = hub.list() + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/operations_example/bandwidth_count_detail.py b/operations_example/bandwidth_count_detail.py new file mode 100644 index 0000000..b0eb5d4 --- /dev/null +++ b/operations_example/bandwidth_count_detail.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +import time +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = '...' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +resp = hub.bandwidth_count_detail(int(time.time())) + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/operations_example/bandwidth_count_history.py b/operations_example/bandwidth_count_history.py new file mode 100644 index 0000000..89bfdb2 --- /dev/null +++ b/operations_example/bandwidth_count_history.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +import time +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = '...' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +resp = hub.bandwidth_count_history(start=int(time.time())-100, + end=int(time.time()), limit=100, marker=None) + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/operations_example/bandwith_count_now.py b/operations_example/bandwith_count_now.py new file mode 100644 index 0000000..70f285c --- /dev/null +++ b/operations_example/bandwith_count_now.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = '...' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +resp = hub.bandwidth_count_now() + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/operations_example/wm_create.py b/operations_example/wm_create.py new file mode 100644 index 0000000..8497803 --- /dev/null +++ b/operations_example/wm_create.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +""" +name: 模版名称, ^[A-Za-z0-9-_]{4,64}$ +comment: 模版描述 +left: 水印图片左上角距离视频图像左上角的水平位置占视频宽度的百分比,[0%, 100%) +top: 水印图片左上角距离视频图像左上角的垂直位置占视频 度的百分比,[0%, 100%) +width: 水印图片的宽度占视频宽度的百分比,高度会等比缩放。[0%, 100%), 与left之和不能超过100 +imageURL: 水印图片地址,仅支持PNG格式 +imageData: base64编码的图片原始数据,与imageURL二选一! +""" + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = '...' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +resp = hub.wm_crete(name="test1", comment="for_test1", left='50%', + top='50%', width='10%', imageURL="http://xxx.xxx.com/abc.png") + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/operations_example/wm_download.py b/operations_example/wm_download.py new file mode 100644 index 0000000..e0b6189 --- /dev/null +++ b/operations_example/wm_download.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = '...' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +resp = hub.wm_download(name="test1") + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/operations_example/wm_list.py b/operations_example/wm_list.py new file mode 100644 index 0000000..ad9dce7 --- /dev/null +++ b/operations_example/wm_list.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = '...' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +resp = hub.wm_list(limit=100) + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/operations_example/wm_query.py b/operations_example/wm_query.py new file mode 100644 index 0000000..d3ca77f --- /dev/null +++ b/operations_example/wm_query.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from pili import Mac, Hub + +# 替换成自己 Qiniu 账号的 AccessKey +access_key = "..." + +# 替换成自己 Qiniu 账号的 SecretKey +secret_key = "..." + +hub_name = '...' + +mac = Mac(access_key, secret_key) + +hub = Hub(mac, hub_name) + +resp = hub.wm_query(name="test1") + +print(resp.status_code) +print(resp.headers) +print(resp.text) diff --git a/pili/__init__.py b/pili/__init__.py index 17c1f57..1f00b15 100644 --- a/pili/__init__.py +++ b/pili/__init__.py @@ -1,6 +1,5 @@ from .errors import APIError # noqa from .auth import Mac # noqa -from .client import Client # noqa from .urls import rtmp_publish_url, rtmp_play_url, hls_play_url, hdl_play_url, snapshot_play_url # noqa -import conf # noqa -from .roomClient import RoomClient # noqa \ No newline at end of file +from .roomClient import RoomClient # noqa +from .hub import Hub # noqa \ No newline at end of file diff --git a/pili/api.py b/pili/api.py index 33fda61..f8cd421 100644 --- a/pili/api.py +++ b/pili/api.py @@ -1,160 +1,16 @@ -from .auth import auth_interface -import pili.conf as conf -from urllib2 import Request -import json -import base64 +from requests import get, post, delete -def normalize(args, keyword): - if set(args) - set(keyword): - raise ValueError('invalid key') - for k, v in args.items(): - if v is None: - del args[k] - return args +def _get(url, auth): + hearders = auth.authed("GET", url) + return get(url=url, headers=hearders) -@auth_interface -def delete_room(version, roomName): - url = "http://%s/%s/rooms/%s" % (conf.RTC_API_HOST, version, roomName) - req = Request(url=url) - req.get_method = lambda: 'DELETE' - return req +def _post(url, auth, data): + hearders = auth.authed("POST", url, body=data) + return post(url=url, headers=hearders, data=data) -@auth_interface -def get_room(version, roomName): - url = "http://%s/%s/rooms/%s" % (conf.RTC_API_HOST, version, roomName) - return Request(url=url) - - -@auth_interface -def get_user(version, roomName): - url = "http://%s/%s/rooms/%s/users" % (conf.RTC_API_HOST, version, roomName) - return Request(url=url) - - -@auth_interface -def kick_user(version, roomName, userId): - url = "http://%s/%s/rooms/%s/users/%s" % (conf.RTC_API_HOST, version, roomName, userId) - req = Request(url=url) - req.get_method = lambda: 'DELETE' - return req - - -@auth_interface -def create_room(ownerId, version, roomName=None): - params = {'owner_id': ownerId} - url = "http://%s/%s/rooms" % (conf.RTC_API_HOST, version) - if bool(roomName): - params['room_name'] = roomName - encoded = json.dumps(params) - req = Request(url=url, data=encoded) - req.get_method = lambda: 'POST' - return req - - -@auth_interface -def create_stream(hub, **kwargs): - keyword = ['key'] - encoded = json.dumps(normalize(kwargs, keyword)) - url = "http://%s/%s/hubs/%s/streams" % (conf.API_HOST, conf.API_VERSION, hub) - return Request(url=url, data=encoded) - - -@auth_interface -def get_stream(hub, key): - key = base64.urlsafe_b64encode(key) - url = "http://%s/%s/hubs/%s/streams/%s" % (conf.API_HOST, conf.API_VERSION, hub, key) - return Request(url=url) - - -@auth_interface -def get_stream_list(hub, **kwargs): - keyword = ['liveonly', 'prefix', 'limit', 'marker'] - args = normalize(kwargs, keyword) - url = "http://%s/%s/hubs/%s/streams?" % (conf.API_HOST, conf.API_VERSION, hub) - for k, v in args.items(): - url += "&%s=%s" % (k, v) - return Request(url=url) - - -@auth_interface -def batch_live_status(hub, streams): - encoded = json.dumps({"items": streams}) - url = "http://%s/%s/hubs/%s/livestreams?" % (conf.API_HOST, conf.API_VERSION, hub) - return Request(url=url, data=encoded) - - -@auth_interface -def disable_stream(hub, key, till): - key = base64.urlsafe_b64encode(key) - url = "http://%s/%s/hubs/%s/streams/%s/disabled" % (conf.API_HOST, conf.API_VERSION, hub, key) - encoded = json.dumps({"disabledTill": till}) - return Request(url=url, data=encoded) - - -@auth_interface -def get_status(hub, key): - key = base64.urlsafe_b64encode(key) - url = "http://%s/%s/hubs/%s/streams/%s/live" % (conf.API_HOST, conf.API_VERSION, hub, key) - return Request(url=url) - - -@auth_interface -def stream_saveas(hub, key, **kwargs): - keyword = ['start', 'end', 'fname', 'format', 'pipeline', 'notify', 'expireDays'] - encoded = json.dumps(normalize(kwargs, keyword)) - key = base64.urlsafe_b64encode(key) - url = "http://%s/%s/hubs/%s/streams/%s/saveas" % (conf.API_HOST, conf.API_VERSION, hub, key) - return Request(url=url, data=encoded) - - -@auth_interface -def stream_snapshot(hub, key, **kwargs): - keyword = ['time', 'fname', 'format'] - encoded = json.dumps(normalize(kwargs, keyword)) - key = base64.urlsafe_b64encode(key) - url = "http://%s/%s/hubs/%s/streams/%s/snapshot" % (conf.API_HOST, conf.API_VERSION, hub, key) - return Request(url=url, data=encoded) - - -@auth_interface -def get_history(hub, key, **kwargs): - keyword = ['start', 'end'] - args = normalize(kwargs, keyword) - key = base64.urlsafe_b64encode(key) - url = "http://%s/%s/hubs/%s/streams/%s/historyactivity?" % (conf.API_HOST, conf.API_VERSION, hub, key) - for k, v in args.items(): - url += "&%s=%s" % (k, v) - return Request(url=url) - - -@auth_interface -def update_stream_converts(hub, key, profiles): - key = base64.urlsafe_b64encode(key) - url = "http://%s/%s/hubs/%s/streams/%s/converts" % (conf.API_HOST, conf.API_VERSION, hub, key) - encoded = json.dumps({"converts": profiles}) - return Request(url=url, data=encoded) - - -@auth_interface -def bandwidth_count_now(hub): - url = "http://%s/%s/hubs/%s/stat/play" % (conf.API_HOST, conf.API_VERSION, hub) - return Request(url=url) - - -@auth_interface -def bandwidth_count_history(hub, **kwargs): - keyword = ['start', 'end', 'limit', 'marker'] - args = normalize(kwargs, keyword) - url = "http://%s/%s/hubs/%s/stat/play/history" % (conf.API_HOST, conf.API_VERSION, hub) - for k, v in args.items(): - url += "&%s=%s" % (k, v) - return Request(url=url) - - -@auth_interface -def bandwidth_count_detail(hub, time): - url = "http://%s/%s/hubs/%s/stat/play/history/detail?time=%s" % (conf.API_HOST, conf.API_VERSION, hub, time) - return Request(url=url) +def _delete(url, auth): + hearders = auth.authed("DELETE", url) + return delete(url=url, headers=hearders) diff --git a/pili/auth.py b/pili/auth.py index 85f1511..3661b58 100644 --- a/pili/auth.py +++ b/pili/auth.py @@ -2,9 +2,9 @@ Auth provide class Auth for authentication account. You can use decorator auth_interface to create a function with auto generated authentication. """ -from urlparse import urlparse -from .utils import send_and_decode, __hmac_sha1__ +from .utils import __hmac_sha1__ +from .compat import urlparse, b import pili.conf as conf @@ -22,7 +22,7 @@ class Auth store the access_key and secret_key for authentication. def __init__(self, access_key, secret_key): if not (access_key and secret_key): raise ValueError('invalid key') - self.access_key, self.secret_key = access_key, secret_key + self.access_key, self.secret_key = access_key, b(secret_key) def auth_interface_str(self, raw_str): """ @@ -31,35 +31,21 @@ def auth_interface_str(self, raw_str): encoded = __hmac_sha1__(raw_str, self.secret_key) return 'Qiniu {0}:{1}'.format(self.access_key, encoded) + def authed(self, method, url, body=None): + headers = {} -def auth_interface(method): - """ - decorator takes func(**args) return req and change it to - func(auth, **args) return json result. - - Args: - func(**args) -> Request - - Returns: - func(**args) -> dict (decoded json) - """ - def authed(auth, **args): - """ - send request and decode response. Return the result in python format. - """ - req = method(**args) - parsed = urlparse(req.get_full_url()) - raw_str = '%s %s' % (req.get_method(), parsed.path) + parsed = urlparse(url) + raw_str = '%s %s' % (method, parsed.path) if parsed.query: - raw_str += '?%s' % (parsed.query) - raw_str += '\nHost: %s' % (parsed.netloc) - if req.has_data(): + raw_str += '?%s' % parsed.query + raw_str += '\nHost: %s' % parsed.netloc + if body: raw_str += '\nContent-Type: application/json' - raw_str += "\n\n" - if req.has_data(): - raw_str += req.get_data() - req.add_header('Content-Type', 'application/json') - req.add_header('Authorization', auth.auth_interface_str(raw_str)) - req.add_header('User-Agent', conf.API_USERAGENT) - return send_and_decode(req) - return authed + raw_str += "\n\n" + raw_str += body + headers.update({'Content-Type': 'application/json'}) + else: + raw_str += "\n\n" + headers.update({'Authorization': self.auth_interface_str(raw_str)}) + headers.update({'User-Agent': conf.API_USERAGENT}) + return headers diff --git a/pili/client.py b/pili/client.py deleted file mode 100644 index 7160e0b..0000000 --- a/pili/client.py +++ /dev/null @@ -1,9 +0,0 @@ -from .hub import Hub - - -class Client(object): - def __init__(self, mac): - self.__mac__ = mac - - def hub(self, hub): - return Hub(self.__mac__, hub) diff --git a/pili/compat.py b/pili/compat.py new file mode 100644 index 0000000..e214493 --- /dev/null +++ b/pili/compat.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +""" +pythoncompat +""" + +import sys + +try: + import simplejson as json +except (ImportError, SyntaxError): + # simplejson does not support Python 3.2, it thows a SyntaxError + # because of u'...' Unicode literals. + import json # noqa + + +# ------- +# Pythons +# ------- + +_ver = sys.version_info + +#: Python 2.x? +is_py2 = (_ver[0] == 2) + +#: Python 3.x? +is_py3 = (_ver[0] == 3) + + +# --------- +# Specifics +# --------- + +if is_py2: + from urlparse import urlparse # noqa + from urllib2 import urlopen, HTTPError + import StringIO + StringIO = BytesIO = StringIO.StringIO + + builtin_str = str + bytes = str + str = unicode # noqa + basestring = basestring # noqa + numeric_types = (int, long, float) # noqa + + def b(data): + return bytes(data) + + def s(data): + return bytes(data) + + def u(data): + return unicode(data, 'unicode_escape') # noqa + +elif is_py3: + from urllib.parse import urlparse # noqa + from urllib.request import urlopen, HTTPError + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + + builtin_str = str + str = str + bytes = bytes + basestring = (str, bytes) + numeric_types = (int, float) + + def b(data): + if isinstance(data, str): + return data.encode('utf-8') + return data + + def s(data): + if isinstance(data, bytes): + data = data.decode('utf-8') + return data + + def u(data): + return data diff --git a/pili/conf.py b/pili/conf.py index 13e20c0..f68e49a 100644 --- a/pili/conf.py +++ b/pili/conf.py @@ -9,5 +9,5 @@ API_HOST = 'pili.qiniuapi.com' API_USERAGENT = "pili-sdk-python/v2 %s %s" % (platform.python_version(), sys.platform) -RTC_API_VERSION = 'v1' +RTC_API_VERSION = 'v2' RTC_API_HOST = 'rtc.qiniuapi.com' diff --git a/pili/hub.py b/pili/hub.py index e280dda..0fcfb1a 100644 --- a/pili/hub.py +++ b/pili/hub.py @@ -1,7 +1,11 @@ # -*- coding: utf-8 -*- +import json + import pili.api as api from .stream import Stream +from .conf import API_HOST, API_VERSION +from .utils import normalize_path, normalize_data class Hub(object): @@ -10,9 +14,11 @@ def __init__(self, mac, hub): self.__hub__ = hub # create 创建一路流 - def create(self, key): - api.create_stream(self.__auth__, hub=self.__hub__, key=key) - return Stream(self.__auth__, hub=self.__hub__, key=key) + def create(self, **kwargs): + keyword = ['key'] + url = "http://{0}/{1}/hubs/{2}/streams".format(API_HOST, API_VERSION, self.__hub__) + encoded = normalize_data(kwargs, keyword) + return api._post(url=url, auth=self.__auth__, data=encoded) # 获取一路流 def get(self, key): @@ -30,8 +36,10 @@ def get(self, key): marker: 这次遍历得到的游标,下次请求应该带上,如果为"",则表示已遍历完所有流 """ def list(self, **kwargs): - res = api.get_stream_list(self.__auth__, hub=self.__hub__, **kwargs) - return res + url = "http://{0}/{1}/hubs/{2}/streams?".format(API_HOST, API_VERSION, self.__hub__) + keyword = ['liveonly', 'prefix', 'limit', 'marker'] + url = normalize_path(kwargs, keyword, url) + return api._get(url=url, auth=self.__auth__) """ batch_live_status 批量查询流的直播信息 @@ -47,19 +55,49 @@ def list(self, **kwargs): video: 正整数,视频帧率 data: 正整数,数据帧率 """ + def batch_live_status(self, streams): - res = api.batch_live_status(self.__auth__, hub=self.__hub__, streams=streams) - return res["items"] + encoded = json.dumps({"items": streams}) + url = "http://{0}/{1}/hubs/{2}/livestreams".format(API_HOST, API_VERSION, self.__hub__) + return api._post(url=url, auth=self.__auth__, data=encoded) def bandwidth_count_now(self): - res = api.bandwidth_count_now(self.__auth__, hub=self.__hub__) - return res + url = "http://{0}/{1}/hubs/{2}/stat/play".format(API_HOST, API_VERSION, self.__hub__) + return api._get(url, self.__auth__) - def bandwidth_count_history(self, start, end, limit=None, marker=None): - res = api.bandwidth_count_history(self.__auth__, hub=self.__hub__, start=start, end=end, limit=limit, - marker=marker) - return res + def bandwidth_count_history(self, **kwargs): + url = "http://{0}/{1}/hubs/{2}/stat/play/history".format(API_HOST, API_VERSION, self.__hub__) + keyword = ['start', 'end', 'limit', 'marker'] + url = normalize_path(kwargs, keyword, url) + return api._get(url=url, auth=self.__auth__) def bandwidth_count_detail(self, time): - res = api.bandwidth_count_detail(self.__auth__, hub=self.__hub__, time=time) - return res + url = "http://{0}/{1}/hubs/{2}/stat/play/history/detail?time={3}".format(API_HOST, API_VERSION, + self.__hub__, time) + return api._get(url, self.__auth__) + + def wm_crete(self, **kwargs): + keyword = ['name', 'comment', "left", "top", "width", "imageURL", "imageData"] + encoded = normalize_data(kwargs, keyword) + url = "http://{0}/{1}/hubs/{2}/watermarktemplate".format(API_HOST, API_VERSION, self.__hub__) + return api._post(url=url, auth=self.__auth__, data=encoded) + + def wm_list(self, **kwargs): + keyword = ['limit'] + url = "http://{0}/{1}/hubs/{2}/watermarktemplate".format(API_HOST, API_VERSION, self.__hub__) + url = normalize_path(kwargs, keyword, url) + return api._get(url=url, auth=self.__auth__) + + def wm_download(self, name): + url = "http://{0}/{1}/hubs/{2}/watermarktemplate/{3}/image".format(API_HOST, API_VERSION, self.__hub__, name) + return api._get(url=url, auth=self.__auth__) + + def wm_query(self, name): + url = "http://{0}/{1}/hubs/{2}/watermarktemplate/{3}".format(API_HOST, API_VERSION, self.__hub__, name) + return api._get(url=url, auth=self.__auth__) + + def se_qweszcdasf(self, **kwargs): + url = "http://{0}/{1}/hubs/{2}/security".format(API_HOST, API_VERSION, self.__hub__) + keyword = ["publishSecurity", "publishKey"] + encoded = normalize_data(kwargs, keyword) + return api._post(url=url, auth=self.__auth__, data=encoded) diff --git a/pili/roomClient.py b/pili/roomClient.py index 99db8ec..40c140b 100755 --- a/pili/roomClient.py +++ b/pili/roomClient.py @@ -2,8 +2,10 @@ import hashlib import json import time + import pili.api as api -from utils import urlsafe_base64_encode +from .utils import urlsafe_base64_encode, b +from .conf import RTC_API_HOST, RTC_API_VERSION class RoomClient(object): @@ -12,28 +14,32 @@ def __init__(self, credentials): self.__credentials__ = credentials self.__auth__ = credentials.__auth__ - def createRoom(self, ownerId, roomName=None, version='v2'): - res = api.create_room(self.__auth__, ownerId=ownerId, roomName=roomName, version=version) - return res + def create_room(self, ownerId, roomName=None, version=RTC_API_VERSION): + params = {'owner_id': ownerId} + url = "http://%s/%s/rooms" % (RTC_API_HOST, version) + if bool(roomName): + params['room_name'] = roomName + encoded = json.dumps(params) + return api._post(url=url, auth=self.__auth__, data=encoded) - def getRoom(self, roomName, version='v2'): - res = api.get_room(self.__auth__, roomName=roomName, version=version) - return res + def getRoom(self, roomName, version=RTC_API_VERSION): + url = "http://%s/%s/rooms/%s" % (RTC_API_HOST, version, roomName) + return api._get(url=url, auth=self.__auth__) - def deleteRoom(self, roomName, version='v2'): - res = api.delete_room(self.__auth__, roomName=roomName, version=version) - return res + def deleteRoom(self, roomName, version=RTC_API_VERSION): + url = "http://%s/%s/rooms/%s" % (RTC_API_HOST, version, roomName) + return api._delete(url=url, auth=self.__auth__) - def getUser(self, roomName, version='v2'): - res = api.get_user(self.__auth__, roomName=roomName, version=version) - return res + def getUser(self, roomName, version=RTC_API_VERSION): + url = "http://%s/%s/rooms/%s/users" % (RTC_API_HOST, version, roomName) + return api._get(url=url, auth=self.__auth__) - def kickUser(self, roomName, userId, version='v2'): - res = api.kick_user(self.__auth__, roomName=roomName, userId=userId, version=version) - return res + def kickUser(self, roomName, userId, version=RTC_API_VERSION): + url = "http://%s/%s/rooms/%s/users/%s" % (RTC_API_HOST, version, roomName, userId) + return api._delete(url=url, auth=self.__auth__) - def roomToken(self, roomName, userId, perm, expireAt, version='v2'): - if version == 'v2': + def roomToken(self, roomName, userId, perm, expireAt, version=RTC_API_VERSION): + if version == RTC_API_VERSION: params = {"version": "2.0", "room_name": roomName, "user_id": userId, "perm": perm, "expire_at": int(time.time()) + expireAt} @@ -42,8 +48,8 @@ def roomToken(self, roomName, userId, perm, expireAt, version='v2'): "user_id": userId, "perm": perm, "expire_at": int(time.time()) + expireAt} - roomAccessString = json.dumps(params, separators=(',', ':')) - encodedRoomAccess = urlsafe_base64_encode(roomAccessString) - hashed = hmac.new(self.__auth__.secret_key, encodedRoomAccess, hashlib.sha1) - encodedSign = urlsafe_base64_encode(hashed.digest()) - return self.__auth__.access_key+":"+encodedSign+":"+encodedRoomAccess + room_access_string = json.dumps(params, separators=(',', ':')) + encoded_room_access = urlsafe_base64_encode(room_access_string) + hashed = hmac.new(self.__auth__.secret_key, b(encoded_room_access), hashlib.sha1) + encoded_sign = urlsafe_base64_encode(hashed.digest()) + return self.__auth__.access_key+":"+encoded_sign+":"+encoded_room_access diff --git a/pili/stream.py b/pili/stream.py index 96fbc4f..29a057d 100644 --- a/pili/stream.py +++ b/pili/stream.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- import json -import time + import pili.api as api +from .utils import urlsafe_base64_encode +from .conf import API_HOST, API_VERSION +from .utils import normalize_path, normalize_data class Stream(object): @@ -27,35 +30,30 @@ def __getattr__(self, attr): self.refresh() try: return self.__data__ if attr == "data" else self.__data__[attr] - except KeyError, e: - return e.message + except KeyError as e: + return e def __repr__(self): return self.to_json() # refresh 主动更新流信息,会产生一次rpc调用 def refresh(self): - data = api.get_stream(self.__auth__, hub=self.hub, key=self.key) + key = urlsafe_base64_encode(self.key) + url = "http://%s/%s/hubs/%s/streams/%s" % (API_HOST, API_VERSION, self.hub, key) + data = api._get(url=url, auth=self.__auth__) self.__data__ = {} - for p in ["disabledTill", "converts"]: - self.__data__[p] = data[p] if p in data else None + for p in ["disabledTill", "converts", "createdAt", "updatedAt", "expireAt", "watermark", "converts"]: + self.__data__[p] = json.loads(data.text).get(p) if p in data.text else None self.__data__["key"] = self.key self.__data__["hub"] = self.hub - return self + return self.__data__ # disable 禁用流,till Unix时间戳,在这之前流均不可用 def disable(self, till=None): - if till is None: - till = -1 - return api.disable_stream(self.__auth__, hub=self.hub, key=self.key, till=till) - - # disabled 判断流是否被禁用 - def disabled(self): - return self.disabledTill == -1 or self.disabledTill > int(time.time()) - - # enable 开启流 - def enable(self): - return api.disable_stream(self.__auth__, hub=self.hub, key=self.key, till=0) + key = urlsafe_base64_encode(self.key) + url = "http://%s/%s/hubs/%s/streams/%s/disabled" % (API_HOST, API_VERSION, self.hub, key) + encoded = json.dumps({"disabledTill": till}) + return api._post(url=url, data=encoded, auth=self.__auth__) """ status 查询直播信息 @@ -69,31 +67,35 @@ def enable(self): data: 正整数,数据帧率 """ def status(self): - res = api.get_status(self.__auth__, hub=self.hub, key=self.key) - return res + key = urlsafe_base64_encode(self.key) + url = "http://%s/%s/hubs/%s/streams/%s/live" % (API_HOST, API_VERSION, self.hub, key) + return api._get(url=url, auth=self.__auth__) """ history 查询直播历史 输入参数: - start_second: Unix时间戳,起始时间,可选,默认不限制起始时间 - end_second: Unix时间戳,结束时间,可选,默认为当前时间 + start: Unix时间戳,起始时间,可选,默认不限制起始时间 + end: Unix时间戳,结束时间,可选,默认为当前时间 返回值: 如下结构的数组 start: Unix时间戳,直播开始时间 end: Unix时间戳,直播结束时间 """ - def history(self, start_second=None, end_second=None): - res = api.get_history(self.__auth__, hub=self.hub, key=self.key, start=start_second, end=end_second) - return res["items"] + def history(self, **kwargs): + key = urlsafe_base64_encode(self.key) + keyword = ['start', 'end'] + url = "http://{0}/{1}/hubs/{2}/streams/{3}/historyactivity?".format(API_HOST, API_VERSION, self.hub, key) + url = normalize_path(kwargs, keyword, url) + return api._get(url=url, auth=self.__auth__) # save_as等同于saveas接口,出于兼容考虑,暂时保留 - def save_as(self, start_second=None, end_second=None, **kwargs): - return self.saveas(start_second, end_second, **kwargs) + def save_as(self, **kwargs): + return self.saveas(**kwargs) """ saveas 保存直播回放到存储空间 输入参数: - start_second: Unix时间戳,起始时间,可选,默认不限制起始时间 - end_second: Unix时间戳,结束时间,可选,默认为当前时间 + start: Unix时间戳,起始时间,可选,默认不限制起始时间 + end: Unix时间戳,结束时间,可选,默认为当前时间 fname: 保存的文件名,可选,不指定会随机生产 format: 保存的文件格式,可选,默认为m3u8,如果指定其他格式则保存动作为异步模式 pipeline: dora的私有队列,可选,不指定则使用默认队列 @@ -106,15 +108,12 @@ def save_as(self, start_second=None, end_second=None, **kwargs): fname: 保存到存储空间的文件名 persistentID: 异步模式时,持久化异步处理任务ID,通常用不到该字段 """ - def saveas(self, start_second=None, end_second=None, **kwargs): - kwargs["hub"] = self.hub - kwargs["key"] = self.key - if start_second is not None: - kwargs["start"] = start_second - if end_second is not None: - kwargs["end"] = end_second - res = api.stream_saveas(self.__auth__, **kwargs) - return res + def saveas(self, **kwargs): + key = urlsafe_base64_encode(self.key) + url = "http://%s/%s/hubs/%s/streams/%s/saveas" % (API_HOST, API_VERSION, self.hub, key) + keyword = ['start', 'end', 'fname', 'format', 'pipeline', 'notify', 'expireDays'] + encoded_data = normalize_data(kwargs, keyword) + return api._post(url=url, auth=self.__auth__, data=encoded_data) """ snapshot 保存直播截图到存储空间 @@ -126,10 +125,11 @@ def saveas(self, start_second=None, end_second=None, **kwargs): fname: 保存到存储空间的文件名 """ def snapshot(self, **kwargs): - kwargs["hub"] = self.hub - kwargs["key"] = self.key - res = api.stream_snapshot(self.__auth__, **kwargs) - return res + keyword = ['time', 'fname', 'format'] + encoded_data = normalize_data(kwargs, keyword) + key = urlsafe_base64_encode(self.key) + url = "http://%s/%s/hubs/%s/streams/%s/snapshot" % (API_HOST, API_VERSION, self.hub, key) + return api._post(url=url, auth=self.__auth__, data=encoded_data) """ update_converts 更改流的转码规格 @@ -138,8 +138,10 @@ def snapshot(self, **kwargs): 返回值: 无 """ def update_converts(self, profiles=[]): - res = api.update_stream_converts(self.__auth__, hub=self.hub, key=self.key, profiles=profiles) - return res + key = urlsafe_base64_encode(self.key) + url = "http://%s/%s/hubs/%s/streams/%s/converts" % (API_HOST, API_VERSION, self.hub, key) + encoded_data = json.dumps({"converts": profiles}) + return api._post(url=url, auth=self.__auth__, data=encoded_data) def to_json(self): return json.dumps(self.data) diff --git a/pili/utils.py b/pili/utils.py index 6fa861b..e417327 100644 --- a/pili/utils.py +++ b/pili/utils.py @@ -1,13 +1,16 @@ """ Utils """ -from urllib2 import urlopen, HTTPError + import contextlib import json -from .errors import APIError import hmac import hashlib -import base64 +from base64 import urlsafe_b64encode, urlsafe_b64decode +from copy import deepcopy + +from .compat import urlopen, HTTPError, b, s +from .errors import APIError def send_and_decode(req): @@ -26,7 +29,7 @@ def send_and_decode(req): return None raw = res.read() return json.loads(raw) - except HTTPError, res: + except HTTPError as res: raw = res.read() try: data = json.loads(raw) @@ -40,18 +43,38 @@ def __hmac_sha1__(data, key): """ hmac-sha1 """ + data = b(data) hashed = hmac.new(key, data, hashlib.sha1) - return base64.urlsafe_b64encode(hashed.digest()) + return urlsafe_base64_encode(hashed.digest()) + +def urlsafe_base64_encode(data): + ret = urlsafe_b64encode(b(data)) + return s(ret) -def b(data): - return bytes(data) +def urlsafe_base64_decode(data): + ret = urlsafe_b64decode(s(data)) + return ret -def s(data): - return bytes(data) +def normalize_path(args, keyword, url): + if set(args) - set(keyword): + raise ValueError('invalid key') + path = '' + for k, v in args.items(): + if v: + path += "&%s=%s" % (k, v) + if path: + url = url + '?' + path[1:] + return url -def urlsafe_base64_encode(data): - ret = base64.urlsafe_b64encode(b(data)) - return s(ret) + +def normalize_data(args, keyword): + if set(args) - set(keyword): + raise ValueError('invalid key') + copy_args = deepcopy(args) + for k, v in args.items(): + if not v: + del copy_args[k] + return json.dumps(copy_args) diff --git a/setup.py b/setup.py index 35f0371..9f1ead7 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='pili2', - version='2.1.0', + version='2.2.0', keywords=('pili', 'streaming', 'hls', 'rtmp'), description='Pili Streaming Cloud Server-Side Library For Python', license='MIT License', diff --git a/tests/test_hub.py b/tests/test_hub.py index 3e99f5d..b848235 100644 --- a/tests/test_hub.py +++ b/tests/test_hub.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- import os -import unittest +import time +import random +from json import loads +from unittest import TestCase, SkipTest -import pili +from pili import Hub, Mac def env(key): @@ -13,23 +16,65 @@ def env(key): return "" -class TestHubCases(unittest.TestCase): +class TestHubCases(TestCase): def setUp(self): - hub_name = "PiliSDKTest" - access_key = env("QINIU_ACCESS_KEY") - secret_key = env("QINIU_SECRET_KEY") + hub_name = env("TEST_HUB") + access_key = env("access_key") + secret_key = env("secret_key") + if access_key == "" or secret_key == "": - raise unittest.SkipTest("need set access_key or secret_key") - if env("PILI_API_HOST") != "": - pili.conf.API_HOST = env("PILI_API_HOST") - client = pili.Client(pili.Mac(access_key, secret_key)) - self.hub = client.hub(hub_name) + raise SkipTest("need set access_key or secret_key") + mac = Mac(access_key, secret_key) + self.hub = Hub(mac, hub_name) # 这个测试case需要保持推流test1 def test_batch_live_status(self): items = self.hub.batch_live_status(["test1", "test2"]) - self.assertEqual(len(items), 1) - self.assertEqual(items[0]["key"], "test1") - self.assertTrue(items[0]["startAt"] > 0) - self.assertTrue(items[0]["bps"] > 0) + self.assertEqual(items.status_code, 200) + self.assertIn("test1", loads(items.text).get("items")[0].get("key")) + + def test_create_stream(self): + self.stream_key = "streamtest" + str(int(random.random()*1e10))+str(time.time())[:10] + items = self.hub.create(key=self.stream_key) + self.assertEqual(items.status_code, 200) + self.assertEqual({}, loads(items.text)) + + def test_query_streams(self): + items = self.hub.get("test1") + self.assertNotEqual(None, items) + + def test_wm_create(self): + test_name = "test" + str(int(time.time())) + items = self.hub.wm_crete(name=test_name, comment="for_test1", left='50%', + top='50%', width='10%', imageURL="http://omhrg3tgg.bkt.clouddn.com/413.png") + self.assertEqual(items.status_code, 200) + self.assertEqual(test_name, loads(items.text).get("name")) + items1 = self.hub.wm_crete(name="test1", comment="for_test1", left='50%', + top='50%', width='10%', imageURL="http://omhrg3tgg.bkt.clouddn.com/413.png") + self.assertEqual(items1.status_code, 614) + + def test_wm_download(self): + item = self.hub.wm_download(name="test1522373806") + self.assertEqual(item.status_code, 200) + + def test_wm_list(self): + items = self.hub.wm_list(limit=100) + self.assertIn("items", loads(items.text)) + + def test_wm_query(self): + items = self.hub.wm_query("test1") + self.assertEqual("test1", loads(items.text).get("name")) + + def test_bandwidth_detail(self): + items = self.hub.bandwidth_count_detail(str(int(time.time()))) + self.assertIn("items", loads(items.text)) + + def test_bandwidth_history(self): + items = self.hub.bandwidth_count_history(start=int(time.time()) - 100, + end=int(time.time()), limit=100, marker=None) + self.assertIn("items", loads(items.text)) + + def test_bandwidth_now(self): + items = self.hub.bandwidth_count_now() + self.assertIn("total", loads(items.text)) diff --git a/tests/test_room.py b/tests/test_room.py new file mode 100644 index 0000000..9215a26 --- /dev/null +++ b/tests/test_room.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +import os +import unittest +import json + +from pili import RoomClient, Mac +from pili.utils import urlsafe_base64_decode + + +def env(key): + if key in os.environ: + return os.environ[key] + else: + return "" + + +class TestRoomCases(unittest.TestCase): + + def setUp(self): + access_key = env("access_key") + secret_key = env("secret_key") + + if access_key == "" or secret_key == "": + raise unittest.SkipTest("need set access_key or secret_key") + mac = Mac(access_key, secret_key) + self.room = RoomClient(mac) + + def test_create_room(self): + items = self.room.create_room('admin_user', 'roomname') + self.assertEqual("roomname", json.loads(items.text).get("room_name")) + + def test_create_token(self): + test_token = self.room.roomToken('roomname', 'admin_user', 'admin', 3600) + decode_token = json.loads(urlsafe_base64_decode(test_token.split(":")[2])) + self.assertIn("admin_user", decode_token.get("user_id")) + self.assertIn("2.0", decode_token.get("version")) + self.assertIn("roomname", decode_token.get("room_name")) + + def test_delete_room(self): + resp = self.room.deleteRoom("roomname") + self.assertEqual({}, json.loads(resp.text)) + + def test_query_room(self): + resp = self.room.getRoom("roomname") + decode_data = json.loads(resp.text) + self.assertEqual("roomname", decode_data.get("room_name")) + + def test_query_user(self): + resp = self.room.getUser(roomName="roomname") + decode_data = json.loads(resp.text) + self.assertIn("active_users", decode_data) diff --git a/tests/test_stream.py b/tests/test_stream.py index 8afc674..acbc341 100644 --- a/tests/test_stream.py +++ b/tests/test_stream.py @@ -3,9 +3,10 @@ import os import random import time -import unittest +from unittest import TestCase, SkipTest +from json import loads -import pili +from pili import Mac, Hub def env(key): @@ -15,76 +16,61 @@ def env(key): return "" -class TestStreamCases(unittest.TestCase): +class TestStreamCases(TestCase): def setUp(self): - hub_name = "PiliSDKTest" - access_key = env("QINIU_ACCESS_KEY") - secret_key = env("QINIU_SECRET_KEY") + hub_name = env("TEST_HUB") + access_key = env("access_key") + secret_key = env("secret_key") + if access_key == "" or secret_key == "": - raise unittest.SkipTest("need set access_key or secret_key") - if env("PILI_API_HOST") != "": - pili.conf.API_HOST = env("PILI_API_HOST") - client = pili.Client(pili.Mac(access_key, secret_key)) - self.hub = client.hub(hub_name) + raise SkipTest("need set access_key or secret_key") + mac = Mac(access_key, secret_key) + self.hub = Hub(mac, hub_name) self.stream_title = "streamTest" + str(int(random.random()*1e10)) def test_stream_create(self): - stream = self.hub.create(self.stream_title) - self.assertEqual(stream.hub, "PiliSDKTest") - self.assertEqual(stream.key, self.stream_title) + items = self.hub.create(key=self.stream_title) + self.assertEqual(items.status_code, 200) + self.assertEqual(loads(items.text), {}) def test_stream_disable(self): - stream = self.hub.create(self.stream_title) - self.assertFalse(stream.disabled()) - stream.disable() - stream = stream.refresh() - self.assertTrue(stream.disabled()) - stream.disable(int(time.time()) + 1) - stream = stream.refresh() - self.assertTrue(stream.disabled()) - time.sleep(2) - stream = stream.refresh() - self.assertFalse(stream.disabled()) + self.hub.create(key=self.stream_title) + stream = self.hub.get(key=self.stream_title) + self.assertEqual({}, loads(stream.disable(-1).text)) + self.assertEqual(-1, stream.refresh().get("disabledTill")) + stream.disable(0) + self.assertEqual(0, stream.refresh().get("disabledTill")) def test_stream_converts(self): - stream = self.hub.create(self.stream_title) - self.assertEqual(len(stream.converts), 0) + stream = self.hub.get(key="test1") stream.update_converts(["480p", "720p"]) - stream = stream.refresh() self.assertEqual(stream.converts, ["480p", "720p"]) - stream.update_converts() - stream = stream.refresh() - self.assertEqual(len(stream.converts), 0) # 这个测试需要维持推流test1 def test_stream_saveas(self): stream = self.hub.get("test1") - stream.save_as() + ret = stream.save_as() + self.assertEqual(200, ret.status_code) now = int(time.time()) - stream.save_as(now - 20) - stream.save_as(now - 20, now) - ret = stream.save_as(now - 20, now, fname="test1.mp4", format="mp4") - self.assertEqual(ret["fname"], "test1.mp4") - self.assertTrue(ret["persistentID"]) - try: - stream.save_as(now - 20, now, format="mp4", pipeline="notexist") - except Exception, e: - self.assertEqual(str(e), "no such pipeline") + ret = stream.saveas(start=now - 3600) + self.assertIn(ret.status_code, (200, 619)) + ret = stream.save_as(start=now - 3600, end=now) + self.assertIn(ret.status_code, (200, 619)) + ret = stream.saveas(start=now - 3600, end=now, fname="test1.m3u8", format="m3u8") + self.assertEqual(loads(ret.text).get("fname"), "test1.m3u8") + ret = stream.save_as(start=now - 3600, end=now, fname="test1.mp4", format="mp4") + self.assertEqual(loads(ret.text).get("fname"), "test1.mp4") + self.assertTrue(loads(ret.text).get("persistentID")) # 这个测试需要维持推流test1 def test_stream_snashot(self): stream = self.hub.get("test1") - ret = stream.snapshot() - self.assertTrue(ret["fname"]) ret = stream.snapshot(fname="test1.jpg") - self.assertEqual(ret["fname"], "test1.jpg") + self.assertEqual(loads(ret.text)["fname"], "test1.jpg") # 这个测试需要维持推流test1 def test_stream_history(self): stream = self.hub.get("test1") - now = int(time.time()) - ret = stream.history(now - 86400, now) - self.assertTrue(len(ret) > 0) - self.assertTrue(ret[0]["start"] > 0) - self.assertTrue(ret[0]["end"] > 0) + self.assertEqual(200, stream.history().status_code) +