From 3b9a78cb3559f4dc488682049d68cda7d693ab47 Mon Sep 17 00:00:00 2001 From: 636f4f3c21b3224591e96573 <636f4f3c21b3224591e96573@devide> Date: Fri, 14 Apr 2023 14:36:53 +0000 Subject: [PATCH] UPDATE --- URL_config.ini | 1 + bp.py | 4 + config.ini | 12 + ...0\345\275\225\346\227\245\345\277\227.log" | 0 ...5\345\277\227\346\226\207\344\273\266.log" | 1 + main.py | 2 +- requirements.txt | 2 + sou.py | 1155 +++++++++++++++++ 8 files changed, 1176 insertions(+), 1 deletion(-) create mode 100644 URL_config.ini create mode 100644 bp.py create mode 100644 config.ini create mode 100644 "log/\345\264\251\346\272\203\350\256\260\345\275\225\346\227\245\345\277\227.log" create mode 100644 "log/\347\233\264\346\222\255\346\227\245\345\277\227\346\226\207\344\273\266.log" create mode 100644 sou.py diff --git a/URL_config.ini b/URL_config.ini new file mode 100644 index 0000000..21baaef --- /dev/null +++ b/URL_config.ini @@ -0,0 +1 @@ +https://live.douyin.com/40461216038 \ No newline at end of file diff --git a/bp.py b/bp.py new file mode 100644 index 0000000..7118e64 --- /dev/null +++ b/bp.py @@ -0,0 +1,4 @@ +import os +import sys + +os.execv(sys.executable, ['python'] + sys.argv) \ No newline at end of file diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..da4934c --- /dev/null +++ b/config.ini @@ -0,0 +1,12 @@ +[1] +循环时间(秒) = 60 +直播保存路径 = +视频保存格式ts|flv|mp4|mp3 = TS +是否显示直播地址 = 否 +是否显示循环秒数 = 否 +mp4格式是否转码h264 = 否 +本地代理端口 = +ts格式分段录制是否开启 = 否 +视频分段大小(兆) = 1000 +ts录制完成后自动增加生成mp4格式 = 否 + diff --git "a/log/\345\264\251\346\272\203\350\256\260\345\275\225\346\227\245\345\277\227.log" "b/log/\345\264\251\346\272\203\350\256\260\345\275\225\346\227\245\345\277\227.log" new file mode 100644 index 0000000..e69de29 diff --git "a/log/\347\233\264\346\222\255\346\227\245\345\277\227\346\226\207\344\273\266.log" "b/log/\347\233\264\346\222\255\346\227\245\345\277\227\346\226\207\344\273\266.log" new file mode 100644 index 0000000..f273a3e --- /dev/null +++ "b/log/\347\233\264\346\222\255\346\227\245\345\277\227\346\226\207\344\273\266.log" @@ -0,0 +1 @@ +2023-04-14 14:26:34,719 - ------------------------------------------------------ diff --git a/main.py b/main.py index 4c0c135..fe76f58 100644 --- a/main.py +++ b/main.py @@ -1 +1 @@ -print('欢迎来到 InsCode') \ No newline at end of file +print('欢迎来到 InsCode'python \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e69de29..ed57998 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,2 @@ +beautifulsoup4==4.11.1 +requests==2.24.0 diff --git a/sou.py b/sou.py new file mode 100644 index 0000000..9c041af --- /dev/null +++ b/sou.py @@ -0,0 +1,1155 @@ +#-*- coding: utf-8 -*- +import random +import requests +import re +import os +import urllib.request +import urllib.parse +import sys +from bs4 import BeautifulSoup +import time +import json +import configparser +import subprocess +import logging +import threading +import string +import jsonpath + +version=221008 #请注意把下面这个地方和文件名改了就行啦 +#pyinstaller -F 抖音直播录制_221008.py + + +class Logger(object): + def __init__(self, stream=sys.stdout): + output_dir = "log" + if not os.path.exists(output_dir): + os.makedirs(output_dir) + log_name = "崩溃记录日志.log" + filename = os.path.join(output_dir, log_name) + self.terminal = stream + self.log = open(filename, 'w',encoding="utf-8-sig") + + def write(self, message): + self.terminal.write(message) + self.log.write(message) + def flush(self): + pass + +sys.stdout = Logger(sys.stdout) # 将输出记录到log +sys.stderr = Logger(sys.stderr) # 将错误信息记录到log +# 创建一个logger +logger = logging.getLogger('抖音直播录制%s版'%str(version)) +logger.setLevel(logging.INFO) +# 创建一个handler,用于写入日志文件 +if not os.path.exists("log"): + os.makedirs("log") +fh = logging.FileHandler("log/直播日志文件.log",encoding="utf-8-sig",mode="a") +fh.setLevel(logging.WARNING) +# 再创建一个handler,用于输出到控制台 +# ch = logging.StreamHandler() +# ch.setLevel(logging.INFO) +# formatter = logging.Formatter() +# ch.setFormatter(formatter) +# 定义handler的输出格式 +formatter = logging.Formatter('%(asctime)s - %(message)s') +fh.setFormatter(formatter) +#ch.setFormatter(formatter) +# 给logger添加handler +logger.addHandler(fh) +# logger.addHandler(ch) +# socket.setdefaulttimeout(10) + + +recording=set() +unrecording=set() + +logger.warning("------------------------------------------------------") #分割线 + +def updateFile(file,old_str,new_str): + file_data = "" + with open(file, "r", encoding="utf-8-sig") as f: + for line in f: + if old_str in line: + line = line.replace(old_str,new_str) + file_data += line + with open(file,"w",encoding="utf-8-sig") as f: + f.write(file_data) + +def get_xx(): + string.ascii_letters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + return random.choice(string.ascii_lowercase) + +def get_dx(): + string.ascii_letters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + return random.choice(string.ascii_uppercase) + +def subwords(words): + words=re.sub('[-? * : " < > / | .]', '', words) + words=re.sub(r'\\', '', words) + return words + +def convertsmp4(address): + if tsconvertmp4: + _output = subprocess.check_output([ + "ffmpeg", "-i",address, + "-c:v","copy", + "-f","mp4",address+".mp4", + ], stderr = subprocess.STDOUT) + #print("转换到mp4") + +def get_roomid(html): + ''' + js = re.findall(r"", html)[0] + ret = json.loads(js.replace("window.__INIT_PROPS__ = ","")) + return ret["/webcast/reflow/:id"]["room"]["owner"]["own_room"]["room_ids_str"][0] + ''' + ret =json.loads(html) + + return ret["data"]["room"]["owner"]["own_room"]["room_ids_str"][0] + +def get_status(html): + js = re.findall(r"", html)[0] + ret = json.loads(js.replace("window.__INIT_PROPS__ = ","")) + return ret["/webcast/reflow/:id"]["room"]["status"] + + +def get_real_url(rid,startname,changestaute): + global recording + try: + #print('开始获取真实地址:') + Modelheaders = { + 'User-Agent':'Mozilla/5.0 (Linux; U; Android 8.1.0; en-US; Nexus 6P Build/OPM7.181205.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.11.1.1197 Mobile Safari/537.36' + } + + if len(proxies2)>0: + res=requests.get(rid,headers=Modelheaders,proxies=proxies2,timeout=15) + else: + res=requests.get(rid,headers=Modelheaders,timeout=15) + + roomid = ((res.url).split('/')[-1]).split('?')[0] + # jsurl = "https://webcast.amemv.com/webcast/room/reflow/info/?type_id=0&live_id=1&room_id=" + roomid + "&app_id=1128" + jsurl = "https://webcast.amemv.com/webcast/room/reflow/info/?verifyFp=verify_l7u0qa4v_lx77IkKY_kSHg_411A_9JqE_9h6LMXjaPvb" + get_dx() + "&type_id=0&live_id=1&room_id=" + roomid + "&sec_user_id=&app_id=1128&msToken=HZq7LCnGU_J6-_ZIP8z7ugoxa3TO4kiefFKCiLe139sbMkgLan0uEVijUk8B7aBNseUuwGfvGToHXv7VgGGI4ELJPWBedkZZdZtq9se_dK2gQ_dP-Rw" + get_xx() + "&X-Bogus=DFSzswVOye0ANHQbSKxn6VXAIQ5" + get_xx() + + if len(proxies2) > 0: + resjs = requests.get(jsurl, headers=headers, proxies=proxies2, timeout=15) + else: + resjs = requests.get(jsurl, headers=headers, timeout=15) + #抖音状态码参考: + #room_status = str(get_status(res.text)) + # print("直播间状态: "+room_status) + # if room_status=="2": + # pass + # if changestaute!=room_status: + # changestaute=room_status + # logger.warning("直播间正在直播") + # else: + # print("直播间正在直播") + + # elif room_status=="4": + # pass + + # if changestaute!=room_status: + # changestaute=room_status + # logger.warning("直播间直播已结束,正在等待下次开播") + # else: + # print("直播间直播已结束,正在等待下次开播") + # elif room_status=="1": + # pass + + # if changestaute!=room_status: + # changestaute=room_status + # logger.warning("直播间未直播") + # else: + # print("直播间未直播") + # else: + + # if changestaute!=room_status: + # changestaute=room_status + # logger.warning("未知状态.直播间状态返回码: "+room_status) + # else: + # print("未知状态.直播间状态返回码: "+room_status) + + + try: + room_ids_str = get_roomid(resjs.text) + changestautenow=True + except: + changestautenow=False + + #nowdate=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + nowdate=time.strftime("%H:%M:%S", time.localtime()) + if changestaute!=changestautenow: + changestaute=changestautenow + if changestautenow==True: + logger.warning(startname+" 正在直播中 ") + print("\r"+startname+" 正在直播 " + nowdate) + if startname in unrecording: + unrecording.add(startname) + else: + + logger.warning(startname+" 直播间未直播,等待开播中.. ") + #print(startname+" 直播间未直播,等待开播中.. "+nowdate) + if startname in recording: + recording.remove(startname) + if startname not in unrecording: + unrecording.add(startname) + + else: + if changestautenow==True: + print("\r"+startname+" 正在直播中 " +nowdate) + else: + #print(startname+" 直播间未直播,等待开播中.. "+nowdate) + if startname in recording: + recording.remove(startname) + if startname not in unrecording: + unrecording.add(startname) + + + #room_url = 'https://webcast.amemv.com/webcast/reflow/{}'.format(room_ids_str)+"?u_code=70baflkg&app=aweme&utm_campaign=client_share&utm_medium=ios&tt_from=copy&utm_source=copy" + # room_url = "https://webcast.amemv.com/webcast/room/reflow/info/?type_id=0&live_id=1&room_id=" + room_ids_str + "&app_id=1128" + room_url = "https://webcast.amemv.com/webcast/room/reflow/info/?verifyFp=verify_l7u0qa4v_lx77IkKY_kSHg_411A_9JqE_9h6LMXjaPvb" + get_dx() + "&type_id=0&live_id=1&room_id=" + room_ids_str + "&sec_user_id=&app_id=1128&msToken=HZq7LCnGU_J6-_ZIP8z7ugoxa3TO4kiefFKCiLe139sbMkgLan0uEVijUk8B7aBNseUuwGfvGToHXv7VgGGI4ELJPWBedkZZdZtq9se_dK2gQ_dP-Rw" + get_xx() + "&X-Bogus=DFSzswVOye0ANHQbSKxn6VXAIQ5" + get_xx() + + Modelheaders = { + 'User-Agent':'Mozilla/5.0 (Linux; U; Android 8.1.0; en-US; Nexus 6P Build/OPM7.181205.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.11.1.1197 Mobile Safari/537.36' + } + + if len(proxies2)>0: + res=requests.get(room_url,headers=Modelheaders,proxies=proxies2,timeout=15) + else: + res=requests.get(room_url,headers=Modelheaders,timeout=15) + json_res = json.loads(res.text) + changestaute = jsonpath.jsonpath(json_res, '$.data.room.status')[0] + hls_pull_url = jsonpath.jsonpath(json_res, '$.data.room.stream_url.hls_pull_url_map')[0] + hls_pull_url2 = list(hls_pull_url.values())[0] + # print(hls_pull_url2) + real_url = [hls_pull_url2, changestaute] + return real_url + except Exception as _e: + real_url = [None,changestaute] + return real_url + + +headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; U; Android 8.1.0; en-US; Nexus 6P Build/OPM7.181205.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.11.1.1197 Mobile Safari/537.36' +} + +print( '-------------- 吾爱破解论坛 程序当前配置----------------' ) +print("循环值守录制抖音直播 版本:%s"%str(version)) + +try: + f =open("config.ini",'r', encoding='utf-8-sig') + f.close() + +except IOError: + f = open("config.ini",'w', encoding='utf-8-sig') + f.close() + + +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + rid=config.get('1', '直播地址') +except: + rid="" + + +if os.path.isfile("URL_config.ini"): + f =open("URL_config.ini",'r', encoding='utf-8-sig') + inicontent=f.read() + f.close() +else: + inicontent="" + + + +if len(inicontent)==0: + print('请输入要录制的抖音主播分享网址,例如: https://v.douyin.com/EQBYoH/:') + inurl=input() + f = open("URL_config.ini",'a+',encoding='utf-8-sig') + f.write(inurl) + f.close() + + config = configparser.ConfigParser() + + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', '直播地址')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + #config.set('1', '直播地址', inurl)# 给type分组设置值 + config.set('1', '循环时间(秒)', '60')# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + + + +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + delaydefault=config.getint('1', '循环时间(秒)') +except: + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', '循环时间(秒)')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + config.set('1', '循环时间(秒)', '60')# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + delaydefault=4 + + +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + videopath=config.get('1', '直播保存路径') +except: + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + else: + config.remove_option('1', '直播保存路径')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + config.set('1', '直播保存路径', '')# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + videopath="" + +if len(videopath)>0: + if not os.path.exists(videopath): + print("配置文件里,直播保存路径并不存在,请重新输入一个正确的路径.或留空表示当前目录,按回车退出") + input("程序结束") + os._exit(0) + else: + print("视频保存路径: "+videopath) +else: + print("视频保存路径: 当前目录") + + + +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + videosavetype=config.get('1', '视频保存格式TS|FLV|MP4|MP3') + +except Exception as _e: + + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', '视频保存格式TS|FLV|MP4|MP3')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + + config.set('1', '视频保存格式TS|FLV|MP4|MP3',"TS")# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + videosavetype="TS" + + +#是否显示直播地址 +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + videom3u8=config.get('1', '是否显示直播地址') + +except Exception as _e: + + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', '是否显示直播地址')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + + config.set('1', '是否显示直播地址',"否")# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + videom3u8="否" + + +if videom3u8=="是": + videom3u8=True +else: + videom3u8=False + + +#是否显示循环秒数 +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + looptime=config.get('1', '是否显示循环秒数') + +except Exception as _e: + + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', '是否显示循环秒数')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + + config.set('1', '是否显示循环秒数',"否")# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + looptime="否" + + +if looptime=="是": + looptime=True +else: + looptime=False + + + +#这里是控制MP4是否转码 +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + videoencode=config.get('1', 'MP4格式是否转码H264') + +except Exception as _e: + + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', 'MP4格式是否转码H264')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + + config.set('1', 'MP4格式是否转码H264',"否")# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + videoencode="否" + + +if videoencode=="是": + videoencode=True +else: + videoencode=False + + + + + +#这里是控制是否设置代理 +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + proxies2=config.get('1', '本地代理端口') + proxiesn=proxies2 + if len(proxies2)>0: + + proxies2={'https': 'http://127.0.0.1:'+str(proxies2)} + print("检测到有设置代理地址为: "+'http://127.0.0.1:'+str(proxiesn)) + +except Exception as _e: + + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', '本地代理端口')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + + config.set('1', '本地代理端口',"")# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + + proxies2="" + + +#这里是控制TS是否分段 +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + Splitvideobysize=config.get('1', 'TS格式分段录制是否开启') + +except Exception as _e: + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', 'TS格式分段录制是否开启')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + + config.set('1', 'TS格式分段录制是否开启',"否")# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + Splitvideobysize="否" + + +if Splitvideobysize=="是": + Splitvideobysize=True +else: + Splitvideobysize=False + + + + +#这里是控制TS分段大小 + +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + Splitsize=config.getint('1', '视频分段大小(兆)') +except: + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + else: + config.remove_option('1', '视频分段大小(兆)')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + config.set('1', '视频分段大小(兆)', '1000')# 给type分组设置值 + o = open('config.ini', 'w',encoding='utf-8-sig') + config.write(o) + o.close()#不要忘记关闭 + Splitsize=1000 #1g + +#分段大小不能小于5m +if Splitsize<5: + Splitsize=5 #最低不能小于5m + +Splitsizes=Splitsize*1024*1024 #分割视频大小,转换为字节 + + +if Splitvideobysize: + print("TS录制分段开启,录制分段大小为 %d M"%Splitsize) + + + + +#这里是控制TS是否追加mp4格式 +try: + config = configparser.ConfigParser() + config.read('config.ini', encoding='utf-8-sig') + tsconvertmp4=config.get('1', 'TS录制完成后自动增加生成MP4格式') + +except Exception as _e: + config = configparser.ConfigParser() + # -read读取ini文件 + config.read('config.ini', encoding='utf-8-sig') + listx = [] + listx = config.sections()# 获取到配置文件中所有分组名称 + if '1' not in listx:# 如果分组type不存在则插入type分组 + config.add_section('1') + + else: + config.remove_option('1', 'TS录制完成后自动增加生成MP4格式')# 删除type分组的stuno + # config.remove_section('tpye')# 删除配置文件中type分组 + + + config.set('1', 'TS录制完成后自动增加生成MP4格式',"否")# 给type分组设置值 + + o = open('config.ini', 'w',encoding='utf-8-sig') + + config.write(o) + + o.close()#不要忘记关闭 + tsconvertmp4="否" + + +if tsconvertmp4=="是": + tsconvertmp4=True +else: + tsconvertmp4=False + + + +if len(videosavetype)>0: + if videosavetype.upper()=="FLV": + videosavetype="FLV" + print("直播视频保存为FLV格式") + elif videosavetype.upper()=="TS": + videosavetype="TS" + print("直播视频保存为TS格式") + elif videosavetype.upper()=="MP4": + videosavetype="MP4" + print("直播视频保存为MP4格式") + elif videosavetype.upper()=="MP3": + videosavetype="MP3" + print("直播视频保存为MP3音频格式") + + else: + videosavetype="TS" + print("直播视频保存格式设置有问题,这次录制重置为默认的TS格式") +else: + videosavetype="TS" + print("直播视频保存为TS格式") + +if videoencode==True and videosavetype=="MP4": + print("转码设置:MP4实时转码H264") + +if videoencode==False and videosavetype=="MP4": + print("转码设置:MP4不进行转码") + + + + + + + +allLive=[] #全部的直播 +allRecordingUrl=[] +print( '......................................................' ) + +def startgo(line): + global allLive + recordfinish=False + recordfinish_2=False + counttime=time.time() + global videopath + + ridcontent = line.split(',') + rid=ridcontent[0] + if len(ridcontent)>1: + print("传入地址: "+ridcontent[0],ridcontent[1]) + else: + print("传入地址: "+ridcontent[0]) + + while True: + + try: + + if len(proxies2)>0: + res=requests.get(rid,headers=headers,proxies=proxies2,timeout=15) + else: + res=requests.get(rid,headers=headers,timeout=15) + + if res.status_code!=200: + print(res.status_code) + #input('直播地址连接失败,请手动检测配置文件里的地址是否正常') + print(rid+' 的直播地址连接失败,请手动检测配置文件里的地址是否正常') + else: + #print(res.current_url) + res.encoding='utf-8-sig' + roomid = ((res.url).split('/')[-1]).split('?')[0] + # jsurl = "https://webcast.amemv.com/webcast/room/reflow/info/?type_id=0&live_id=1&room_id=" + roomid + "&app_id=1128" + jsurl = "https://webcast.amemv.com/webcast/room/reflow/info/?verifyFp=verify_l7u0qa4v_lx77IkKY_kSHg_411A_9JqE_9h6LMXjaPvb" + get_dx() + "&type_id=0&live_id=1&room_id=" + roomid + "&sec_user_id=&app_id=1128&msToken=HZq7LCnGU_J6-_ZIP8z7ugoxa3TO4kiefFKCiLe139sbMkgLan0uEVijUk8B7aBNseUuwGfvGToHXv7VgGGI4ELJPWBedkZZdZtq9se_dK2gQ_dP-Rw" + get_xx() + "&X-Bogus=DFSzswVOye0ANHQbSKxn6VXAIQ5" + get_xx() + + if len(proxies2) > 0: + resjs = requests.get(jsurl, headers=headers, proxies=proxies2, timeout=15) + else: + resjs = requests.get(jsurl, headers=headers, timeout=15) + # print(resjs.text) + if resjs.status_code != 200: + print(resjs.status_code) + print(jsurl + ' 的直播地址连接失败,请手动检测配置文件里的地址是否正常') + else: + resjs.encoding = 'utf-8-sig' + #print (resjs.text) + resjs=json.loads(resjs.text) + startname=resjs["data"]["room"]["owner"]['nickname'] + startname = subwords(startname) + if startname in allLive: + print("新增的地址: %s 已经存在,本条线程将会退出"%startname) + namelist.append(str(rid)+"|"+str("#"+rid)) + exit(0) + if len(ridcontent)==1: + namelist.append(str(ridcontent[0])+"|"+str(ridcontent[0]+",主播: "+startname.strip())) + break + + except Exception as e: + print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) + print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') + + + x=delaydefault + if recordfinish==True: + counttimeend=time.time()-counttime + if counttimeend<60: + x=2 + else: + recordfinish=False + else: + x=delaydefault + + + while x: + x = x-1 + if looptime: + print('\r循环等待%d秒 '%x ,end="") + time.sleep(1) + if looptime: + print('\r重新检测直播间中...',end="") + + changestaute="" + while True: + real_urlcontent = get_real_url(rid,startname,changestaute) + real_url=real_urlcontent[0] + changestaute=real_urlcontent[1] + if real_url!=None: + now = time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime(time.time())) + try: + if len(videopath)>0: + if videopath[-1]!="\\": + videopath=videopath+"\\" + if not os.path.exists(videopath+startname): + os.makedirs(videopath+startname) + else: + if not os.path.exists(startname): + os.makedirs('./'+startname) + + except Exception as e: + print("路径错误信息708: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) + + + if not os.path.exists(videopath+startname): + print("保存路径不存在,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置") + videopath="" + print("因为配置文件的路径错误,本次录制在程序目录") + + + if videosavetype=="FLV": + filename=startname + '_' + now + '.flv' + if len(videopath)==0: + print("\r"+startname+" 录制视频中: "+os.getcwd()+"\\"+startname +'\\'+ filename) + else: + print("\r"+startname+" 录制视频中: "+videopath+startname +'\\'+ filename) + + if not os.path.exists(videopath+startname): + print("目录均不能生成文件,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置,程序将会退出") + input("请按回车退出") + os._exit(0) + #flv录制格式 + + + try: + recording.add(startname) + _filepath, _ = urllib.request.urlretrieve(real_url, videopath+startname +'\\'+ filename) + recordfinish=True + recordfinish_2=True + counttime=time.time() + if startname in recording: + recording.remove(startname) + if startname in unrecording: + unrecording.add(startname) + + except: + print('\r'+time.strftime('%Y-%m-%d %H:%M:%S ') +startname + ' 未开播') + + elif videosavetype=="MP4": + filename=startname + '_' + now + ".mp4" + if len(videopath)==0: + print("\r"+startname+ " 录制视频中: "+os.getcwd()+"\\"+startname +'\\'+ filename) + else: + print("\r"+startname+ " 录制视频中: "+videopath+startname +'\\'+ filename) + + ffmpeg_path = "ffmpeg" + file = videopath+startname +'\\'+ filename + try: + real_url=real_url.replace("\\u0026","&") + recording.add(startname) + if videoencode: + _output = subprocess.check_output([ + ffmpeg_path, "-y", + "-v","verbose", + "-rw_timeout","10000000", # 10s + "-loglevel","error", + "-hide_banner", + "-user_agent",headers["User-Agent"], + "-analyzeduration","2147483647", + "-probesize","2147483647", + "-i",real_url, + '-bufsize','5000k', + "-map","0", + "-sn","-dn", + # "-f","mpegts", + # "-bsf:v","h264_mp4toannexb", + # "-c","copy", + "-c:v","libx264", #后期可以用crf来控制大小 + #"-c:v","copy", #直接用copy的话体积特别大. + '-max_muxing_queue_size','64', + "{path}".format(path=file), + ], stderr = subprocess.STDOUT) + else: + _output = subprocess.check_output([ + ffmpeg_path, "-y", + "-v","verbose", + "-rw_timeout","10000000", # 10s + "-loglevel","error", + "-hide_banner", + "-user_agent",headers["User-Agent"], + "-analyzeduration","2147483647", + "-probesize","2147483647", + "-i",real_url, + '-bufsize','5000k', + "-map","0", + "-sn","-dn", + # "-f","mpegts", + # "-bsf:v","h264_mp4toannexb", + # "-c","copy", + #"-c:v","libx264", #后期可以用crf来控制大小 + "-c:v","copy", #直接用copy的话体积特别大. + '-max_muxing_queue_size','64', + "{path}".format(path=file), + ], stderr = subprocess.STDOUT) + + recordfinish=True + recordfinish_2=True + counttime=time.time() + if startname in recording: + recording.remove(startname) + if startname in unrecording: + unrecording.add(startname) + except subprocess.CalledProcessError as exc: + #logging.warning(str(exc.output)) + print(str(exc.output) +" 发生错误的行数: "+str(exc.__traceback__.tb_lineno) ) + + elif videosavetype=="MP3": + filename=startname + '_' + now + ".mp3" + if len(videopath)==0: + print("\r"+startname+" 录制音频中: "+os.getcwd()+"\\"+startname +'\\'+ filename) + else: + print("\r"+startname+" 录制音频中: "+videopath+startname +'\\'+ filename) + + ffmpeg_path = "ffmpeg" + file = videopath+startname +'\\'+ filename + try: + recording.add(startname) + _output = subprocess.check_output([ + ffmpeg_path, "-y", + "-rw_timeout","10000000", # 10s + "-user_agent",headers["User-Agent"], + "-i",real_url, + "{path}".format(path=file), + ], stderr = subprocess.STDOUT) + + + recordfinish=True + recordfinish_2=True + counttime=time.time() + if startname in recording: + recording.remove(startname) + if startname in unrecording: + unrecording.add(startname) + except subprocess.CalledProcessError as exc: + #logging.warning(str(exc.output)) + print(str(exc.output) +" 发生错误的行数: "+str(exc.__traceback__.tb_lineno) ) + + + else: + + if(Splitvideobysize): #这里默认是启用/不启用视频分割功能 + while(True): + now = time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime(time.time())) + filename=startname + '_' + now + ".ts" + if len(videopath)==0: + print("\r"+startname+" 分段录制视频中: "+os.getcwd()+"\\"+startname +'\\'+ filename + " 每录满: %d M 存一个视频"%Splitsize ) + else: + print("\r"+startname+" 分段录制视频中: "+videopath+startname +'\\'+ filename+ " 每录满: %d M 存一个视频"%Splitsize) + + ffmpeg_path = "ffmpeg" + file = videopath+startname +'\\'+ filename + try: + real_url=real_url.replace("\\u0026","&") + recording.add(startname) + _output = subprocess.check_output([ + ffmpeg_path, "-y", + "-v","verbose", + "-rw_timeout","10000000", # 10s + "-loglevel","error", + "-hide_banner", + "-user_agent",headers["User-Agent"], + "-analyzeduration","2147483647", + "-probesize","2147483647", + "-i",real_url, + '-bufsize','5000k', + "-map","0", + "-sn","-dn", + "-f","mpegts", + # "-bsf:v","h264_mp4toannexb", + # "-c","copy", + "-c:v","copy", + '-max_muxing_queue_size','64', + "-fs",str(Splitsizes), + "{path}".format(path=file), + ], stderr = subprocess.STDOUT) + + + recordfinish=True #这里表示正常录制成功一次 + recordfinish_2=True + counttime=time.time() #这个记录当前时间, 用于后面 1分钟内快速2秒循环 这个值不能放到后面 + if startname in recording: + recording.remove(startname) + if startname in unrecording: + unrecording.add(startname) + if tsconvertmp4: + threading.Thread(target=convertsmp4,args=(file,)).start() + except subprocess.CalledProcessError as exc: + #logging.warning(str(exc.output)) + print(str(exc.output) +" 发生错误的行数: "+str(exc.__traceback__.tb_lineno) ) + break + else: + filename=startname + '_' + now + ".ts" + if len(videopath)==0: + print("\r"+startname+" 录制视频中: "+os.getcwd()+"\\"+startname +'\\'+ filename) + else: + print("\r"+startname+" 录制视频中: "+videopath+startname +'\\'+ filename) + + ffmpeg_path = "ffmpeg" + file = videopath+startname +'\\'+ filename + try: + recording.add(startname) + _output = subprocess.check_output([ + ffmpeg_path, "-y", + "-v","verbose", + "-rw_timeout","10000000", # 10s + "-loglevel","error", + "-hide_banner", + # "-user_agent",headers["User-Agent"], + "-analyzeduration","2147483647", + "-probesize","2147483647", + "-i",real_url, + '-bufsize','5000k', + "-map","0", + "-sn","-dn", + "-f","mpegts", + # "-bsf:v","h264_mp4toannexb", + # "-c","copy", + "-c:v","copy", + '-max_muxing_queue_size','64', + "{path}".format(path=file), + ], stderr = subprocess.STDOUT) + + + recordfinish=True + recordfinish_2=True + counttime=time.time() + if startname in recording: + recording.remove(startname) + if startname in unrecording: + unrecording.add(startname) + if tsconvertmp4: + threading.Thread(target=convertsmp4,args=(file,)).start() + + + except subprocess.CalledProcessError as exc: + #logging.warning(str(exc.output)) + print(str(exc.output) +" 发生错误的行数: "+str(exc.__traceback__.tb_lineno) ) + + else: + #print('直播间不存在或未开播') + pass + + if recordfinish_2==True: + if startname in recording: + recording.remove(startname) + if startname in unrecording: + unrecording.add(startname) + print('\n'+startname+" "+ time.strftime('%Y-%m-%d %H:%M:%S ') + '直播录制完成\n') + logger.warning(startname+" "+"直播录制完成") + recordfinish_2=False + + x=delaydefault + if recordfinish==True: + counttimeend=time.time()-counttime + if counttimeend<60: + x=20 #现在改成默认20s + else: + recordfinish=False + else: + x=delaydefault + + while x: + x = x-1 + #print('\r循环等待%d秒 '%x) + if looptime: + print('\r循环等待%d秒 '%x ,end="") + time.sleep(1) + if looptime: + print('\r检测直播间中...',end="") + + + +import datetime +start5 = datetime.datetime.now() +def displayinfo(): + global start5 + time.sleep(10) + while True: + time.sleep(30) + os.system("cls") + print("循环值守录制抖音直播 版本:%s"%str(version)) + + if len(recording)==0 and len(unrecording)==0: + time.sleep(10) + nowdate=time.strftime("%H:%M:%S", time.localtime()) + print("\r没有正在录制的直播,请检查配置文件是否为空 "+nowdate,end="") + print("") + + continue + else: + # livetask = len(recording) + len(unrecording) + # print("正则监控{}个直播".format(str(livetask))) + if len(recording)>0: + print("x"*60) + NoRepeatrecording = list(set(recording)) + print("正在录制{}个直播: ".format(str(len(NoRepeatrecording)))) + for x in NoRepeatrecording: + print(x+" 正在录制中") + #print("%i个直播正在录制中: "%len(NoRepeatrecording)+nowdate) + end = datetime.datetime.now() + print('总共录制时间: ' +str(end - start5)) + print("x"*60) + else: + start5 = datetime.datetime.now() + + # if len(unrecording)>0: + # NounRepeatrecording = list(set(unrecording)) + # for x in NounRepeatrecording: + # #logger.warning(x+" 直播间未直播,等待开播中.. ") + # print(x+" :等待直播中") + # print("x"*60) + # time.sleep(10) + +t = threading.Thread(target=displayinfo, args=(), daemon=True) +t.start() +runingList=[] +texturl=[] +textNoRepeatUrl=[] +createVar = locals() +zz=0 +namelist=[] +while True: + file=open("URL_config.ini","r",encoding="utf-8-sig") + for line in file: + line=line.strip() + if line.startswith("#"): + continue + c=line.split() + if len(c)==0: + continue + else: + c=line.strip() + + c=c.split('#') + if len(line)<20: + continue + + texturl.append(line) + + #print(c[0]) + while len(namelist): + a=namelist.pop() + replacewords = a.split('|') + updateFile(r"URL_config.ini", replacewords[0], replacewords[1]) + #格式化后查找不一样 + file.close() + if len(texturl)>0: + textNoRepeatUrl = list(set(texturl)) + + if len(textNoRepeatUrl)>0: + for i in textNoRepeatUrl: + formatcontent = i.split(',') + #formatcontent[0] #这个为分离出的地址 + if formatcontent[0] not in runingList: + #print("新增链接: "+ formatcontent[0]) + zz=zz+1 + createVar['thread'+ str(zz)] = threading.Thread(target=startgo,args=(i,)) + createVar['thread'+ str(zz)].setDaemon(True) + createVar['thread'+ str(zz)].start() + runingList.append(formatcontent[0]) + time.sleep(1.5) + texturl=[] + time.sleep(3) + + +#print('程式结束,请按任意键退出') +input() + + -- GitLab