16 Commits

Author SHA1 Message Date
50216dc3e7 Create README.md 2019-01-26 14:09:26 +08:00
90435ccbaf 上传完成后自动删除片段,以节省内存 2019-01-26 14:04:23 +08:00
c41ed1f950 Bug Fixed 2019-01-26 13:21:44 +08:00
48cb2edc58 Fix Bugs And Improve Perfomance 2019-01-26 04:18:57 +08:00
798b59e3fd 修改Bilibili.py,尝试下载好片段就上传,等待下播后结束上传 2019-01-25 19:39:15 +08:00
d290c89345 初版自动投稿工具 to Bilibili
直播缓存完就直接上传了,正在测试中,感谢bilibili.py原代码作者:comwrg
2019-01-25 14:26:07 +08:00
33739d21b3 更新:添加未知消息的提示,添加设置房管消息检测 2019-01-23 19:43:53 +08:00
2df0dc07bd 临时更新:未返回导致的错误 2019-01-23 19:12:47 +08:00
746f993a8d 准备加入自动投递功能 2019-01-23 14:50:01 +08:00
4270612a98 更新大部分判断,WinMain加入全部显示参数启动 2019-01-22 21:37:44 +08:00
52a49e5e2d 临时更新,因西瓜视频WEB API变化 2019-01-22 15:22:30 +08:00
4572fd63ea Emeg Update 2019-01-22 15:08:05 +08:00
10754d8ca0 修正部分代码 2019-01-22 00:29:37 +08:00
ef732aa965 增加DEBUG模式 2019-01-22 00:28:25 +08:00
414aef7eb3 修正房间提示信息 2019-01-22 00:21:32 +08:00
e3da5d98b3 增加禁言消息 2019-01-22 00:13:49 +08:00
7 changed files with 684 additions and 53 deletions

34
MemberMsg.py Normal file
View File

@ -0,0 +1,34 @@
from User import User
class MemberMsg:
type:int = 0
content:str = ""
user:User = None
def __init__(self, json=None):
if json:
self.parse(json)
def parse(self, json):
self.user = User(json)
if "Msg" in json:
if "action" in json["Msg"]:
self.type = json["Msg"]['action']
elif "content" in json["Msg"]:
self.content = json["Msg"]['content']
def __str__(self):
if self.type == 3:
return "{} 被禁言了".format(self.user)
elif self.type == 4:
return "{} 被取消禁言了".format(self.user)
elif self.type == 5:
return "{} 被任命为房管".format(self.user)
elif self.type == 1:
return "{} 进入了房间".format(self.user)
else:
return self.content.format(self.user)
def __unicode__(self):
return self.__str__()

8
README.md Normal file
View File

@ -0,0 +1,8 @@
# XiguaLiveDanmakuHelper
### 西瓜直播弹幕助手
因个人能力有限不懂C#,无法做出界面版,只好用控制台版先顶着
### 计划更新:
- 使用android app协议避免出现网页版协议更改后无法使用
- 闲的无聊的时候看一看有没有好用的GUI轮子可以用用

View File

@ -14,15 +14,13 @@ class User:
if "user" in json["Msg"]: if "user" in json["Msg"]:
self.ID = json["Msg"]['user']['user_id'] self.ID = json["Msg"]['user']['user_id']
self.name = json["Msg"]['user']['name'] self.name = json["Msg"]['user']['name']
self.type = json["Msg"]['user']['user_type']
if "discipulus_info" in json["Msg"]: if "discipulus_info" in json["Msg"]:
self.level = json["Msg"]["discipulus_info"]["level"] self.level = json["Msg"]["discipulus_info"]["level"]
self.brand = json["Msg"]["discipulus_info"]["discipulus_group_title"] self.brand = json["Msg"]["discipulus_info"]["discipulus_group_title"]
elif "data" in json: elif "data" in json:
if "anchorInfo" in json["data"]: if "anchorInfo" in json["data"]:
self.ID = json["data"]['anchorInfo']['user_id'] self.ID = json["data"]['anchorInfo']['id']
self.name = json["data"]['anchorInfo']['name'] self.name = json["data"]['anchorInfo']['name']
self.type = 0
if self.type is None: if self.type is None:
self.type = 0 self.type = 0
@ -30,7 +28,7 @@ class User:
if self.level == 0: if self.level == 0:
if self.type == 1: if self.type == 1:
return "[房管]{}".format(self.name) return "[房管]{}".format(self.name)
elif self.type == 2: elif self.type == 3:
return "[主播]{}".format(self.name) return "[主播]{}".format(self.name)
else: else:
return "{}".format(self.name) return "{}".format(self.name)

View File

@ -3,6 +3,7 @@ import sys
import time import time
from Gift import Gift from Gift import Gift
from MemberMsg import MemberMsg
from User import User from User import User
from Chat import Chat from Chat import Chat
@ -10,6 +11,7 @@ from api import XiGuaLiveApi as Api
import msvcrt import msvcrt
import ctypes import ctypes
SHOW_ALL = False
def readInput(caption, default, timeout: int = 5): def readInput(caption, default, timeout: int = 5):
start_time = time.time() start_time = time.time()
@ -116,7 +118,7 @@ class WinMain(Api):
def onMessage(self, msg: str): def onMessage(self, msg: str):
set_cmd_text_color(FOREGROUND_DARKGRAY) set_cmd_text_color(FOREGROUND_DARKGRAY)
print("消息", msg) print("消息 : ", msg)
resetColor() resetColor()
def onJoin(self, user: User): def onJoin(self, user: User):
@ -125,24 +127,26 @@ class WinMain(Api):
resetColor() resetColor()
def onSubscribe(self, user: User): def onSubscribe(self, user: User):
return if SHOW_ALL:
def onEnter(self, user: User, content: str == ""):
if content == "":
if user.name == "三国空白" or user.name == "四维v":
set_cmd_text_color(FOREGROUND_DARKGRAY) set_cmd_text_color(FOREGROUND_DARKGRAY)
print("消息:", user, "进入直播间") print("用户", user, "关注了主播")
resetColor() resetColor()
else:
def onEnter(self, msg:MemberMsg):
if SHOW_ALL:
set_cmd_text_color(FOREGROUND_DARKGRAY) set_cmd_text_color(FOREGROUND_DARKGRAY)
print("消息:", content.format(user)) print("提示 :", msg)
resetColor() resetColor()
def onChat(self, chat: Chat): def onChat(self, chat: Chat):
if SHOW_ALL:
print(chat) print(chat)
def onPresent(self, gift: Gift): def onPresent(self, gift: Gift):
return if SHOW_ALL:
set_cmd_text_color(FOREGROUND_DARKGRAY)
print("连击 :", gift)
resetColor()
def onPresentEnd(self, gift: Gift): def onPresentEnd(self, gift: Gift):
set_cmd_text_color(BACKGROUND_WHITE | FOREGROUND_BLACK) set_cmd_text_color(BACKGROUND_WHITE | FOREGROUND_BLACK)
@ -150,7 +154,10 @@ class WinMain(Api):
resetColor() resetColor()
def onLike(self, user: User): def onLike(self, user: User):
return if SHOW_ALL:
set_cmd_text_color(FOREGROUND_DARKGRAY)
print("用户", user, "点了喜欢")
resetColor()
def onLeave(self, json: any): def onLeave(self, json: any):
return return
@ -167,10 +174,15 @@ if __name__ == "__main__":
resetColor() resetColor()
print("西瓜直播弹幕助手 by JerryYan") print("西瓜直播弹幕助手 by JerryYan")
if len(sys.argv) > 1: if len(sys.argv) > 1:
if sys.argv[-1] == "a":
SHOW_ALL = True
try:
room = int(sys.argv[1]) room = int(sys.argv[1])
except:
pass
else: else:
try: try:
room = int(readInput("请输入房间号,默认为永恒的直播间", room, 3)) room = int(readInput("请输入用户ID号,默认为永恒的ID号", room, 3))
except ValueError: except ValueError:
pass pass
api = WinMain(room) api = WinMain(room)
@ -189,6 +201,8 @@ if __name__ == "__main__":
warning(e) warning(e)
time.sleep(1) time.sleep(1)
else: else:
set_cmd_text_color(FOREGROUND_RED)
print("主播未开播等待1分钟后重试") print("主播未开播等待1分钟后重试")
resetColor()
time.sleep(60) time.sleep(60)
api.updRoomInfo() api.updRoomInfo()

77
api.py
View File

@ -1,4 +1,6 @@
import sys import sys
from MemberMsg import MemberMsg
from User import User from User import User
from Gift import Gift from Gift import Gift
from Chat import Chat from Chat import Chat
@ -7,6 +9,8 @@ import time
s = requests.Session() s = requests.Session()
DEBUG: bool = False
class XiGuaLiveApi: class XiGuaLiveApi:
isLive: bool = False isLive: bool = False
@ -25,22 +29,22 @@ class XiGuaLiveApi:
Gift.update(self.roomID) Gift.update(self.roomID)
self._enterRoom() self._enterRoom()
def notLiveError(self):
print("主播未开播")
def _updateRoomInfo(self, json): def _updateRoomInfo(self, json):
if "Msg" in json: if "Msg" in json:
if "member_count" in json["Msg"]: if "member_count" in json["Msg"]:
self.roomMember = json["Msg"]["member_count"] self.roomMember = json["Msg"]["member_count"]
if "popularity" in json["Msg"]: if "popularity" in json["Msg"]:
self.roomPopularity = json["Msg"]["popularity"] self.roomPopularity = json["Msg"]["popularity"]
elif "data" in json:
if "popularity" in json["data"]:
self.roomPopularity = json["data"]["popularity"]
def apiChangedError(self, msg: str, *args): def apiChangedError(self, msg: str, *args):
print(msg) print(msg)
print(*args) print(*args)
def onPresent(self, gift: Gift): def onPresent(self, gift: Gift):
print("礼物连击", gift) print("礼物连击 : ", gift)
def onPresentEnd(self, gift: Gift): def onPresentEnd(self, gift: Gift):
print("感谢", gift) print("感谢", gift)
@ -52,52 +56,49 @@ class XiGuaLiveApi:
def onChat(self, chat: Chat): def onChat(self, chat: Chat):
print(chat) print(chat)
def onEnter(self, user: User, content: str == ""): def onEnter(self, msg: MemberMsg):
if content == "": print("提示 : ", msg)
print("消息:", user, "进入直播间")
else:
print("消息:", content.format(user))
def onSubscribe(self, user: User): def onSubscribe(self, user: User):
print("消息", user, "关注了主播") print("消息 : ", user, "关注了主播")
def onJoin(self, user: User): def onJoin(self, user: User):
print("感谢", user, "加入了粉丝团") print("感谢", user, "加入了粉丝团")
def onMessage(self, msg: str): def onMessage(self, msg: str):
print("消息", msg) print("消息 : ", msg)
def onLike(self, user: User): def onLike(self, user: User):
print("用户", user, "点了喜欢") print("用户", user, "点了喜欢")
def onLeave(self, json: any): def onLeave(self, json: any):
print("消息", "主播离开一小会") print("消息 : ", "主播离开一小会")
def _enterRoom(self): def _enterRoom(self):
if not self.isValidRoom: if not self.isValidRoom:
return return
p = s.post("https://live.ixigua.com/api/room/enter/{roomID}".format(roomID=self.roomID)) p = s.post("https://live.ixigua.com/api/room/enter/{roomID}".format(roomID=self.roomID))
if DEBUG:
print(p.text)
def updRoomInfo(self): def updRoomInfo(self):
p = s.get("https://live.ixigua.com/api/room/{room}".format(room=self.room)) p = s.get("https://live.ixigua.com/api/room?anchorId={room}".format(room=self.room))
if DEBUG:
print(p.text)
d = p.json() d = p.json()
if "data" not in d: if "data" not in d or "title" not in d["data"] or "id" not in d["data"]:
self.apiChangedError("无法获取RoomID请与我联系") self.apiChangedError("无法获取RoomID请与我联系")
return return
self.isValidRoom = True self.isValidRoom = True
self._rawRoomInfo = d["data"] self._rawRoomInfo = d["data"]
self.roomLiver = User(d) self.roomLiver = User(d)
self.roomTitle = self._rawRoomInfo["Title"] self.roomTitle = d["data"]["title"]
self.roomPopularity = self._rawRoomInfo["Extra2"]["Popularity"] self.roomID = d["data"]["id"]
if "Id" in d["data"]: self._updateRoomInfo(d)
self.roomID = d["data"]["Id"] if "status" in d["data"] and d["data"]["status"] == 2:
else:
self.apiChangedError("无法获取RoomID请与我联系")
if "FinishTime" in d["data"]:
self.isLive = False
self.notLiveError()
else:
self.isLive = True self.isLive = True
else:
self.isLive = False
def getDanmaku(self): def getDanmaku(self):
if not self.isValidRoom: if not self.isValidRoom:
@ -108,20 +109,23 @@ class XiGuaLiveApi:
cursor=self._cursor cursor=self._cursor
)) ))
d = p.json() d = p.json()
if "data" not in d: if "data" not in d or "Extra" not in d["data"] or "Cursor" not in d["data"]["Extra"]:
self.apiChangedError("数据结构改变,请与我联系", d) if DEBUG:
return print(d)
if "Extra" not in d["data"]: self.apiChangedError("数据结构改变,请与我联系")
self.apiChangedError("数据结构改变,请与我联系", d)
return
if "Cursor" not in d["data"]["Extra"]:
self.apiChangedError("数据结构改变,请与我联系", d)
return return
else: else:
self._cursor = d["data"]["Extra"]["Cursor"] self._cursor = d["data"]["Extra"]["Cursor"]
if DEBUG:
print("Cursor", self._cursor)
if "LiveMsgs" not in d["data"]: if "LiveMsgs" not in d["data"]:
self.updRoomInfo()
return return
for i in d['data']['LiveMsgs']: for i in d['data']['LiveMsgs']:
if DEBUG:
print(i)
if "Method" not in i:
continue
if i['Method'] == "VideoLivePresentMessage": if i['Method'] == "VideoLivePresentMessage":
self.onPresent(Gift(i)) self.onPresent(Gift(i))
elif i['Method'] == "VideoLivePresentEndTipMessage": elif i['Method'] == "VideoLivePresentEndTipMessage":
@ -132,7 +136,7 @@ class XiGuaLiveApi:
self.onChat(Chat(i)) self.onChat(Chat(i))
elif i['Method'] == "VideoLiveMemberMessage": elif i['Method'] == "VideoLiveMemberMessage":
self._updateRoomInfo(i) self._updateRoomInfo(i)
self.onEnter(User(i), i["Msg"]["content"]) self.onEnter(MemberMsg(i))
elif i['Method'] == "VideoLiveSocialMessage": elif i['Method'] == "VideoLiveSocialMessage":
self.onSubscribe(User(i)) self.onSubscribe(User(i))
elif i['Method'] == "VideoLiveJoinDiscipulusMessage": elif i['Method'] == "VideoLiveJoinDiscipulusMessage":
@ -149,6 +153,13 @@ if __name__ == "__main__":
room = 97621754276 # 永恒 room = 97621754276 # 永恒
# room = 75366565294 # room = 75366565294
# room = 83940182312 #Dae # room = 83940182312 #Dae
if len(sys.argv) > 1:
if sys.argv[-1] == "d":
DEBUG = True
try:
room = int(sys.argv[1])
except ValueError:
pass
print("西瓜直播弹幕助手 by JerryYan") print("西瓜直播弹幕助手 by JerryYan")
api = XiGuaLiveApi(room) api = XiGuaLiveApi(room)
print("进入", api.roomLiver, "的直播间") print("进入", api.roomLiver, "的直播间")

392
bilibili.py Normal file
View File

@ -0,0 +1,392 @@
# coding=utf-8
import os
import re
from datetime import datetime
import rsa
import math
import base64
import hashlib
import requests
from urllib import parse
class VideoPart:
def __init__(self, path, title='', desc=''):
self.path = path
self.title = title
self.desc = desc
class Bilibili:
def __init__(self, cookie=None):
self.videos = []
self.session = requests.session()
if cookie:
self.session.headers["cookie"] = cookie
self.csrf = re.search('bili_jct=(.*?);', cookie).group(1)
self.mid = re.search('DedeUserID=(.*?);', cookie).group(1)
self.session.headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'
self.session.headers['Referer'] = 'https://space.bilibili.com/{mid}/#!/'.format(mid=self.mid)
# session.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
# session.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
def login(self, user, pwd):
"""
:param user: username
:type user: str
:param pwd: password
:type pwd: str
:return: if success return True
else return msg json
"""
APPKEY = '1d8b6e7d45233436'
ACTIONKEY = 'appkey'
BUILD = 520001
DEVICE = 'android'
MOBI_APP = 'android'
PLATFORM = 'android'
APPSECRET = '560c52ccd288fed045859ed18bffd973'
def md5(s):
h = hashlib.md5()
h.update(s.encode('utf-8'))
return h.hexdigest()
def sign(s):
"""
:return: return sign
"""
return md5(s + APPSECRET)
def signed_body(body):
"""
:return: body which be added sign
"""
if isinstance(body, str):
return body + '&sign=' + sign(body)
elif isinstance(body, dict):
ls = []
for k, v in body.items():
ls.append(k + '=' + v)
body['sign'] = sign('&'.join(ls))
return body
def getkey():
"""
:return: hash, key
"""
r = self.session.post(
'https://passport.bilibili.com/api/oauth2/getKey',
signed_body({'appkey': APPKEY}),
)
# {"ts":1544152439,"code":0,"data":{"hash":"99c7573759582e0b","key":"-----BEGIN PUBLIC----- -----END PUBLIC KEY-----\n"}}
json = r.json()
data = json['data']
return data['hash'], data['key']
def cnn_captcha(img):
url = "http://47.95.255.188:5000/code"
data = {"image": img}
r = requests.post(url, data=data)
return r.text
self.session.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
h, k = getkey()
pwd = base64.b64encode(
rsa.encrypt(
(h + pwd).encode('utf-8'),
rsa.PublicKey.load_pkcs1_openssl_pem(k.encode())
)
)
user = parse.quote_plus(user)
pwd = parse.quote_plus(pwd)
r = self.session.post(
'https://passport.bilibili.com/api/v2/oauth2/login',
signed_body('appkey={appkey}&password={password}&username={username}'
.format(appkey=APPKEY, username=user, password=pwd))
)
try:
json = r.json()
except:
return r.text
if json['code'] == -105:
# need captcha
self.session.headers['cookie'] = 'sid=xxxxxxxx'
r = self.session.get('https://passport.bilibili.com/captcha')
captcha = cnn_captcha(base64.b64encode(r.content))
r = self.session.post(
'https://passport.bilibili.com/api/v2/oauth2/login',
signed_body('actionKey={actionKey}&appkey={appkey}&build={build}&captcha={captcha}&device={device}'
'&mobi_app={mobi_app}&password={password}&platform={platform}&username={username}'
.format(actionKey=ACTIONKEY,
appkey=APPKEY,
build=BUILD,
captcha=captcha,
device=DEVICE,
mobi_app=MOBI_APP,
password=pwd,
platform=PLATFORM,
username=user)),
)
json = r.json()
if json['code'] is not 0:
return r.text
ls = []
for item in json['data']['cookie_info']['cookies']:
ls.append(item['name'] + '=' + item['value'])
cookie = '; '.join(ls)
self.session.headers["cookie"] = cookie
self.csrf = re.search('bili_jct=(.*?);', cookie).group(1)
self.mid = re.search('DedeUserID=(.*?);', cookie).group(1)
self.session.headers['Accept'] = 'application/json, text/javascript, */*; q=0.01'
self.session.headers['Referer'] = 'https://space.bilibili.com/{mid}/#!/'.format(mid=self.mid)
return True
def upload(self,
parts,
title,
tid,
tag,
desc,
source='',
cover='',
no_reprint=1,
):
"""
:param parts: e.g. VideoPart('part path', 'part title', 'part desc'), or [VideoPart(...), VideoPart(...)]
:type parts: VideoPart or list<VideoPart>
:param title: video's title
:type title: str
:param tid: video type, see: https://member.bilibili.com/x/web/archive/pre
or https://github.com/uupers/BiliSpider/wiki/%E8%A7%86%E9%A2%91%E5%88%86%E5%8C%BA%E5%AF%B9%E5%BA%94%E8%A1%A8
:type tid: int
:param tag: video's tag
:type tag: list<str>
:param desc: video's description
:type desc: str
:param source: (optional) 转载地址
:type source: str
:param cover: (optional) cover's URL, use method *cover_up* to get
:type cover: str
:param no_reprint: (optional) 0=可以转载, 1=禁止转载(default)
:type no_reprint: int
"""
self.preUpload(parts)
self.finishUpload(title,tid,tag,desc,source,cover,no_reprint)
def preUpload(self,parts):
"""
:param parts: e.g. VideoPart('part path', 'part title', 'part desc'), or [VideoPart(...), VideoPart(...)]
:type parts: VideoPart or list<VideoPart>
:param title: video's title
:type title: str
:param tid: video type, see: https://member.bilibili.com/x/web/archive/pre
or https://github.com/uupers/BiliSpider/wiki/%E8%A7%86%E9%A2%91%E5%88%86%E5%8C%BA%E5%AF%B9%E5%BA%94%E8%A1%A8
:type tid: int
:param tag: video's tag
:type tag: list<str>
:param desc: video's description
:type desc: str
:param source: (optional) 转载地址
:type source: str
:param cover: (optional) cover's URL, use method *cover_up* to get
:type cover: str
:param no_reprint: (optional) 0=可以转载, 1=禁止转载(default)
:type no_reprint: int
"""
self.session.headers['Content-Type'] = 'application/json; charset=utf-8'
if not isinstance(parts, list):
parts = [parts]
videos = []
for part in parts:
filepath = part.path
filename = os.path.basename(filepath)
filesize = os.path.getsize(filepath)
r = self.session.get('https://member.bilibili.com/preupload?'
'os=upos&upcdn=ws&name={name}&size={size}&r=upos&profile=ugcupos%2Fyb&ssl=0'
.format(name=filename, size=filesize))
"""return example
{
"upos_uri": "upos://ugc/i181012ws18x52mti3gg0h33chn3tyhp.mp4",
"biz_id": 58993125,
"endpoint": "//upos-hz-upcdnws.acgvideo.com",
"endpoints": [
"//upos-hz-upcdnws.acgvideo.com",
"//upos-hz-upcdntx.acgvideo.com"
],
"chunk_retry_delay": 3,
"chunk_retry": 200,
"chunk_size": 4194304,
"threads": 2,
"timeout": 900,
"auth": "os=upos&cdn=upcdnws&uid=&net_state=4&device=&build=&os_version=&ak=×tamp=&sign=",
"OK": 1
}
"""
json = r.json()
upos_uri = json['upos_uri']
endpoint = json['endpoint']
auth = json['auth']
biz_id = json['biz_id']
chunk_size = json['chunk_size']
self.session.headers['X-Upos-Auth'] = auth # add auth header
r = self.session.post(
'https:{}/{}?uploads&output=json'.format(endpoint, upos_uri.replace('upos://', '')))
# {"upload_id":"72eb747b9650b8c7995fdb0efbdc2bb6","key":"\/i181012ws2wg1tb7tjzswk2voxrwlk1u.mp4","OK":1,"bucket":"ugc"}
json = r.json()
upload_id = json['upload_id']
with open(filepath, 'rb') as f:
chunks_num = math.ceil(filesize / chunk_size)
chunks_index = -1
while True:
chunks_data = f.read(chunk_size)
if not chunks_data:
break
chunks_index += 1 # start with 0
r = self.session.put('https:{endpoint}/{upos_uri}?'
'partNumber={part_number}&uploadId={upload_id}&chunk={chunk}&chunks={chunks}&size={size}&start={start}&end={end}&total={total}'
.format(endpoint=endpoint,
upos_uri=upos_uri.replace('upos://', ''),
part_number=chunks_index + 1, # starts with 1
upload_id=upload_id,
chunk=chunks_index,
chunks=chunks_num,
size=len(chunks_data),
start=chunks_index * chunk_size,
end=chunks_index * chunk_size + len(chunks_data),
total=filesize,
),
chunks_data,
)
print('{} : UPLOAD {}/{}'.format(datetime.strftime(datetime.now(), "%y%m%d %H%M"), chunks_index,
chunks_num), r.text)
# NOT DELETE! Refer to https://github.com/comwrg/bilibiliupload/issues/15#issuecomment-424379769
self.session.post('https:{endpoint}/{upos_uri}?'
'output=json&name={name}&profile=ugcupos%2Fyb&uploadId={upload_id}&biz_id={biz_id}'
.format(endpoint=endpoint,
upos_uri=upos_uri.replace('upos://', ''),
name=filename,
upload_id=upload_id,
biz_id=biz_id,
),
{"parts": [{"partNumber": i, "eTag": "etag"} for i in range(1, chunks_num + 1)]},
)
self.videos.append({'filename': upos_uri.replace('upos://ugc/', '').split('.')[0],
'title': part.title,
'desc': part.desc})
def finishUpload(self,
title,
tid,
tag,
desc,
source='',
cover='',
no_reprint=1,
):
copyright = 2 if source else 1
r = self.session.post('https://member.bilibili.com/x/vu/web/add?csrf=' + self.csrf,
json={
"copyright" : copyright,
"source" : source,
"title" : title,
"tid" : tid,
"tag" : ','.join(tag),
"no_reprint": no_reprint,
"desc" : desc,
"cover" : cover,
"mission_id": 0,
"order_id" : 0,
"videos" : self.videos}
)
print(r.text)
for _p in self.videos:
os.remove(_p.path)
def addChannel(self, name, intro=''):
"""
:param name: channel's name
:type name: str
:param intro: channel's introduction
:type intro: str
"""
r = self.session.post(
url='https://space.bilibili.com/ajax/channel/addChannel',
data={
'name' : name,
'intro': intro,
'aids' : '',
'csrf' : self.csrf,
},
# name=123&intro=123&aids=&csrf=565d7ed17cef2cc8ad054210c4e64324&_=1497077610768
)
# return
# {"status":true,"data":{"cid":"15812"}}
print(r.json())
def channel_addVideo(self, cid, aids):
"""
:param cid: channel's id
:type cid: int
:param aids: videos' id
:type aids: list<int>
"""
r = self.session.post(
url='https://space.bilibili.com/ajax/channel/addVideo',
data={
'aids': '%2C'.join(aids),
'cid' : cid,
'csrf': self.csrf
}
# aids=9953555%2C9872953&cid=15814&csrf=565d7ed17cef2cc8ad054210c4e64324&_=1497079332679
)
print(r.json())
def cover_up(self, img):
"""
:param img: img path or stream
:type img: str or BufferedReader
:return: img URL
"""
if isinstance(img, str):
f = open(img, 'rb')
else:
f = img
r = self.session.post(
url='https://member.bilibili.com/x/vu/web/cover/up',
data={
'cover': b'data:image/jpeg;base64,' + (base64.b64encode(f.read())),
'csrf': self.csrf,
}
)
# print(r.text)
# {"code":0,"data":{"url":"http://i0.hdslb.com/bfs/archive/67db4a6eae398c309244e74f6e85ae8d813bd7c9.jpg"},"message":"","ttl":1}
return r.json()['data']['url']

174
liveDownloader.py Normal file
View File

@ -0,0 +1,174 @@
import sys
import time
from datetime import datetime
import m3u8
import queue
import threading
from config import config
from api import XiGuaLiveApi
from bilibili import *
q = queue.Queue()
base_uri = ""
isUpload = False
uq = queue.Queue()
d = datetime.strftime(datetime.now(),"%Y_%m_%d")
class downloader(XiGuaLiveApi):
files = []
playlist: str = None
def updRoomInfo(self):
super(downloader, self).updRoomInfo()
self.updPlayList()
def updPlayList(self):
if "playInfo" not in self._rawRoomInfo or "Main" not in self._rawRoomInfo["playInfo"]:
if self.playlist is None:
self.apiChangedError("无法获取直播链接")
self.playlist = False
else:
self.playlist = self._rawRoomInfo["playInfo"]["Main"]["1"]["Url"]["HlsUrl"]
def onLike(self, user):
pass
def onAd(self, i):
pass
def onChat(self, chat):
pass
def onEnter(self, msg):
pass
def onJoin(self, user):
pass
def onLeave(self, json):
self.updRoomInfo()
def onMessage(self, msg):
pass
def onPresent(self, gift):
pass
def onPresentEnd(self, gift):
pass
def onSubscribe(self, user):
pass
def preDownload(self):
global base_uri
if self.playlist:
try:
p = m3u8.load(self.playlist)
except:
self.updRoomInfo()
return
base_uri = p.base_uri
for i in p.files:
if i not in self.files:
self.files.append(i)
print("{} : Add Sequence {}".format(datetime.strftime(datetime.now(), "%y%m%d %H%M"),
len(self.files)))
q.put(i)
self.genNewName()
def genNewName(self):
if len(self.files) > 800:
q.put(True)
self.files.clear()
def download(path=datetime.strftime(datetime.now(),"%Y%m%d_%H%M.ts")):
global isUpload
print("{} : Download Daemon Starting".format(datetime.strftime(datetime.now(), "%y%m%d %H%M")))
n = False
isUpload = False
i = q.get()
while True:
if isinstance(i, bool):
print("{} : Download Daemon Receive Command {}".format(datetime.strftime(datetime.now(), "%y%m%d %H%M"), i))
break
print("{} : Download {}".format(datetime.strftime(datetime.now(), "%y%m%d %H%M"), i))
try:
_p = requests.get("{}{}".format(base_uri,i))
except:
continue
f = open(path, "ab")
f.write(_p.content)
f.close()
n=True
i = q.get()
if n:
uq.put(path)
print("{} : Download Daemon Quiting".format(datetime.strftime(datetime.now(), "%y%m%d %H%M")))
isUpload = True
def upload(date = datetime.strftime(datetime.now(), "%Y_%m_%d")):
print("{} : Upload Daemon Starting".format(datetime.strftime(datetime.now(), "%y%m%d %H%M")))
i = uq.get()
while True:
if isinstance(i, bool):
if i is True:
print("自动投稿中,请稍后")
b.finishUpload(config["t_t"].format(date),17, config["tag"],config["des"],
source= "https://live.ixigua.com/userlive/97621754276", no_reprint= 0)
print("{} : Upload Daemon Receive Command {}".format(datetime.strftime(datetime.now(), "%y%m%d %H%M"), i))
break
print("{} : Upload {}".format(datetime.strftime(datetime.now(), "%y%m%d %H%M"), i))
try:
b.preUpload(VideoPart(i, i))
except:
continue
i = uq.get()
print("{} : Upload Daemon Quiting".format(datetime.strftime(datetime.now(), "%y%m%d %H%M")))
b = Bilibili()
b.login(config["b_u"], config["b_p"])
if __name__ == "__main__":
room = 97621754276 # 永恒
# room = 75366565294
# room = 83940182312 #Dae
# room = 5947850784 #⑦
# room = 58649240617 #戏
if len(sys.argv) > 1:
try:
room = int(sys.argv[1])
except ValueError:
pass
print("西瓜直播录播助手 by JerryYan")
api = downloader(room)
print("进入", api.roomLiver, "的直播间")
if not api.isValidRoom:
input("房间不存在")
sys.exit()
print("=" * 30)
_preT = datetime.strftime(datetime.now(), "%Y%m%d_%H%M.ts")
t = threading.Thread(target=download, args=(_preT,))
ut = threading.Thread(target=upload, args=(d,))
while True:
if api.isLive:
if d is None:
d = datetime.strftime(datetime.now(), "%Y_%m_%d")
if not t.is_alive():
_preT = datetime.strftime(datetime.now(), "%Y%m%d_%H%M.ts")
t = threading.Thread(target=download, args=(_preT,))
t.setDaemon(True)
t.start()
if not ut.is_alive():
ut = threading.Thread(target=upload, args=(d,))
ut.setDaemon(True)
ut.start()
try:
api.preDownload()
except:
pass
time.sleep(3)
else:
q.put(False)
if isUpload:
uq.put(True)
isUpload = False
else:
pass
# print("主播未开播等待1分钟后重试")
time.sleep(60)
d=None
api.updRoomInfo()