This repository has been archived on 2022-05-30. You can view files and clone it, but cannot push or open issues or pull requests.

434 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import queue
from datetime import datetime, timedelta
import psutil
from api import XiGuaLiveApi
import json
import threading
from bilibili import Bilibili, VideoPart
# 默认设置
config = {
# 录像的主播ID
"l_u": "97621754276",
# 标题及预留时间位置
"t_t": "【永恒de草薙直播录播】直播于 {}",
# 标签
"tag": ["永恒de草薙", "三国", "三国战记", "直播录像", "录播", "怀旧", "街机"],
# 描述
"des": "西瓜直播 https://live.ixigua.com/userlive/97621754276 \n自动投递\n原主播永恒de草薙\n直播时间晚上6点多到凌晨4点左右",
# 来源, 空则为自制
"src": "",
# Log条数
"l_c": 5,
# 错误Log条数
"elc": 10,
# 每一chunk大小
"c_s": 16 * 1024,
# 每一块视频大小
"p_s": 2141000000,
# 忽略的大小
"i_s": 2048000,
"max": 75,
"dow": "echo 'clean'",
# 仅下载
"dlO": True,
# 下播延迟投稿
"dly": 30,
# 短的时间的格式
"sdf": "%Y%m%d",
"enc": "ffmpeg -i {f} -c:v copy -c:a copy -f mp4 {t} -y"
}
doCleanTime = datetime.fromtimestamp(0)
loginTime = datetime.fromtimestamp(0)
_clean_flag = None
delay = datetime.fromtimestamp(0)
b = Bilibili()
network = [{
"currentTime": datetime.now(),
"out": {
"currentByte": psutil.net_io_counters().bytes_sent,
},
"in": {
"currentByte": psutil.net_io_counters().bytes_recv,
}
}, {
"currentTime": datetime.now(),
"out": {
"currentByte": psutil.net_io_counters().bytes_sent,
},
"in": {
"currentByte": psutil.net_io_counters().bytes_recv,
}
}]
def reloadConfig():
global config
if os.path.exists('config.json'):
_config_fp = open("config.json", "r", encoding="utf8")
_config = json.load(_config_fp)
config.update(_config)
_config_fp.close()
def resetDelay():
global delay
delay = datetime.now() + timedelta(minutes=int(config['dly']))
def doDelay():
global delay
if -60 < getTimeDelta(datetime.now(), delay) < 60:
delay = datetime.fromtimestamp(0)
return True
return False
def updateNetwork():
global network
network.append({
"currentTime": datetime.now(),
"out": {
"currentByte": psutil.net_io_counters().bytes_sent,
},
"in": {
"currentByte": psutil.net_io_counters().bytes_recv,
}
})
network = network[-3:]
def getTimeDelta(a, b):
return (a - b).total_seconds()
def _doClean(_force=False):
global doCleanTime, _clean_flag
_disk = psutil.disk_usage(".")
if _disk.percent > config["max"] or _force:
_clean_flag = True
doCleanTime = datetime.now()
appendOperation("执行配置的清理命令")
os.system(config["dow"])
appendOperation("执行配置的清理命令完毕")
doCleanTime = datetime.now()
_clean_flag = False
def doClean(_force=False):
if _clean_flag:
return
p = threading.Thread(target=_doClean, args=(_force,))
p.setDaemon(True)
p.start()
def getCurrentStatus():
_disk = psutil.disk_usage(".")
_mem = psutil.virtual_memory()
_net = psutil.net_io_counters()
_delta = getTimeDelta(network[-1]["currentTime"], network[-2]["currentTime"])
if 60 > _delta > 1:
_inSpeed = (network[-1]["in"]["currentByte"] - network[-2]["in"]["currentByte"]) / _delta
_outSpeed = (network[-1]["out"]["currentByte"] - network[-2]["out"]["currentByte"]) / _delta
else:
_outSpeed = (network[-1]["in"]["currentByte"] - network[-2]["in"]["currentByte"])
_inSpeed = (network[-1]["out"]["currentByte"] - network[-2]["out"]["currentByte"])
updateNetwork()
return {
"memTotal": parseSize(_mem.total),
"memUsed": parseSize(_mem.used),
"memUsage": _mem.percent,
"diskTotal": parseSize(_disk.total),
"diskUsed": parseSize(_disk.used),
"diskUsage": _disk.percent,
"cpu": psutil.cpu_percent(),
"outSpeed": parseSize(_outSpeed),
"inSpeed": parseSize(_inSpeed),
"doCleanTime": datetime.strftime(doCleanTime, dt_format),
}
dt_format = "%Y/%m/%d %H:%M:%S"
reloadConfig()
broadcaster = ""
streamUrl = ""
forceNotDownload = False
forceNotBroadcasting = False
forceNotUpload = False
forceNotEncode = False
if config["dlO"] is True:
forceNotUpload = True
forceNotEncode = True
forceStartEncodeThread = False
forceStartUploadThread = False
uploadQueue = queue.Queue()
encodeQueue = queue.Queue()
uploadStatus = []
downloadStatus = []
encodeStatus = []
errors = []
operations = []
def appendOperation(obj):
global operations
if isinstance(obj, dict):
if "datetime" not in obj:
obj["datetime"] = datetime.strftime(datetime.now(), dt_format)
operations.append(obj)
else:
operations.append({
"datetime": datetime.strftime(datetime.now(), dt_format),
"message": str(obj)
})
operations = operations[-config["elc"]:]
def parseSize(size):
K = size / 1024.0
if K > 1000:
M = K / 1024.0
if M > 1000:
return "{:.2f}GB".format(M / 1024.0)
else:
return "{:.2f}MB".format(M)
else:
return "{:.2f}KB".format(K)
def appendUploadStatus(obj):
global uploadStatus
if isinstance(obj, dict):
if "datetime" not in obj:
obj["datetime"] = datetime.strftime(datetime.now(), dt_format)
uploadStatus.append(obj)
else:
uploadStatus.append({
"datetime": datetime.strftime(datetime.now(), dt_format),
"message": str(obj)
})
uploadStatus = uploadStatus[-config["l_c"]:]
def modifyLastUploadStatus(obj):
global uploadStatus
if isinstance(obj, dict):
if "datetime" not in obj:
obj["datetime"] = datetime.strftime(datetime.now(), dt_format)
uploadStatus[-1] = obj
else:
uploadStatus[-1]["message"] = str(obj)
uploadStatus[-1]["datetime"] = datetime.strftime(datetime.now(), dt_format)
def appendEncodeStatus(obj):
global encodeStatus
if isinstance(obj, dict):
if "datetime" not in obj:
obj["datetime"] = datetime.strftime(datetime.now(), dt_format)
encodeStatus.append(obj)
else:
encodeStatus.append({
"datetime": datetime.strftime(datetime.now(), dt_format),
"message": str(obj)
})
encodeStatus = encodeStatus[-config["l_c"]:]
def modifyLastEncodeStatus(obj):
global encodeStatus
if isinstance(obj, dict):
if "datetime" not in obj:
obj["datetime"] = datetime.strftime(datetime.now(), dt_format)
encodeStatus[-1] = obj
else:
encodeStatus[-1]["message"] = str(obj)
encodeStatus[-1]["datetime"] = datetime.strftime(datetime.now(), dt_format)
def appendDownloadStatus(obj):
global downloadStatus
if isinstance(obj, dict):
if "datetime" not in obj:
obj["datetime"] = datetime.strftime(datetime.now(), dt_format)
downloadStatus.append(obj)
else:
downloadStatus.append({
"datetime": datetime.strftime(datetime.now(), dt_format),
"message": str(obj)
})
downloadStatus = downloadStatus[-config["l_c"]:]
def modifyLastDownloadStatus(obj):
global downloadStatus
if isinstance(obj, dict):
if "datetime" not in obj:
obj["datetime"] = datetime.strftime(datetime.now(), dt_format)
downloadStatus[-1] = obj
else:
downloadStatus[-1]["message"] = str(obj)
downloadStatus[-1]["datetime"] = datetime.strftime(datetime.now(), dt_format)
def appendError(obj):
global errors
if isinstance(obj, dict):
if "datetime" not in obj:
obj["datetime"] = datetime.strftime(datetime.now(), dt_format)
errors.append(obj)
else:
errors.append({
"datetime": datetime.strftime(datetime.now(), dt_format),
"message": str(obj)
})
errors = errors[-config["elc"]:]
def loginBilibili(force=False):
if config["dlO"] is False or forceNotUpload is False:
global loginTime
global b
if getTimeDelta(datetime.now(), loginTime) < 86400 * 10 and not force:
return False
try:
b.login()
loginTime = datetime.now()
return True
except Exception as e:
appendError(e)
appendOperation("登录失败")
return False
else:
appendOperation("设置了不上传,所以不会登陆")
class downloader(XiGuaLiveApi):
__playlist = None
@property
def playlist(self):
return self.__playlist
@playlist.setter
def playlist(self, value):
global streamUrl
self.__playlist = value
streamUrl = value
def _checkUsernameIsMatched(self, compare=None):
return True
def updRoomInfo(self, force=False):
global broadcaster
_prev_status = self.isLive
doClean()
_result = super(downloader, self).updRoomInfo(force)
if _prev_status != self.isLive and not self.isLive:
resetDelay()
broadcaster = self.broadcaster
if _result:
if self.isLive:
self.updPlayList()
else:
self.playlist = False
return _result
def updPlayList(self):
if self.isLive and "stream_url" in self._rawRoomInfo:
if 'rtmp_pull_url' in self._rawRoomInfo["stream_url"]:
self.playlist = self._rawRoomInfo["stream_url"]['rtmp_pull_url']
elif 'flv_pull_url' in self._rawRoomInfo["stream_url"]:
_playlist = self._rawRoomInfo["stream_url"]["flv_pull_url"]
if type(_playlist) is dict:
for _ in _playlist.values():
self.playlist = _
break
self.playlist = self.playlist.replace("_hd5", "").replace("_sd5", "").replace("_ld5", "").replace("_md", "")
else:
self.playlist = None
api = downloader(config["l_u"])
def refreshDownloader():
global api
api = downloader(config["l_u"])
def uploadVideo(name):
if not os.path.exists(name):
appendError("Upload File Not Exist {}".format(name))
return
loginBilibili()
doClean()
if forceNotUpload is False:
b.preUpload(VideoPart(title=name, path=os.path.basename(name)))
else:
appendUploadStatus("设置了不上传,所以[{}]不会上传了".format(name))
if not forceNotEncode:
os.remove(name)
def publishVideo(date):
if forceNotUpload is False:
b.finishUpload(config["t_t"].format(date), 17, config["tag"], config["des"],
source=config["src"], no_reprint=0)
b.clear()
else:
appendUploadStatus("设置了不上传,所以[{}]的录播不会投了".format(date))
def encodeVideo(name):
if forceNotEncode:
appendEncodeStatus("设置了不编码,所以[{}]不会编码".format(name))
return False
if not os.path.exists(name):
appendEncodeStatus("文件[{}]不存在".format(name))
return False
if os.path.getsize(name) < 8 * 1024 * 1024:
appendEncodeStatus("Encoded File >{}< is too small, will ignore it".format(name))
return False
appendEncodeStatus("Encoding >{}< Start".format(name))
_new_name = os.path.splitext(name)[0] + ".mp4"
_code = os.system(config["enc"].format(f=name, t=_new_name))
if _code != 0:
appendError("Encode {} with Non-Zero Return.".format(name))
return False
modifyLastEncodeStatus("Encode >{}< Finished".format(name))
uploadQueue.put(_new_name)
def collectInfomation():
return {
"download": downloadStatus,
"encode": encodeStatus,
"encodeQueueSize": encodeQueue.qsize(),
"upload": uploadStatus,
"uploadQueueSize": uploadQueue.qsize(),
"error": errors,
"operation": operations,
"broadcast": {
"broadcaster": broadcaster.__str__(),
"isBroadcasting": api.isLive,
"streamUrl": streamUrl,
"updateTime": api.updateAt.strftime(dt_format),
"delayTime": delay.strftime(dt_format)
},
"config": {
"forceNotBroadcasting": forceNotBroadcasting,
"forceNotDownload": forceNotDownload,
"forceNotUpload": forceNotUpload,
"forceNotEncode": forceNotEncode,
"downloadOnly": config['dlO'],
},
}