├── URL_config.ini ├── Cookie获取方式.png ├── config.ini ├── 第三步-创建网页下载或者观看直播.ipynb ├── 第二步-运行程序.ipynb ├── 第一步-安装ffmpeg.ipynb ├── README.md ├── 抖音直播录制_230530.5-NoSelenium.py └── 抖音直播录制_230530.6.py /URL_config.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Cookie获取方式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlyclxy/douyin-live-download/HEAD/Cookie获取方式.png -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [1] 2 | 循环时间(秒) = 60 3 | 同一时间访问网络的线程数 = 20 4 | 排队读取网址时间(秒) = 0 5 | 直播保存路径 = 6 | 视频保存格式ts|mkv|flv|mp4|ts音频|mkv音频 = TS 7 | 原画|超清|高清|标清 = 原画 8 | 是否显示直播地址 = 否 9 | 是否显示循环秒数 = 否 10 | ts格式分段录制是否开启 = 否 11 | 视频分段大小(兆) = 1000 12 | ts录制完成后自动增加生成mp4格式 = 否 13 | ts录制完成后自动增加生成m4a格式 = 否 14 | 追加格式后删除原文件 = 否 15 | 生成时间文件 = 否 16 | 是否显示浏览器 = 否 17 | 短链接自动转换为长连接 = 否 18 | 仅用浏览器录制 = 否 19 | cookies = 20 | 21 | -------------------------------------------------------------------------------- /第三步-创建网页下载或者观看直播.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "5ad367a5-1398-40ac-8a7f-ba620a542fe2", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "#创建网页下载或者观看直播\n", 11 | "!python -m http.server 6006" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "id": "0dfed602-9a0e-43ef-8955-43dde10280d4", 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [] 21 | } 22 | ], 23 | "metadata": { 24 | "kernelspec": { 25 | "display_name": "Python 3 (ipykernel)", 26 | "language": "python", 27 | "name": "python3" 28 | }, 29 | "language_info": { 30 | "codemirror_mode": { 31 | "name": "ipython", 32 | "version": 3 33 | }, 34 | "file_extension": ".py", 35 | "mimetype": "text/x-python", 36 | "name": "python", 37 | "nbconvert_exporter": "python", 38 | "pygments_lexer": "ipython3", 39 | "version": "3.8.10" 40 | } 41 | }, 42 | "nbformat": 4, 43 | "nbformat_minor": 5 44 | } 45 | -------------------------------------------------------------------------------- /第二步-运行程序.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "04868127-4c2a-4241-bde4-e44853374c6f", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "#第二步-运行程序\n", 11 | "#或者建议新开一个终端,复制下面的命令进去,请去掉前面的叹号.这样可以清屏\n", 12 | "!python 抖音直播录制_230507-linux.py" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "id": "eca66c54-c742-4f40-a02e-d862e20222bf", 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [] 22 | } 23 | ], 24 | "metadata": { 25 | "kernelspec": { 26 | "display_name": "Python 3 (ipykernel)", 27 | "language": "python", 28 | "name": "python3" 29 | }, 30 | "language_info": { 31 | "codemirror_mode": { 32 | "name": "ipython", 33 | "version": 3 34 | }, 35 | "file_extension": ".py", 36 | "mimetype": "text/x-python", 37 | "name": "python", 38 | "nbconvert_exporter": "python", 39 | "pygments_lexer": "ipython3", 40 | "version": "3.8.10" 41 | } 42 | }, 43 | "nbformat": 4, 44 | "nbformat_minor": 5 45 | } 46 | -------------------------------------------------------------------------------- /第一步-安装ffmpeg.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "ccb2989c-6169-4b01-b077-799cf15212e3", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "#开启学术加速\n", 11 | "!export http_proxy=http://192.168.1.174:12798 && export https_proxy=http://192.168.1.174:12798" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "id": "fa954a2f-e778-4426-a3e3-55555987bf10", 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "#安装ffmpeg1\n", 22 | "!su -c \"apt-get update\"" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "id": "17220baf-2000-40a0-8a85-2d8b3a0ef7db", 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "#安装ffmpeg2\n", 33 | "!su -c \"apt-get -y install ffmpeg\"" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "id": "5ec3b6b4-b599-4064-9fb2-401303e2bc38", 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "#显示ffmpeg版本\n", 44 | "!ffmpeg -version" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "id": "1758e13e-fda9-4035-a2a7-6b44c5138a60", 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [] 54 | } 55 | ], 56 | "metadata": { 57 | "kernelspec": { 58 | "display_name": "Python 3 (ipykernel)", 59 | "language": "python", 60 | "name": "python3" 61 | }, 62 | "language_info": { 63 | "codemirror_mode": { 64 | "name": "ipython", 65 | "version": 3 66 | }, 67 | "file_extension": ".py", 68 | "mimetype": "text/x-python", 69 | "name": "python", 70 | "nbconvert_exporter": "python", 71 | "pygments_lexer": "ipython3", 72 | "version": "3.8.10" 73 | } 74 | }, 75 | "nbformat": 4, 76 | "nbformat_minor": 5 77 | } 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 好久不写这个了.发一个能用的上来. 这个去掉了selenium模块,并且添加了自动获取cookies,可以直接在远程服务器直接拉取录制 2 | 现在6月份的版本经测试总会漏.6月份用的异步,感觉有问题.所以现在放弃这个异步的方案, 以后更只会更5月的这个版本.即多线程版本. 3 | 这次提供了一个autodl的设置文本,可以依次点击运行.来在autodl里录制直播. 4 | 录制应该只支持pc端的链接了(长连接),要是录制失败,可能需要你自己再设置一个cookies(默认是自动获取的) 5 | 6 | 关于autodl的,一开始要设置学术加速.这个你要根据你自己的区域来改那个ip. 然后装ffmpeg. 学术加速就是加速下载ffmpeg用的... 第一次装完后,第二次就不用进行第一步了.可以直接运行py 7 | 然后提供了一个第三部.如果你需要看录制的结果. 不过日常有文件其实就不需要看了 8 | 再另外那个文本文档,你录制后最后关了文档. 因为他一直会打印,打印多了会卡死你的服务器... 9 | 10 | ------------------------------------------------------------------------------------------------------------- 11 | 本代码用于监控录制抖音直播,最早写于2020年12月份.一直维护至今...因为之前不会用github.所以一直没有发到这个上面来. 12 | 因为后来不想研究js逆向了. 所以现在只能通过cookies获取直播链接.其实也给用户造成的一定的麻烦. 13 | 如果需要在linux上使用,需要去除掉selenium模块... 需要把selenium相关代码去掉然后把路径的反斜杠改成正斜杠 14 | 另外这个需要ffmpeg,没有的需要自己配置一下 15 | 16 | 此版本手机分享的短连接失效了,建议用cookies方式填写pc端长连接. 短连接方式因为去年好像断断续续用不了了. 就删除了. 直到前一阵在github上见到一个能用的代码,就拿过来用了两个月,结果又失效了.所以暂时这段代码躺尸了. 17 | 18 | --------------------------------之前的更新说明-------------------------------------------------------------- 19 | 前提说明: 20 | 21 | 配置文件是 config.ini 需要手动改 22 | 主播地址填在 URL_config.ini 一个主播一行 23 | 24 | https://v.douyin.com/yNPMeQT/ 这种是手机分享的短连接,不需要填cookies 25 | https://live.douyin.com/215813525764 这种是电脑端的长连接,需要填cookies才能检测 26 | 然后运行 抖音直播录制_230530.4.exe 27 | 28 | 29 | 30 | 说明 31 | 32 | 1.这版建议填入手机分享的短连接,比如 https://v.douyin.com/yNPMeQT/ 33 | 现在URL_config.ini里填了一个短连接做测试. 直接打开程序没有报错就是正常的. 34 | 后续你可以把那个地址删掉改成你自己的. 一个主播一行 35 | 2.pc端网页那个地址也可以用,但是必须在配置里填入抖音的cookies,不需要登录的chrome版本的cookies就可以.因为操作比较麻烦,所以暂时不 36 | 建议用pc端的地址,而且cookies大约会在个把月的时候失效需要重新获取 37 | 3.cookies获取方式提供了个图片.图片是百度的网站,你需要打开抖音的的网站获取抖音的cookies. https://www.douyin.com/ 38 | 而且必须要chrome浏览器获取的才可以 39 | 4.暂时这个版本不太好改成liunx版的. 因为里面包含了selenium模块... 需要把selenium相关代码去掉然后把路径的反斜杠改成正斜杠. 如果特别需要请联系我 40 | 可以留个邮箱,有需要代码直接发你 41 | 5.关于主页.有人想问未直播的怎么录.这个更麻烦. 不怕折腾的可以把配置文件里 短链接自动转换为长连接 = 是 仅用浏览器录制 = 是,然后会调用selenium 42 | 把URL_config.ini的主页地址转为pc端地址. 转换完了记得把这两个配置改回来.这个建议用多文件版本(那个win7的),那个放了个chrome的驱动. 43 | 44 | 45 | -------------------------------------------------------------------------------- /抖音直播录制_230530.5-NoSelenium.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | import random 3 | import requests 4 | import re 5 | import os 6 | # import urllib.request 7 | import urllib.parse 8 | import sys 9 | # from bs4 import BeautifulSoup 10 | import time 11 | import json 12 | import configparser 13 | import subprocess 14 | import threading 15 | import string 16 | import logging 17 | import datetime 18 | from configparser import RawConfigParser 19 | # from selenium.common.exceptions import TimeoutException 20 | # from selenium.webdriver.chrome.options import Options 21 | # # from selenium.webdriver.chrome.service import Service 22 | # from selenium import webdriver 23 | import shutil 24 | import hashlib 25 | 26 | 27 | version=230530.4 #请注意把下面这个地方和文件名改了就行啦 28 | #pyinstaller -F 抖音直播录制_230530.5.py 29 | #pyinstaller 抖音直播录制_230530.5.py 30 | 31 | #log日志--------------------------------------------------------------- 32 | # sys.stdout = Logger(sys.stdout) # 将输出记录到log 33 | # sys.stderr = Logger(sys.stderr) # 将错误信息记录到log 34 | # 创建一个logger 35 | logger = logging.getLogger('抖音直播录制%s版'%str(version)) 36 | logger.setLevel(logging.INFO) 37 | # 创建一个handler,用于写入日志文件 38 | if not os.path.exists("log"): 39 | os.makedirs("log") 40 | fh = logging.FileHandler("log/错误日志文件.log",encoding="utf-8-sig",mode="a") 41 | fh.setLevel(logging.WARNING) 42 | # 再创建一个handler,用于输出到控制台 43 | # ch = logging.StreamHandler() 44 | # ch.setLevel(logging.INFO) 45 | # formatter = logging.Formatter() 46 | # ch.setFormatter(formatter) 47 | # 定义handler的输出格式 48 | formatter = logging.Formatter('%(asctime)s - %(message)s') 49 | fh.setFormatter(formatter) 50 | #ch.setFormatter(formatter) 51 | # 给logger添加handler 52 | logger.addHandler(fh) 53 | # logger.addHandler(ch) 54 | # socket.setdefaulttimeout(10) 55 | 56 | #全局变量-------------------------------------------------------------------------------- 57 | recording=set() 58 | unrecording=set() 59 | warning_count=0 60 | maxrequest=0 61 | runingList=[] 62 | texturl=[] 63 | textNoRepeatUrl=[] 64 | createVar = locals() 65 | firstStart=True #第一次 不会出现新增链接的字眼 66 | namelist=[] 67 | firstRunOtherLine=True #第一次运行显示线程等 68 | 69 | 70 | # videosavetype="TS" 71 | # delaydefault=60 72 | # localdelaydefault=0 73 | # videopath="" 74 | # videoQuality="原画" 75 | # videom3u8=False 76 | # looptime=False 77 | # Splitvideobysize=False 78 | # Splitsizes=0 79 | # tsconvertmp4=False 80 | # tsconvertm4a=False 81 | # delFilebeforeconversion=False 82 | # creatTimeFile=False 83 | # displayChrome=False 84 | # coverlongurl=False 85 | # onlybrowser=False 86 | # cookiesSet="" 87 | 88 | 89 | 90 | 91 | 92 | 93 | def updateFile(file,old_str,new_str): 94 | file_data = "" 95 | with open(file, "r", encoding="utf-8-sig") as f: 96 | for line in f: 97 | if old_str in line: 98 | line = line.replace(old_str,new_str) 99 | file_data += line 100 | with open(file,"w",encoding="utf-8-sig") as f: 101 | f.write(file_data) 102 | 103 | def get_xx(): 104 | string.ascii_letters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 105 | return random.choice(string.ascii_lowercase) 106 | 107 | def get_dx(): 108 | string.ascii_letters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 109 | return random.choice(string.ascii_uppercase) 110 | 111 | def subwords(words): 112 | words=re.sub('[-? * : " < > / | .]', '', words) 113 | words=re.sub(r'/', '', words) 114 | return words 115 | 116 | 117 | 118 | def convertsmp4(address): 119 | address2=address 120 | address2=address2.replace('.ts' , '') 121 | address2=address2.replace('.flv' , '') 122 | if tsconvertmp4: 123 | _output = subprocess.check_output([ 124 | "ffmpeg", "-i",address, 125 | "-c:v","copy", 126 | "-c:a","copy", 127 | "-f","mp4",address2+".mp4", 128 | ], stderr = subprocess.STDOUT) 129 | #print("转换到mp4") 130 | if delFilebeforeconversion: 131 | time.sleep(1) 132 | if os.path.exists(address): 133 | os.remove(address) 134 | 135 | 136 | def convertsm4a(address): 137 | if tsconvertm4a: 138 | address2=address 139 | address2=address2.replace('.ts' , '') 140 | address2=address2.replace('.flv' , '') 141 | _output = subprocess.check_output([ 142 | "ffmpeg", "-i",address, 143 | "-n","-vn", 144 | "-c:a","aac","-bsf:a","aac_adtstoasc","-ab","320k", 145 | address2+".m4a", 146 | ], stderr = subprocess.STDOUT) 147 | if delFilebeforeconversion: 148 | time.sleep(1) 149 | if os.path.exists(address): 150 | os.remove(address) 151 | #print("转换到m4a") 152 | 153 | def creatass(filegruop): 154 | startname=filegruop[0] 155 | assname=filegruop[1] 156 | index_time = -1 157 | finish=0 158 | today = datetime.datetime.now() 159 | re_datatime =today.strftime('%Y-%m-%d %H:%M:%S') 160 | 161 | # createVar[str(filegruop+"_th")] =threading.Thread() 162 | 163 | 164 | while(True): 165 | index_time+=1 166 | txt=str(index_time)+ "\n" + tranform_int_to_time(index_time)+',000 --> '+ tranform_int_to_time(index_time + 1)+',000' + "\n" + str(re_datatime) + "\n" 167 | 168 | with open(assname+".ass",'a',encoding='utf8') as f: 169 | f.write(txt) 170 | # print(txt) 171 | 172 | if startname not in recording: 173 | finish+=1 174 | offset = datetime.timedelta(seconds=1) 175 | # 获取修改后的时间并格式化 176 | re_datatime = (today + offset).strftime('%Y-%m-%d %H:%M:%S') 177 | today=today + offset 178 | # print(re_date) 179 | 180 | else: 181 | time.sleep(1) 182 | today = datetime.datetime.now() 183 | re_datatime =today.strftime('%Y-%m-%d %H:%M:%S') 184 | 185 | if finish>15: 186 | break 187 | 188 | def videodownload(url,filename,size_all): 189 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362'} 190 | size = 0 191 | chunk_size = 1024 192 | requests.urllib3.disable_warnings() 193 | response = requests.get(url, headers=headers, stream=True, verify=False,proxies=None) 194 | with open(filename, 'wb') as file: 195 | for data in response.iter_content(chunk_size = chunk_size): 196 | file.write(data) 197 | size += len(data) 198 | file.flush() 199 | if size_all > 0: 200 | sys.stdout.write(' [下载进度]:%.2fMB/%.2fMB' % (float(size/10/ (size_all*1024*1024) * 100), size_all) + '\r') 201 | if size > size_all*1024*1024: 202 | break 203 | else: 204 | sys.stdout.write(' [下载进度]:%.2fMB' % float(size/1024/1024) + '\r') 205 | print('下载完成') 206 | 207 | headers = { 208 | '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' 209 | } 210 | 211 | print( '-------------- 吾爱破解论坛 程序当前配置----------------' ) 212 | print("循环值守录制抖音直播 版本:%s"%str(version)) 213 | 214 | 215 | 216 | 217 | 218 | 219 | def newgeturl(url): 220 | # print("请求地址"+url) 221 | global warning_count 222 | try: 223 | for _i in range(10): 224 | headers2 = {"referer": "https://www.douyin.com/", 225 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', 226 | "cookie":cookiesSet} 227 | 228 | try: 229 | res=requests.get(url,headers=headers2,proxies=None) 230 | except Exception as e: 231 | print("请求网页失败,请检查是否用了代理,错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 232 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 233 | warning_count+=1 234 | return "" 235 | # print(res.text) 236 | if len(res.text)<5 or res.text.find("繁忙")>=0: 237 | # print(1) 238 | # print(res.text) 239 | time.sleep(1) 240 | continue 241 | else: 242 | # print(res.text) 243 | # print("端口网址为:"+url) 244 | return res.text 245 | # len(res.text)>5 246 | print("未获取到正确的网页信息.此时获取的网页信息如下:"+ str(res.text)) 247 | warning_count+=1 248 | 249 | except Exception as e: 250 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 251 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 252 | 253 | warning_count+=1 254 | return "" 255 | 256 | 257 | ''' 258 | def chromeAuto(url): 259 | global warning_count 260 | try: 261 | if displayChrome: 262 | #是否有浏览器设置 263 | chrome = webdriver.Chrome() 264 | else: 265 | chrome = webdriver.Chrome(options=chrome_options) 266 | 267 | for _b in range(5): 268 | 269 | chrome.set_page_load_timeout(1) 270 | try: 271 | chrome.get("https://www.douyin.com/discover") # 往浏览器的网页地址栏填入url参数 272 | except: 273 | pass 274 | 275 | # print("超时了") 276 | # time.sleep(2) 277 | # chrome.close() 278 | # chrome.quit() 279 | 280 | chrome.set_page_load_timeout(3) 281 | for _t in range(10): 282 | #是否显示浏览器 283 | # chrome = Chrome() 284 | #设置超时5秒 285 | 286 | # url="https://live.douyin.com/webcast/web/enter/?aid=6383&web_rid=677464104206" 287 | try: 288 | try: 289 | chrome.get(url) # 往浏览器的网页地址栏填入url参数 290 | except: 291 | pass 292 | 293 | # try: 294 | # chrome.get(url) # 往浏览器的网页地址栏填入url参数 295 | # time.sleep(10) 296 | # except: 297 | # print("超时了") 298 | # time.sleep(2) 299 | # # chrome.close() 300 | # # chrome.quit() 301 | # continue 302 | 303 | data = chrome.page_source 304 | 305 | if data.find("status")<0 or data.find("繁忙")>-1: 306 | # print("未找到源码网址,2s后重试..") 307 | time.sleep(0.5) 308 | chrome.refresh() 309 | continue 310 | else: 311 | chrome.quit() 312 | return data 313 | 314 | except: 315 | time.sleep(2) 316 | continue 317 | # print(1) 318 | # chrome.close() 319 | 320 | # print(2) 321 | time.sleep(2) 322 | except Exception as e: 323 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 324 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 325 | 326 | warning_count+=1 327 | 328 | chrome.quit() 329 | return "" 330 | ''' 331 | 332 | 333 | def getStream_url(douyindata): 334 | # global warning_count 335 | portGroup=[] 336 | restext="" 337 | if douyindata.find("系统繁忙,请稍后再试")>=0: 338 | print("未找到源码网址,等待设定好的秒数后会自动重试, 如果一直出现这个问题,请联系作者") 339 | return portGroup 340 | 341 | try: 342 | #因为获取的网页包含html的信息, 下面对这些框架的代码进行清理. 清理后的代码为{..}可以转json 343 | #获取不到网页.返回空 344 | if douyindata=="": 345 | 346 | return(portGroup) 347 | 348 | position1=douyindata.find("{") 349 | position2=douyindata.rfind("}") 350 | restext=douyindata[position1:position2+1] 351 | 352 | #转换网页源码为json 353 | try: 354 | resjs=json.loads(restext) 355 | except Exception as e: 356 | if len(str(restext))>0: 357 | print("json转换失败, 转换错误信息为: "+str(restext) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 358 | logger.warning("json转换失败,转换错误信息为: "+str(restext) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 359 | else: 360 | # print("json获取为空 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 361 | # logger.warning("json获取为空 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 362 | pass 363 | return portGroup 364 | #创建端口参数组 365 | #转换过json的代码,获取端口具体内容 366 | #获取主播名字 367 | startname=resjs["data"]["user"]['nickname'] 368 | 369 | #格式化字符串.防止文件名出现特殊字符 370 | startname = subwords(startname) 371 | portGroup.append(startname) 372 | 373 | #获取直播间状态.2是直播.4是直播结束 374 | status=resjs["data"]["data"][0]["status"] #直播状态 #m3u8流 375 | # print("直播状态:"+str(+status)) 376 | 377 | # #去除前后空格 378 | # status=status.strip() 379 | 380 | if status==2: 381 | #参数组添加信息 382 | portGroup.append(status) 383 | #获取直播流地址 384 | 385 | 386 | if videoQuality=="超清": 387 | res=resjs["data"]["data"][0]["stream_url"]["hls_pull_url_map"]["HD1"] #m3u8流 388 | res2=resjs["data"]["data"][0]["stream_url"]["flv_pull_url"]["HD1"] #flv流 389 | if videoQuality=="高清": 390 | res=resjs["data"]["data"][0]["stream_url"]["hls_pull_url_map"]["SD1"] #m3u8流 391 | res2=resjs["data"]["data"][0]["stream_url"]["flv_pull_url"]["SD1"] #flv流 392 | if videoQuality=="标清": 393 | res=resjs["data"]["data"][0]["stream_url"]["hls_pull_url_map"]["SD2"] #m3u8流 394 | res2=resjs["data"]["data"][0]["stream_url"]["flv_pull_url"]["SD2"] #flv流 395 | 396 | #蓝光,原画 397 | else: 398 | res=resjs["data"]["data"][0]["stream_url"]["hls_pull_url_map"]["FULL_HD1"] #m3u8流 399 | res2=resjs["data"]["data"][0]["stream_url"]["flv_pull_url"]["FULL_HD1"] #flv流 400 | 401 | 402 | 403 | 404 | 405 | # print("下载地址为:"+res) 406 | #参数组添加信息 407 | portGroup.append(res) 408 | portGroup.append(res2) 409 | 410 | else: 411 | #参数组添加信息 412 | portGroup.append(status) 413 | portGroup.append("") 414 | portGroup.append("") 415 | # print("没有直播") 416 | 417 | #返回各种端口信息,主播名,状态码,地址 418 | return(portGroup) 419 | except Exception as e: 420 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 421 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 422 | return portGroup 423 | return portGroup 424 | 425 | 426 | #拼接端口的网址,这里只接收输入pc端的网址,输出端口网址 427 | def SplicingUrl(inputUrl): 428 | try: 429 | resStr = (inputUrl.split("?")[0]).split("/")[-1] 430 | #当获取的rid拼接到网址里,获得端口网址 431 | # resStr="https://live.douyin.com/webcast/web/enter/?aid=6383&web_rid="+resStr 以前的版本 432 | resStr="https://live.douyin.com/webcast/room/web/enter/?aid=6383&device_platform=web&cookie_enabled=true&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=100.0.4896.127&web_rid="+resStr 433 | # print(resStr) 434 | #返回拼接网址 435 | return resStr 436 | except Exception as e: 437 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 438 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 439 | 440 | 441 | def changemaxconnect(): 442 | global maxrequest 443 | global warning_count 444 | #动态控制连接次数 445 | 446 | Preset=maxrequest 447 | # 记录当前时间 448 | start_time = time.time() 449 | 450 | while True: 451 | 452 | time.sleep(5) 453 | 454 | if 10 <= warning_count <=20: 455 | if Preset > 5: 456 | maxrequest = 5 457 | else: 458 | maxrequest //= 2 # 将maxrequest除以2(向下取整) 459 | if maxrequest > 0: # 如果得到的结果大于0,则直接取该结果 460 | maxrequest = Preset 461 | else: # 否则将其设置为1 462 | Preset = 1 463 | 464 | print("同一时间访问网络的线程数动态改为", maxrequest) 465 | warning_count=0 466 | time.sleep(5) 467 | 468 | elif 20 < warning_count: 469 | maxrequest = 1 470 | print("同一时间访问网络的线程数动态改为", maxrequest) 471 | warning_count=0 472 | time.sleep(10) 473 | 474 | elif warning_count < 10 and time.time() - start_time >60: 475 | maxrequest = Preset 476 | warning_count=0 477 | start_time = time.time() 478 | print("同一时间访问网络的线程数动态改为", maxrequest) 479 | 480 | 481 | def check_md5(file_path): 482 | """ 483 | 计算文件的md5值 484 | """ 485 | with open(file_path, 'rb') as fp: 486 | file_md5 = hashlib.md5(fp.read()).hexdigest() 487 | return file_md5 488 | 489 | def backup_file(file_path, backup_dir): 490 | """ 491 | 备份配置文件到备份目录 492 | """ 493 | try: 494 | if not os.path.exists(backup_dir): 495 | os.makedirs(backup_dir) 496 | # 拼接备份文件名,年-月-日-时-分-秒 497 | timestamp = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S') 498 | backup_file_name = os.path.basename(file_path) + '_' + timestamp 499 | # 拷贝文件到备份目录 500 | backup_file_path = os.path.join(backup_dir, backup_file_name) 501 | shutil.copy2(file_path, backup_file_path) 502 | print(f'已备份配置文件{file_path}到{backup_file_path}') 503 | except Exception as e: 504 | print(f'备份配置文件{file_path}失败:{str(e)}') 505 | 506 | def backup_file_start(): 507 | config_file_path = './config.ini' 508 | url_config_file_path = './URL_config.ini' 509 | backup_dir = './配置自动备份文件夹' 510 | 511 | config_md5 = '' 512 | url_config_md5 = '' 513 | 514 | while True: 515 | try: 516 | if os.path.exists(config_file_path): 517 | new_config_md5 = check_md5(config_file_path) 518 | if new_config_md5 != config_md5: 519 | backup_file(config_file_path, backup_dir) 520 | config_md5 = new_config_md5 521 | 522 | if os.path.exists(url_config_file_path): 523 | new_url_config_md5 = check_md5(url_config_file_path) 524 | if new_url_config_md5 != url_config_md5: 525 | backup_file(url_config_file_path, backup_dir) 526 | url_config_md5 = new_url_config_md5 527 | time.sleep(60) # 每1分钟检测一次文件是否有修改 528 | except Exception as e: 529 | print(f'执行脚本异常:{str(e)}') 530 | 531 | 532 | 533 | allLive=[] #全部的直播 534 | allRecordingUrl=[] 535 | 536 | class C_real_url(): 537 | def douyin(url): 538 | zhibo_ids="" 539 | #代码来自于https://github.com/wbt5/real-url/blob/master/douyin.py. 侵删 540 | x=0 541 | for y in range(2): 542 | x+=1 543 | if x>1: 544 | url=zhibo_ids 545 | # DEBUG = False 546 | DEBUG = False 547 | headers = { 548 | 'authority': 'v.douyin.com', 549 | 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1', 550 | } 551 | 552 | # url = input('请输入抖音直播链接或19位room_id:') 553 | # print("传入url",url) 554 | start_url=url #初始固定一个,防止后面的变量变了 555 | if re.match(r'\d{19}', url): 556 | room_id = url 557 | else: 558 | try: 559 | url = re.search(r'(https.*)', url).group(1) 560 | response = requests.head(url, headers=headers) 561 | url = response.headers['location'] 562 | room_id = re.search(r'\d{19}', url).group(0) 563 | except Exception as e: 564 | if DEBUG: 565 | print(e) 566 | print('地址请求失败,请检查这个网址是否正常: '+start_url) 567 | sys.exit(1) 568 | # print('room_id', room_id) 569 | 570 | try: 571 | headers.update({ 572 | 'authority': 'webcast.amemv.com', 573 | 'cookie': '_tea_utm_cache_1128={%22utm_source%22:%22copy%22%2C%22utm_medium%22:%22android%22%2C%22utm_campaign%22:%22client_share%22}', 574 | }) 575 | 576 | params = ( 577 | ('type_id', '0'), 578 | ('live_id', '1'), 579 | ('room_id', room_id), 580 | ('app_id', '1128'), 581 | ) 582 | 583 | response = requests.get('https://webcast.amemv.com/webcast/room/reflow/info/', headers=headers, params=params).json() 584 | 585 | 586 | try: 587 | rtmp_pull_url = response['data']['room']['stream_url']['flv_pull_url']['FULL_HD1'] 588 | except Exception as e: 589 | rtmp_pull_url = response['data']['room']['stream_url']['rtmp_pull_url'] 590 | 591 | 592 | try: 593 | hls_pull_url = response['data']['room']['stream_url']['hls_pull_url_map']['FULL_HD1'] 594 | except Exception as e: 595 | hls_pull_url = response['data']['room']['stream_url']['hls_pull_url'] 596 | 597 | 598 | 599 | hls_pull_url = response['data']['room']['stream_url']['hls_pull_url'] 600 | zhubo_name = response['data']['room']['owner']['nickname'] 601 | zhibo_id = response['data']['room']["id_str"] #只要判定这个有没有 602 | 603 | 604 | 605 | 606 | try: 607 | zhibo_ids = response['data']['room']['owner']['own_room']["room_ids_str"][0] #如果这个出错,就判断没有播. 只有播放的时候才会出现这个 608 | if zhibo_id==zhibo_ids: 609 | # print(zhibo_id) 610 | 611 | response = requests.head(rtmp_pull_url) 612 | if response.status_code == 200: 613 | # print(rtmp_pull_url+'直播流链接存在') 614 | valueA=True 615 | else: 616 | # print(rtmp_pull_url+'直播流链接不存在') 617 | valueA=False 618 | 619 | response = requests.head(hls_pull_url) 620 | if response.status_code == 200: 621 | valueB=True 622 | # print(hls_pull_url+'直播流链接存在') 623 | else: 624 | # print(hls_pull_url+'直播流链接不存在') 625 | valueB=False 626 | 627 | if valueA and valueB: 628 | urlA=hls_pull_url 629 | urlB=rtmp_pull_url 630 | elif valueA and valueB==False: 631 | urlA=hls_pull_url 632 | urlB=hls_pull_url 633 | elif valueA==False and valueB: 634 | urlA=rtmp_pull_url 635 | urlB=rtmp_pull_url 636 | else: #都不存在 637 | urlA=hls_pull_url 638 | urlB=rtmp_pull_url 639 | 640 | return([zhubo_name,2,urlA,urlB]) #因为上面出错到不了这里. 所以这里直接设置状态为2 641 | else: 642 | continue 643 | 644 | except: 645 | response = requests.head(rtmp_pull_url) 646 | if response.status_code == 200: 647 | # print(rtmp_pull_url+'直播流链接存在') 648 | valueA=True 649 | else: 650 | # print(rtmp_pull_url+'直播流链接不存在') 651 | valueA=False 652 | 653 | response = requests.head(hls_pull_url) 654 | if response.status_code == 200: 655 | valueB=True 656 | # print(hls_pull_url+'直播流链接存在') 657 | else: 658 | # print(hls_pull_url+'直播流链接不存在') 659 | valueB=False 660 | 661 | if valueA and valueB: 662 | urlA=hls_pull_url 663 | urlB=rtmp_pull_url 664 | elif valueA and valueB==False: 665 | urlA=hls_pull_url 666 | urlB=hls_pull_url 667 | elif valueA==False and valueB: 668 | urlA=rtmp_pull_url 669 | urlB=rtmp_pull_url 670 | else: #都不存在 671 | urlA=hls_pull_url 672 | urlB=rtmp_pull_url 673 | 674 | return([zhubo_name,4,urlA,urlB]) 675 | pass 676 | # print(zhibo_ids) #如果这个出错,就判断没有播. 只有播放的时候才会出现这个 677 | # response = requests.head(rtmp_pull_url, timeout=10) 678 | # if response.status_code == 200: 679 | # print('直播流链接存在') 680 | # else: 681 | # print('直播流链接不存在') 682 | 683 | 684 | # print(rtmp_pull_url) 685 | # print(hls_pull_url) 686 | return [""] 687 | except Exception as e: 688 | if DEBUG: 689 | print(e) 690 | return [""] 691 | # print('获取real url失败') 692 | return [""] 693 | 694 | print( '......................................................' ) 695 | 696 | def startgo(line,countvariable=-1): 697 | global warning_count 698 | # countvariable=line[1] 699 | # line=line[0] 700 | while True: 701 | try: 702 | # global allLive 703 | recordfinish=False 704 | recordfinish_2=False 705 | counttime=time.time() 706 | global videopath 707 | 708 | if len(line)<2 or type(line)==str: 709 | ridcontent=[line,""] 710 | else: 711 | ridcontent = line 712 | rid=ridcontent[0] 713 | if ridcontent[1]!="": 714 | print("运行新线程,传入地址"+ridcontent[0],ridcontent[1]+" 序号"+str(countvariable)) 715 | else: 716 | print("运行新线程,传入地址"+ridcontent[0]+" 序号"+str(countvariable)) 717 | 718 | #设置一个数组来获取端口参数 719 | portInfo=[] 720 | startname="" 721 | # changestaute="" 722 | Runonce=False 723 | while True: 724 | try: 725 | if ridcontent[0].find("https://live.douyin.com/")>-1: 726 | #获取源码 727 | codePath=SplicingUrl(ridcontent[0]) 728 | # print("codepath:"+codePath) 729 | # #浏览器方案 730 | if onlybrowser: 731 | # dycode=chromeAuto(codePath) 732 | pass 733 | # cookies方案 734 | else: 735 | with semaphore: 736 | dycode=newgeturl(codePath) 737 | # print("获取网页信息完毕") 738 | #获取端口信息 739 | portInfo=getStream_url(dycode) 740 | elif ridcontent[0].find("https://v.douyin.com/")>-1: 741 | portInfo=C_real_url.douyin(ridcontent[0]) 742 | # print("portInfo",portInfo) 743 | else: 744 | portInfo="" 745 | 746 | # print("端口信息:"+str(portInfo)) 747 | # 判断返回参数数组长度,防止出错闪退 748 | if len(portInfo)==4: 749 | #判断状态码 750 | startname=portInfo[0] 751 | if startname in allLive: 752 | print("新增的地址: %s 已经存在,本条线程将会退出"%startname) 753 | namelist.append(str(rid)+"|"+str("#"+rid)) 754 | exit(0) 755 | if ridcontent[1]=="" and Runonce==False: 756 | namelist.append(str(ridcontent[0])+"|"+str(ridcontent[0]+",主播: "+startname.strip())) 757 | Runonce=True 758 | 759 | if portInfo[1]==2: 760 | print(portInfo[0]+" 正在直播中 序号"+str(countvariable)) 761 | 762 | 763 | #是否显示地址 764 | if videom3u8: 765 | if videosavetype=="FLV": 766 | print(str(portInfo[0])+ " 直播地址为:"+str(portInfo[3])) 767 | else: 768 | print(str(portInfo[0])+ " 直播地址为:"+str(portInfo[2])) 769 | 770 | #-------------------- 771 | #“portInfo[2]”对应的是m3u8地址 772 | real_url=portInfo[2] 773 | # changestaute=portInfo[1] 774 | if real_url!="": 775 | now = time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime(time.time())) 776 | try: 777 | if len(videopath)>0: 778 | if videopath[-1]!="/": 779 | videopath=videopath+"/" 780 | if not os.path.exists(videopath+startname): 781 | os.makedirs(videopath+startname) 782 | else: 783 | if not os.path.exists(startname): 784 | os.makedirs('./'+startname) 785 | 786 | except Exception as e: 787 | print("路径错误信息708: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 788 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 789 | 790 | 791 | if not os.path.exists(videopath+startname): 792 | print("保存路径不存在,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置") 793 | videopath="" 794 | print("因为配置文件的路径错误,本次录制在程序目录") 795 | 796 | 797 | if videosavetype=="FLV": 798 | filename=startname + '_' + now + '.flv' 799 | filenameshort=startname + '_' + now 800 | 801 | if len(videopath)==0: 802 | print("\r"+startname+" 录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename) 803 | else: 804 | print("\r"+startname+" 录制视频中: "+videopath+startname +'/'+ filename) 805 | 806 | if not os.path.exists(videopath+startname): 807 | print("目录均不能生成文件,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置,程序将会退出") 808 | input("请按回车退出") 809 | os._exit(0) 810 | #flv录制格式 811 | 812 | filenameshort=videopath+startname +'/'+ startname + '_' + now 813 | 814 | if creatTimeFile: 815 | filenamegruop=[startname,filenameshort] 816 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 817 | createVar[str(filenameshort)].daemon=True 818 | createVar[str(filenameshort)].start() 819 | 820 | 821 | try: 822 | #“portInfo[3]”对应的是flv地址,使用老方法下载(直接请求下载flv)只能是下载flv流的。 823 | real_url=portInfo[3] 824 | real_url=real_url.replace("/u0026","&") 825 | #real_url='"'+real_url+'"' 826 | recording.add(startname) 827 | #老的下载方法 828 | _filepath, _ = urllib.request.urlretrieve(real_url, videopath+startname +'/'+ filename) 829 | # videodownload(real_url, videopath+startname +'/'+ filename,0) 830 | recordfinish=True 831 | recordfinish_2=True 832 | counttime=time.time() 833 | if startname in recording: 834 | recording.remove(startname) 835 | if startname in unrecording: 836 | unrecording.add(startname) 837 | 838 | except: 839 | print('\r'+time.strftime('%Y-%m-%d %H:%M:%S ') +startname + ' 未开播') 840 | if startname in recording: 841 | recording.remove(startname) 842 | if startname in unrecording: 843 | unrecording.add(startname) 844 | 845 | elif videosavetype=="MKV": 846 | filename=startname + '_' + now + ".mkv" 847 | if len(videopath)==0: 848 | print("\r"+startname+ " 录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename) 849 | else: 850 | print("\r"+startname+ " 录制视频中: "+videopath+startname +'/'+ filename) 851 | 852 | 853 | ffmpeg_path = "ffmpeg" 854 | file = videopath+startname +'/'+ filename 855 | 856 | filenameshort=videopath+startname +'/'+ startname + '_' + now 857 | 858 | 859 | if creatTimeFile: 860 | filenamegruop=[startname,filenameshort] 861 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 862 | createVar[str(filenameshort)].daemon=True 863 | createVar[str(filenameshort)].start() 864 | 865 | 866 | 867 | 868 | try: 869 | real_url=real_url.replace("/u0026","&") 870 | #real_url='"'+real_url+'"' 871 | recording.add(startname) 872 | _output = subprocess.check_output([ 873 | ffmpeg_path, "-y", 874 | "-v","verbose", 875 | "-rw_timeout","10000000", # 10s 876 | "-loglevel","error", 877 | "-hide_banner", 878 | "-user_agent",headers["User-Agent"], 879 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 880 | "-thread_queue_size","1024", 881 | "-analyzeduration","2147483647", 882 | "-probesize","2147483647", 883 | "-fflags","+discardcorrupt", 884 | "-i",real_url, 885 | "-bufsize","5000k", 886 | "-map","0", 887 | "-sn","-dn", 888 | # "-bsf:v","h264_mp4toannexb", 889 | # "-c","copy", 890 | #"-c:v","libx264", #后期可以用crf来控制大小 891 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 892 | "-c:v","copy", #直接用copy的话体积特别大. 893 | "-c:a","copy", 894 | "-max_muxing_queue_size","64", 895 | "-correct_ts_overflow","1", 896 | "-f","matroska", 897 | "{path}".format(path=file), 898 | ], stderr = subprocess.STDOUT) 899 | 900 | recordfinish=True 901 | recordfinish_2=True 902 | counttime=time.time() 903 | if startname in recording: 904 | recording.remove(startname) 905 | if startname in unrecording: 906 | unrecording.add(startname) 907 | except subprocess.CalledProcessError as e: 908 | #logging.warning(str(e.output)) 909 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 910 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 911 | if startname in recording: 912 | recording.remove(startname) 913 | if startname in unrecording: 914 | unrecording.add(startname) 915 | 916 | elif videosavetype=="MP4": 917 | filename=startname + '_' + now + ".mp4" 918 | if len(videopath)==0: 919 | print("\r"+startname+ " 录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename) 920 | else: 921 | print("\r"+startname+ " 录制视频中: "+videopath+startname +'/'+ filename) 922 | 923 | 924 | ffmpeg_path = "ffmpeg" 925 | file = videopath+startname +'/'+ filename 926 | 927 | filenameshort=videopath+startname +'/'+ startname + '_' + now 928 | 929 | 930 | if creatTimeFile: 931 | filenamegruop=[startname,filenameshort] 932 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 933 | createVar[str(filenameshort)].daemon=True 934 | createVar[str(filenameshort)].start() 935 | 936 | 937 | 938 | 939 | try: 940 | real_url=real_url.replace("/u0026","&") 941 | #real_url='"'+real_url+'"' 942 | recording.add(startname) 943 | _output = subprocess.check_output([ 944 | ffmpeg_path, "-y", 945 | "-v","verbose", 946 | "-rw_timeout","10000000", # 10s 947 | "-loglevel","error", 948 | "-hide_banner", 949 | "-user_agent",headers["User-Agent"], 950 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 951 | "-thread_queue_size","1024", 952 | "-analyzeduration","2147483647", 953 | "-probesize","2147483647", 954 | "-fflags","+discardcorrupt", 955 | "-i",real_url, 956 | "-bufsize","5000k", 957 | "-map","0", 958 | "-sn","-dn", 959 | # "-bsf:v","h264_mp4toannexb", 960 | # "-c","copy", 961 | #"-c:v","libx264", #后期可以用crf来控制大小 962 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 963 | "-c:v","copy", #直接用copy的话体积特别大. 964 | "-c:a","copy", 965 | "-max_muxing_queue_size","64", 966 | "-correct_ts_overflow","1", 967 | "-f","mp4", 968 | "{path}".format(path=file), 969 | ], stderr = subprocess.STDOUT) 970 | 971 | recordfinish=True 972 | recordfinish_2=True 973 | counttime=time.time() 974 | if startname in recording: 975 | recording.remove(startname) 976 | if startname in unrecording: 977 | unrecording.add(startname) 978 | except subprocess.CalledProcessError as e: 979 | #logging.warning(str(e.output)) 980 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 981 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 982 | if startname in recording: 983 | recording.remove(startname) 984 | if startname in unrecording: 985 | unrecording.add(startname) 986 | 987 | elif videosavetype=="MKV音频": 988 | filename=startname + '_' + now + ".mkv" 989 | if len(videopath)==0: 990 | print("\r"+startname+" 录制MKV音频中: "+os.getcwd()+"/"+startname +'/'+ filename) 991 | else: 992 | print("\r"+startname+" 录制MKV音频中: "+videopath+startname +'/'+ filename) 993 | 994 | ffmpeg_path = "ffmpeg" 995 | file = videopath+startname +'/'+ filename 996 | try: 997 | real_url=real_url.replace("/u0026","&") 998 | #real_url='"'+real_url+'"' 999 | recording.add(startname) 1000 | _output = subprocess.check_output([ 1001 | ffmpeg_path,"-y", 1002 | "-v","verbose", 1003 | "-rw_timeout","10000000", # 10s 1004 | "-loglevel","error", 1005 | "-hide_banner", 1006 | "-user_agent",headers["User-Agent"], 1007 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1008 | "-thread_queue_size","1024", 1009 | "-analyzeduration","2147483647", 1010 | "-probesize","2147483647", 1011 | "-fflags","+discardcorrupt", 1012 | "-i",real_url, 1013 | "-bufsize","5000k", 1014 | "-map","0:a", 1015 | "-sn","-dn", 1016 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1017 | "-c:a","copy", 1018 | "-max_muxing_queue_size","64", 1019 | "-correct_ts_overflow","1", 1020 | "-f","matroska", 1021 | "{path}".format(path=file), 1022 | ], stderr = subprocess.STDOUT) 1023 | 1024 | 1025 | recordfinish=True 1026 | recordfinish_2=True 1027 | counttime=time.time() 1028 | if startname in recording: 1029 | recording.remove(startname) 1030 | if startname in unrecording: 1031 | unrecording.add(startname) 1032 | if tsconvertm4a: 1033 | threading.Thread(target=convertsm4a,args=(file,)).start() 1034 | except subprocess.CalledProcessError as e: 1035 | #logging.warning(str(e.output)) 1036 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1037 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1038 | if startname in recording: 1039 | recording.remove(startname) 1040 | if startname in unrecording: 1041 | unrecording.add(startname) 1042 | 1043 | elif videosavetype=="TS音频": 1044 | filename=startname + '_' + now + ".ts" 1045 | if len(videopath)==0: 1046 | print("\r"+startname+" 录制TS音频中: "+os.getcwd()+"/"+startname +'/'+ filename) 1047 | else: 1048 | print("\r"+startname+" 录制TS音频中: "+videopath+startname +'/'+ filename) 1049 | 1050 | ffmpeg_path = "ffmpeg" 1051 | file = videopath+startname +'/'+ filename 1052 | try: 1053 | real_url=real_url.replace("/u0026","&") 1054 | #real_url='"'+real_url+'"' 1055 | recording.add(startname) 1056 | _output = subprocess.check_output([ 1057 | ffmpeg_path,"-y", 1058 | "-v","verbose", 1059 | "-rw_timeout","10000000", # 10s 1060 | "-loglevel","error", 1061 | "-hide_banner", 1062 | "-user_agent",headers["User-Agent"], 1063 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1064 | "-thread_queue_size","1024", 1065 | "-analyzeduration","2147483647", 1066 | "-probesize","2147483647", 1067 | "-fflags","+discardcorrupt", 1068 | "-i",real_url, 1069 | "-bufsize","5000k", 1070 | "-map","0:a", 1071 | "-sn","-dn", 1072 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1073 | "-c:a","copy", 1074 | "-max_muxing_queue_size","64", 1075 | "-correct_ts_overflow","1", 1076 | "-f","mpegts", 1077 | "{path}".format(path=file), 1078 | ], stderr = subprocess.STDOUT) 1079 | 1080 | 1081 | recordfinish=True 1082 | recordfinish_2=True 1083 | counttime=time.time() 1084 | if startname in recording: 1085 | recording.remove(startname) 1086 | if startname in unrecording: 1087 | unrecording.add(startname) 1088 | if tsconvertm4a: 1089 | threading.Thread(target=convertsm4a,args=(file,)).start() 1090 | except subprocess.CalledProcessError as e: 1091 | #logging.warning(str(e.output)) 1092 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1093 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1094 | if startname in recording: 1095 | recording.remove(startname) 1096 | if startname in unrecording: 1097 | unrecording.add(startname) 1098 | 1099 | 1100 | 1101 | else: 1102 | 1103 | if(Splitvideobysize): #这里默认是启用/不启用视频分割功能 1104 | while(True): 1105 | now = time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime(time.time())) 1106 | filename=startname + '_' + now + ".ts" 1107 | if len(videopath)==0: 1108 | print("\r"+startname+" 分段录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename + " 每录满: %d M 存一个视频"%Splitsize ) 1109 | else: 1110 | print("\r"+startname+" 分段录制视频中: "+videopath+startname +'/'+ filename+ " 每录满: %d M 存一个视频"%Splitsize) 1111 | 1112 | ffmpeg_path = "ffmpeg" 1113 | file = videopath+startname +'/'+ filename 1114 | filenameshort=videopath+startname +'/'+ startname + '_' + now 1115 | 1116 | if creatTimeFile: 1117 | filenamegruop=[startname,filenameshort] 1118 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 1119 | createVar[str(filenameshort)].daemon=True 1120 | createVar[str(filenameshort)].start() 1121 | 1122 | 1123 | try: 1124 | real_url=real_url.replace("/u0026","&") 1125 | #real_url='"'+real_url+'"' 1126 | recording.add(startname) 1127 | _output = subprocess.check_output([ 1128 | ffmpeg_path, "-y", 1129 | "-v","verbose", 1130 | "-rw_timeout","10000000", # 10s 1131 | "-loglevel","error", 1132 | "-hide_banner", 1133 | "-user_agent",headers["User-Agent"], 1134 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1135 | "-thread_queue_size","1024", 1136 | "-analyzeduration","2147483647", 1137 | "-probesize","2147483647", 1138 | "-fflags","+discardcorrupt", 1139 | "-i",real_url, 1140 | "-bufsize","5000k", 1141 | "-map","0", 1142 | "-sn","-dn", 1143 | # "-bsf:v","h264_mp4toannexb", 1144 | # "-c","copy", 1145 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1146 | "-c:v","copy", 1147 | "-c:a","copy", 1148 | "-max_muxing_queue_size","64", 1149 | "-correct_ts_overflow","1", 1150 | "-f","mpegts", 1151 | "-fs",str(Splitsizes), 1152 | "{path}".format(path=file), 1153 | ], stderr = subprocess.STDOUT) 1154 | 1155 | 1156 | recordfinish=True #这里表示正常录制成功一次 1157 | recordfinish_2=True 1158 | counttime=time.time() #这个记录当前时间, 用于后面 1分钟内快速2秒循环 这个值不能放到后面 1159 | if startname in recording: 1160 | recording.remove(startname) 1161 | if startname in unrecording: 1162 | unrecording.add(startname) 1163 | if tsconvertmp4: 1164 | threading.Thread(target=convertsmp4,args=(file,)).start() 1165 | if tsconvertm4a: 1166 | threading.Thread(target=convertsm4a,args=(file,)).start() 1167 | 1168 | except subprocess.CalledProcessError as e: 1169 | #这是里分段 如果录制错误会跳转到这里来 1170 | #logging.warning(str(e.output)) 1171 | # print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1172 | # logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1173 | if startname in recording: 1174 | recording.remove(startname) 1175 | if startname in unrecording: 1176 | unrecording.add(startname) 1177 | break 1178 | else: 1179 | filename=startname + '_' + now + ".ts" 1180 | if len(videopath)==0: 1181 | print("\r"+startname+" 录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename) 1182 | else: 1183 | print("\r"+startname+" 录制视频中: "+videopath+startname +'/'+ filename) 1184 | 1185 | ffmpeg_path = "ffmpeg" 1186 | file = videopath+startname +'/'+ filename 1187 | filenameshort=videopath+startname +'/'+ startname + '_' + now 1188 | 1189 | if creatTimeFile: 1190 | filenamegruop=[startname,filenameshort] 1191 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 1192 | createVar[str(filenameshort)].daemon=True 1193 | createVar[str(filenameshort)].start() 1194 | 1195 | 1196 | 1197 | try: 1198 | real_url=real_url.replace("/u0026","&") 1199 | # real_url="\""+real_url+"\"" 1200 | #real_url='"'+real_url+'"' 1201 | # print(real_url) 1202 | recording.add(startname) 1203 | 1204 | 1205 | 1206 | 1207 | abc=[ 1208 | ffmpeg_path, "-y", 1209 | "-v","verbose", 1210 | "-rw_timeout","10000000", # 10s 1211 | "-loglevel","error", 1212 | "-hide_banner", 1213 | "-user_agent",headers["User-Agent"], 1214 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1215 | "-thread_queue_size","1024", 1216 | "-analyzeduration","2147483647", 1217 | "-probesize","2147483647", 1218 | "-fflags","+discardcorrupt", 1219 | "-i",real_url, 1220 | "-bufsize","5000k", 1221 | "-map","0", 1222 | "-sn","-dn", 1223 | # "-bsf:v","h264_mp4toannexb", 1224 | # "-c","copy", 1225 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1226 | "-c:v","copy", 1227 | "-c:a","copy", 1228 | "-max_muxing_queue_size","64", 1229 | "-correct_ts_overflow","1", 1230 | "-f","mpegts", 1231 | "{path}".format(path=file),] 1232 | print(abc) 1233 | 1234 | _output = subprocess.check_output([ 1235 | ffmpeg_path, "-y", 1236 | "-v","verbose", 1237 | "-rw_timeout","10000000", # 10s 1238 | "-loglevel","error", 1239 | "-hide_banner", 1240 | "-user_agent",headers["User-Agent"], 1241 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1242 | "-thread_queue_size","1024", 1243 | "-analyzeduration","2147483647", 1244 | "-probesize","2147483647", 1245 | "-fflags","+discardcorrupt", 1246 | "-i",real_url, 1247 | "-bufsize","5000k", 1248 | "-map","0", 1249 | "-sn","-dn", 1250 | # "-bsf:v","h264_mp4toannexb", 1251 | # "-c","copy", 1252 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1253 | "-c:v","copy", 1254 | "-c:a","copy", 1255 | "-max_muxing_queue_size","64", 1256 | "-correct_ts_overflow","1", 1257 | "-f","mpegts", 1258 | "{path}".format(path=file), 1259 | ], stderr = subprocess.STDOUT) 1260 | 1261 | 1262 | recordfinish=True 1263 | recordfinish_2=True 1264 | counttime=time.time() 1265 | if startname in recording: 1266 | recording.remove(startname) 1267 | if startname in unrecording: 1268 | unrecording.add(startname) 1269 | if tsconvertmp4: 1270 | threading.Thread(target=convertsmp4,args=(file,)).start() 1271 | if tsconvertm4a: 1272 | threading.Thread(target=convertsm4a,args=(file,)).start() 1273 | 1274 | 1275 | except subprocess.CalledProcessError as e: 1276 | #logging.warning(str(e.output)) 1277 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1278 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1279 | if startname in recording: 1280 | recording.remove(startname) 1281 | if startname in unrecording: 1282 | unrecording.add(startname) 1283 | 1284 | else: 1285 | print('直播间不存在或未开播') 1286 | pass 1287 | 1288 | if recordfinish_2==True: 1289 | if startname in recording: 1290 | recording.remove(startname) 1291 | if startname in unrecording: 1292 | unrecording.add(startname) 1293 | print('\n'+startname+" "+ time.strftime('%Y-%m-%d %H:%M:%S ') + '直播录制完成\n') 1294 | recordfinish_2=False 1295 | 1296 | else: 1297 | print("序号"+str(countvariable) + " " + portInfo[0] + " 等待直播.. ") 1298 | startname=portInfo[0] 1299 | 1300 | 1301 | else: 1302 | if len(startname)==0: 1303 | print('序号'+ str(countvariable) +' 网址内容获取失败,进行重试中...获取失败的地址是:'+ str(line)) 1304 | else: 1305 | print('序号'+ str(countvariable) +' 网址内容获取失败,进行重试中...获取失败的地址是:'+ str(line)+" 主播为:"+str(startname)) 1306 | warning_count+=1 1307 | 1308 | except Exception as e: 1309 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1310 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1311 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1312 | warning_count+=1 1313 | 1314 | 1315 | num = random.randint(-5, 5) + delaydefault # 生成-5到5的随机数,加上delaydefault 1316 | if num < 0: # 如果得到的结果小于0,则将其设置为0 1317 | num = 0 1318 | x=num 1319 | 1320 | # 如果出错太多,就加秒数 1321 | if warning_count>100: 1322 | x=x+60 1323 | print("瞬时错误太多,延迟加60秒") 1324 | 1325 | 1326 | #这里是.如果录制结束后,循环时间会暂时变成30s后检测一遍. 这样一定程度上防止主播卡顿造成少录 1327 | #当30秒过后检测一遍后. 会回归正常设置的循环秒数 1328 | if recordfinish==True: 1329 | counttimeend=time.time()-counttime 1330 | if counttimeend<60: 1331 | x=30 #现在改成默认20s 1332 | recordfinish=False 1333 | else: 1334 | recordfinish=False 1335 | else: 1336 | x=num 1337 | 1338 | #这里是正常循环 1339 | while x: 1340 | x = x-1 1341 | # print('\r循环等待%d秒 '%x) 1342 | if looptime: 1343 | print('\r'+startname+' 循环等待%d秒 '%x ,end="") 1344 | time.sleep(1) 1345 | if looptime: 1346 | print('\r检测直播间中...',end="") 1347 | except Exception as e: 1348 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1349 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1350 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1351 | print("线程崩溃2秒后重试.错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1352 | warning_count+=1 1353 | time.sleep(2) 1354 | def searchUserWeb(url): 1355 | # re=requests.get("https://www.douyin.com/user/MS4wLjABAAAAfSxBYimoCmZ8PjEV0yd0ETo9gZHW9DJCHO0arjojTuQ") 1356 | try: 1357 | 1358 | # headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76'} 1359 | headers3 = {"referer": "https://www.douyin.com/", 1360 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', 1361 | "cookie":cookiesSet} 1362 | 1363 | 1364 | # re=requests.get(url=url,proxies=None) 1365 | re=requests.get(url=url,headers=headers3) 1366 | print(re.text) 1367 | p1=re.text.find(">300: 1375 | res="" 1376 | print("主页解析未完成,有可能是没有在直播.直播时会自动转成直播间网址:"+url) 1377 | else: 1378 | print("主页解析完成:"+url) 1379 | 1380 | url=res 1381 | except Exception as e: 1382 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1383 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1384 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1385 | # print(res) 1386 | return url 1387 | 1388 | ''' 1389 | def checkUrlTxt(UrlTxt): 1390 | lastUrlGroup=[] 1391 | try: 1392 | if len(UrlTxt)<=30: #短连接 1393 | # chrome = webdriver.Chrome(service=ChromeDrivePath,options=chrome_options) 1394 | if displayChrome: 1395 | #是否有浏览器设置 1396 | chrome = webdriver.Chrome() 1397 | else: 1398 | chrome = webdriver.Chrome(options=chrome_options) 1399 | 1400 | try: 1401 | chrome.get(UrlTxt) # 往浏览器的网页地址栏填入url参数 1402 | # 设置显式等待,超时时长最大为5s,每隔1s查找元素一次 1403 | # WebDriverWait(chrome,5,1).until(EC.presence_of_element_located(("class name","fpRIB_wC"))) 1404 | time.sleep(1) 1405 | dyUrl=chrome.current_url 1406 | chrome.quit() 1407 | except: 1408 | dyUrl=chrome.current_url 1409 | print("超时了") 1410 | chrome.quit() 1411 | 1412 | 1413 | # 获取全部标签页 1414 | 1415 | # time.sleep(2) 1416 | # 将激活标签页设置为最新的一项(按自己业务改) 1417 | # chrome.switch_to.window(window.pop()) 1418 | 1419 | position1=dyUrl.find("https://live.douyin.com/") 1420 | if position1>-1: #表示是正常跳转过后的地址 1421 | position2=dyUrl.find("?") 1422 | if position2>-1: 1423 | lastUrl=dyUrl[0:position2] 1424 | lastUrlGroup=[lastUrl,'1'] #1标识为有替换长连接 1425 | else: 1426 | lastUrlGroup=[] #1标识为有替换长连接 1427 | else: 1428 | lastUrlGroup=[] #1标识为有替换长连接 1429 | 1430 | else: 1431 | lastUrlGroup=[] 1432 | 1433 | except Exception as e: 1434 | chrome.quit() 1435 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1436 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1437 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1438 | lastUrlGroup=[] 1439 | 1440 | return(lastUrlGroup) 1441 | ''' 1442 | 1443 | start5 = datetime.datetime.now() 1444 | def displayinfo(): 1445 | global start5 1446 | time.sleep(5) 1447 | while True: 1448 | try: 1449 | time.sleep(5) 1450 | os.system("cls") 1451 | # print("循环值守录制抖音直播 版本:%s"%str(version)) 1452 | print("\r共监测"+str(Monitoring)+"个直播中", end=" | ") 1453 | #以前的信息 1454 | print("同一时间访问网络的线程数:",maxrequest, end=" | ") 1455 | if len(videopath)>0: 1456 | if not os.path.exists(videopath): 1457 | print("配置文件里,直播保存路径并不存在,请重新输入一个正确的路径.或留空表示当前目录,按回车退出") 1458 | input("程序结束") 1459 | os._exit(0) 1460 | else: 1461 | print("视频保存路径: "+videopath, end=" | ") 1462 | pass 1463 | else: 1464 | print("视频保存路径: 当前目录", end=" | ") 1465 | 1466 | 1467 | if Splitvideobysize: 1468 | print("TS录制分段开启,录制分段大小为 %d M"%Splitsize, end=" | ") 1469 | 1470 | if onlybrowser: 1471 | print("浏览器检测录制", end=" | ") 1472 | else: 1473 | print("Cookies录制", end=" | ") 1474 | 1475 | 1476 | 1477 | 1478 | 1479 | print("录制视频质量为: "+str(videoQuality), end=" | ") 1480 | print("录制视频格式为: "+str(videosavetype), end=" | ") 1481 | # print("同一时间访问网络的线程数为: "+str(maxrequest), end=" | ") 1482 | print("目前瞬时错误数为: "+str(warning_count), end=" | ") 1483 | nowdate=time.strftime("%H:%M:%S", time.localtime()) 1484 | print(str(nowdate)) 1485 | if len(recording)==0 and len(unrecording)==0: 1486 | time.sleep(5) 1487 | print("\r没有正在录制的直播 "+nowdate,end="") 1488 | print("") 1489 | 1490 | continue 1491 | else: 1492 | # livetask = len(recording) + len(unrecording) 1493 | # print("正则监控{}个直播".format(str(livetask))) 1494 | if len(recording)>0: 1495 | print("x"*60) 1496 | NoRepeatrecording = list(set(recording)) 1497 | print("正在录制{}个直播: ".format(str(len(NoRepeatrecording)))) 1498 | for x in NoRepeatrecording: 1499 | print(x+" 正在录制中") 1500 | #print("%i个直播正在录制中: "%len(NoRepeatrecording)+nowdate) 1501 | end = datetime.datetime.now() 1502 | print('总共录制时间: ' +str(end - start5)) 1503 | print("x"*60) 1504 | else: 1505 | start5 = datetime.datetime.now() 1506 | except Exception as e: 1507 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1508 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1509 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1510 | 1511 | #-------------------------------------------------------------------------------------------------------------------------------------------- 1512 | #最开始运行下面的 1513 | 1514 | 1515 | #备份 1516 | t3 = threading.Thread(target=backup_file_start, args=(), daemon=True) 1517 | t3.start() 1518 | 1519 | #检测ffmpeg是否存在 1520 | ffmepgFileCheck= subprocess.getoutput(["ffmpeg"]) 1521 | if ffmepgFileCheck.find("run")>-1: 1522 | # print("ffmpeg存在") 1523 | pass 1524 | else: 1525 | print("重要提示:") 1526 | print("检测到ffmpeg不存在,请将ffmpeg.exe放到同目录,或者设置为环境变量,没有ffmpeg将无法录制") 1527 | 1528 | Monitoring=0 1529 | while True: 1530 | #循环读取配置 1531 | try: 1532 | f =open("config.ini",'r', encoding='utf-8-sig') 1533 | f.close() 1534 | 1535 | except IOError: 1536 | f = open("config.ini",'w', encoding='utf-8-sig') 1537 | f.close() 1538 | 1539 | 1540 | try: 1541 | config = configparser.ConfigParser() 1542 | config.read('config.ini', encoding='utf-8-sig') 1543 | rid=config.get('1', '直播地址') 1544 | except: 1545 | rid="" 1546 | 1547 | 1548 | if os.path.isfile("URL_config.ini"): 1549 | f =open("URL_config.ini",'r', encoding='utf-8-sig') 1550 | inicontent=f.read() 1551 | f.close() 1552 | else: 1553 | inicontent="" 1554 | 1555 | 1556 | 1557 | if len(inicontent)==0: 1558 | print('请输入要录制的抖音主播pc端直播网址.请注意备份url_config.ini,最近版本增加短连接可能会误删配置里的内容:') 1559 | inurl=input() 1560 | f = open("URL_config.ini",'a+',encoding='utf-8-sig') 1561 | f.write(inurl) 1562 | f.close() 1563 | 1564 | config = configparser.ConfigParser() 1565 | 1566 | config.read('config.ini', encoding='utf-8-sig') 1567 | listx = [] 1568 | listx = config.sections()# 获取到配置文件中所有分组名称 1569 | if '1' not in listx:# 如果分组type不存在则插入type分组 1570 | config.add_section('1') 1571 | 1572 | else: 1573 | config.remove_option('1', '直播地址')# 删除type分组的stuno 1574 | # config.remove_section('tpye')# 删除配置文件中type分组 1575 | 1576 | #config.set('1', '直播地址', inurl)# 给type分组设置值 1577 | config.set('1', '循环时间(秒)', '60')# 给type分组设置值 1578 | 1579 | o = open('config.ini', 'w',encoding='utf-8-sig') 1580 | 1581 | config.write(o) 1582 | 1583 | o.close()#不要忘记关闭 1584 | 1585 | 1586 | 1587 | try: 1588 | config = configparser.ConfigParser() 1589 | config.read('config.ini', encoding='utf-8-sig') 1590 | maxrequest=config.getint('1', '同一时间访问网络的线程数') 1591 | except: 1592 | config = configparser.ConfigParser() 1593 | # -read读取ini文件 1594 | config.read('config.ini', encoding='utf-8-sig') 1595 | listx = [] 1596 | listx = config.sections()# 获取到配置文件中所有分组名称 1597 | if '1' not in listx:# 如果分组type不存在则插入type分组 1598 | config.add_section('1') 1599 | 1600 | else: 1601 | config.remove_option('1', '同一时间访问网络的线程数')# 删除type分组的stuno 1602 | # config.remove_section('tpye')# 删除配置文件中type分组 1603 | 1604 | config.set('1', '同一时间访问网络的线程数', '3')# 给type分组设置值 1605 | 1606 | o = open('config.ini', 'w',encoding='utf-8-sig') 1607 | 1608 | config.write(o) 1609 | 1610 | o.close()#不要忘记关闭 1611 | maxrequest=3 1612 | 1613 | semaphore = threading.Semaphore(maxrequest) 1614 | # print("同一时间访问网络的线程数:",maxrequest) 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | 1622 | try: 1623 | config = configparser.ConfigParser() 1624 | config.read('config.ini', encoding='utf-8-sig') 1625 | delaydefault=config.getint('1', '循环时间(秒)') 1626 | except: 1627 | config = configparser.ConfigParser() 1628 | # -read读取ini文件 1629 | config.read('config.ini', encoding='utf-8-sig') 1630 | listx = [] 1631 | listx = config.sections()# 获取到配置文件中所有分组名称 1632 | if '1' not in listx:# 如果分组type不存在则插入type分组 1633 | config.add_section('1') 1634 | 1635 | else: 1636 | config.remove_option('1', '循环时间(秒)')# 删除type分组的stuno 1637 | # config.remove_section('tpye')# 删除配置文件中type分组 1638 | 1639 | config.set('1', '循环时间(秒)', '60')# 给type分组设置值 1640 | 1641 | o = open('config.ini', 'w',encoding='utf-8-sig') 1642 | 1643 | config.write(o) 1644 | 1645 | o.close()#不要忘记关闭 1646 | delaydefault=60 1647 | 1648 | 1649 | 1650 | # config.get(section,option) 得到section中option的值,返回为string类型 1651 | # config.getint(section,option) 得到section中option的值,返回为int类型 1652 | # config.getboolean(section,option) 得到section中option的值,返回为bool类型 1653 | # config.getfloat(section,option) 得到section中option的值,返回为float类型 1654 | 1655 | 1656 | try: 1657 | config = configparser.ConfigParser() 1658 | config.read('config.ini', encoding='utf-8-sig') 1659 | localdelaydefault=config.getint('1', '排队读取网址时间(秒)') 1660 | except: 1661 | config = configparser.ConfigParser() 1662 | # -read读取ini文件 1663 | config.read('config.ini', encoding='utf-8-sig') 1664 | listx = [] 1665 | listx = config.sections()# 获取到配置文件中所有分组名称 1666 | if '1' not in listx:# 如果分组type不存在则插入type分组 1667 | config.add_section('1') 1668 | 1669 | else: 1670 | config.remove_option('1', '排队读取网址时间(秒)')# 删除type分组的stuno 1671 | # config.remove_section('tpye')# 删除配置文件中type分组 1672 | 1673 | config.set('1', '排队读取网址时间(秒)', '3')# 给type分组设置值 1674 | 1675 | o = open('config.ini', 'w',encoding='utf-8-sig') 1676 | 1677 | config.write(o) 1678 | 1679 | o.close()#不要忘记关闭 1680 | localdelaydefault=0 1681 | 1682 | 1683 | 1684 | try: 1685 | config = configparser.ConfigParser() 1686 | config.read('config.ini', encoding='utf-8-sig') 1687 | videopath=config.get('1', '直播保存路径') 1688 | except: 1689 | config = configparser.ConfigParser() 1690 | # -read读取ini文件 1691 | config.read('config.ini', encoding='utf-8-sig') 1692 | listx = [] 1693 | listx = config.sections()# 获取到配置文件中所有分组名称 1694 | if '1' not in listx:# 如果分组type不存在则插入type分组 1695 | config.add_section('1') 1696 | else: 1697 | config.remove_option('1', '直播保存路径')# 删除type分组的stuno 1698 | # config.remove_section('tpye')# 删除配置文件中type分组 1699 | 1700 | config.set('1', '直播保存路径', '')# 给type分组设置值 1701 | 1702 | o = open('config.ini', 'w',encoding='utf-8-sig') 1703 | 1704 | config.write(o) 1705 | 1706 | o.close()#不要忘记关闭 1707 | videopath="" 1708 | 1709 | 1710 | 1711 | 1712 | 1713 | try: 1714 | config = configparser.ConfigParser() 1715 | config.read('config.ini', encoding='utf-8-sig') 1716 | videosavetype=config.get('1', '视频保存格式TS|MKV|FLV|MP4|TS音频|MKV音频') 1717 | 1718 | except Exception as _e: 1719 | 1720 | config = configparser.ConfigParser() 1721 | # -read读取ini文件 1722 | config.read('config.ini', encoding='utf-8-sig') 1723 | listx = [] 1724 | listx = config.sections()# 获取到配置文件中所有分组名称 1725 | if '1' not in listx:# 如果分组type不存在则插入type分组 1726 | config.add_section('1') 1727 | 1728 | else: 1729 | config.remove_option('1', '视频保存格式TS|MKV|FLV|MP4|TS音频|MKV音频')# 删除type分组的stuno 1730 | # config.remove_section('tpye')# 删除配置文件中type分组 1731 | 1732 | 1733 | config.set('1', '视频保存格式TS|MKV|FLV|MP4|TS音频|MKV音频',"TS")# 给type分组设置值 1734 | 1735 | o = open('config.ini', 'w',encoding='utf-8-sig') 1736 | 1737 | config.write(o) 1738 | 1739 | o.close()#不要忘记关闭 1740 | videosavetype="TS" 1741 | 1742 | if len(videosavetype)>0: 1743 | if videosavetype.upper().lower()=="FLV".lower(): 1744 | videosavetype="FLV" 1745 | # print("直播视频保存为FLV格式") 1746 | elif videosavetype.upper().lower()=="MKV".lower(): 1747 | videosavetype="MKV" 1748 | # print("直播视频保存为MKV格式") 1749 | elif videosavetype.upper().lower()=="TS".lower(): 1750 | videosavetype="TS" 1751 | # print("直播视频保存为TS格式") 1752 | elif videosavetype.upper().lower()=="MP4".lower(): 1753 | videosavetype="MP4" 1754 | # print("直播视频保存为MP4格式") 1755 | elif videosavetype.upper().lower()=="TS音频".lower(): 1756 | videosavetype="TS音频" 1757 | # print("直播视频保存为TS音频格式") 1758 | elif videosavetype.upper().lower()=="MKV音频".lower(): 1759 | videosavetype="MKV音频" 1760 | # print("直播视频保存为MKV音频格式") 1761 | else: 1762 | videosavetype="TS" 1763 | print("直播视频保存格式设置有问题,这次录制重置为默认的TS格式") 1764 | else: 1765 | videosavetype="TS" 1766 | print("直播视频保存为TS格式") 1767 | #------------------------------------------ 1768 | #选择画质 1769 | try: 1770 | config = configparser.ConfigParser() 1771 | config.read('config.ini', encoding='utf-8-sig') 1772 | videoQuality=config.get('1', '原画|超清|高清|标清') 1773 | 1774 | except Exception as _e: 1775 | 1776 | config = configparser.ConfigParser() 1777 | # -read读取ini文件 1778 | config.read('config.ini', encoding='utf-8-sig') 1779 | listx = [] 1780 | listx = config.sections()# 获取到配置文件中所有分组名称 1781 | if '1' not in listx:# 如果分组type不存在则插入type分组 1782 | config.add_section('1') 1783 | 1784 | else: 1785 | config.remove_option('1', '原画|超清|高清|标清')# 删除type分组的stuno 1786 | # config.remove_section('tpye')# 删除配置文件中type分组 1787 | 1788 | 1789 | config.set('1', '原画|超清|高清|标清',"原画")# 给type分组设置值 1790 | 1791 | o = open('config.ini', 'w',encoding='utf-8-sig') 1792 | 1793 | config.write(o) 1794 | 1795 | o.close()#不要忘记关闭 1796 | videoQuality="原画" 1797 | 1798 | #是否显示直播地址 1799 | try: 1800 | config = configparser.ConfigParser() 1801 | config.read('config.ini', encoding='utf-8-sig') 1802 | videom3u8=config.get('1', '是否显示直播地址') 1803 | 1804 | except Exception as _e: 1805 | 1806 | config = configparser.ConfigParser() 1807 | # -read读取ini文件 1808 | config.read('config.ini', encoding='utf-8-sig') 1809 | listx = [] 1810 | listx = config.sections()# 获取到配置文件中所有分组名称 1811 | if '1' not in listx:# 如果分组type不存在则插入type分组 1812 | config.add_section('1') 1813 | 1814 | else: 1815 | config.remove_option('1', '是否显示直播地址')# 删除type分组的stuno 1816 | # config.remove_section('tpye')# 删除配置文件中type分组 1817 | 1818 | 1819 | config.set('1', '是否显示直播地址',"否")# 给type分组设置值 1820 | 1821 | o = open('config.ini', 'w',encoding='utf-8-sig') 1822 | 1823 | config.write(o) 1824 | 1825 | o.close()#不要忘记关闭 1826 | videom3u8="否" 1827 | 1828 | 1829 | if videom3u8=="是": 1830 | videom3u8=True 1831 | else: 1832 | videom3u8=False 1833 | 1834 | 1835 | 1836 | 1837 | 1838 | 1839 | #是否显示循环秒数 1840 | try: 1841 | config = configparser.ConfigParser() 1842 | config.read('config.ini', encoding='utf-8-sig') 1843 | looptime=config.get('1', '是否显示循环秒数') 1844 | 1845 | except Exception as _e: 1846 | 1847 | config = configparser.ConfigParser() 1848 | # -read读取ini文件 1849 | config.read('config.ini', encoding='utf-8-sig') 1850 | listx = [] 1851 | listx = config.sections()# 获取到配置文件中所有分组名称 1852 | if '1' not in listx:# 如果分组type不存在则插入type分组 1853 | config.add_section('1') 1854 | 1855 | else: 1856 | config.remove_option('1', '是否显示循环秒数')# 删除type分组的stuno 1857 | # config.remove_section('tpye')# 删除配置文件中type分组 1858 | 1859 | 1860 | config.set('1', '是否显示循环秒数',"否")# 给type分组设置值 1861 | 1862 | o = open('config.ini', 'w',encoding='utf-8-sig') 1863 | 1864 | config.write(o) 1865 | 1866 | o.close()#不要忘记关闭 1867 | looptime="否" 1868 | 1869 | 1870 | if looptime=="是": 1871 | looptime=True 1872 | else: 1873 | looptime=False 1874 | 1875 | 1876 | # #这里是控制是否设置代理 1877 | # try: 1878 | # config = configparser.ConfigParser() 1879 | # config.read('config.ini', encoding='utf-8-sig') 1880 | # proxies2=config.get('1', '本地代理端口') 1881 | # proxiesn=proxies2 1882 | # if len(proxies2)>0: 1883 | 1884 | # proxies2={'https': 'http://127.0.0.1:'+str(proxies2)} 1885 | # print("检测到有设置代理地址为: "+'http://127.0.0.1:'+str(proxiesn)) 1886 | 1887 | # except Exception as _e: 1888 | 1889 | # config = configparser.ConfigParser() 1890 | # # -read读取ini文件 1891 | # config.read('config.ini', encoding='utf-8-sig') 1892 | # listx = [] 1893 | # listx = config.sections()# 获取到配置文件中所有分组名称 1894 | # if '1' not in listx:# 如果分组type不存在则插入type分组 1895 | # config.add_section('1') 1896 | 1897 | # else: 1898 | # config.remove_option('1', '本地代理端口')# 删除type分组的stuno 1899 | # # config.remove_section('tpye')# 删除配置文件中type分组 1900 | 1901 | 1902 | # config.set('1', '本地代理端口',"")# 给type分组设置值 1903 | 1904 | # o = open('config.ini', 'w',encoding='utf-8-sig') 1905 | 1906 | # config.write(o) 1907 | 1908 | # o.close()#不要忘记关闭 1909 | 1910 | # proxies2="" 1911 | 1912 | 1913 | #这里是控制TS是否分段 1914 | try: 1915 | config = configparser.ConfigParser() 1916 | config.read('config.ini', encoding='utf-8-sig') 1917 | Splitvideobysize=config.get('1', 'TS格式分段录制是否开启') 1918 | 1919 | except Exception as _e: 1920 | config = configparser.ConfigParser() 1921 | # -read读取ini文件 1922 | config.read('config.ini', encoding='utf-8-sig') 1923 | listx = [] 1924 | listx = config.sections()# 获取到配置文件中所有分组名称 1925 | if '1' not in listx:# 如果分组type不存在则插入type分组 1926 | config.add_section('1') 1927 | 1928 | else: 1929 | config.remove_option('1', 'TS格式分段录制是否开启')# 删除type分组的stuno 1930 | # config.remove_section('tpye')# 删除配置文件中type分组 1931 | 1932 | 1933 | config.set('1', 'TS格式分段录制是否开启',"否")# 给type分组设置值 1934 | 1935 | o = open('config.ini', 'w',encoding='utf-8-sig') 1936 | 1937 | config.write(o) 1938 | 1939 | o.close()#不要忘记关闭 1940 | Splitvideobysize="否" 1941 | 1942 | 1943 | if Splitvideobysize=="是": 1944 | Splitvideobysize=True 1945 | else: 1946 | Splitvideobysize=False 1947 | 1948 | 1949 | 1950 | 1951 | #这里是控制TS分段大小 1952 | 1953 | try: 1954 | config = configparser.ConfigParser() 1955 | config.read('config.ini', encoding='utf-8-sig') 1956 | Splitsize=config.getint('1', '视频分段大小(兆)') 1957 | except: 1958 | config = configparser.ConfigParser() 1959 | # -read读取ini文件 1960 | config.read('config.ini', encoding='utf-8-sig') 1961 | listx = [] 1962 | listx = config.sections()# 获取到配置文件中所有分组名称 1963 | if '1' not in listx:# 如果分组type不存在则插入type分组 1964 | config.add_section('1') 1965 | else: 1966 | config.remove_option('1', '视频分段大小(兆)')# 删除type分组的stuno 1967 | # config.remove_section('tpye')# 删除配置文件中type分组 1968 | config.set('1', '视频分段大小(兆)', '1000')# 给type分组设置值 1969 | o = open('config.ini', 'w',encoding='utf-8-sig') 1970 | config.write(o) 1971 | o.close()#不要忘记关闭 1972 | Splitsize=1000 #1g 1973 | 1974 | #分段大小不能小于5m 1975 | if Splitsize<5: 1976 | Splitsize=5 #最低不能小于5m 1977 | 1978 | Splitsizes=Splitsize*1024*1024 #分割视频大小,转换为字节 1979 | 1980 | 1981 | 1982 | 1983 | 1984 | 1985 | #这里是控制TS是否追加mp4格式 1986 | try: 1987 | config = configparser.ConfigParser() 1988 | config.read('config.ini', encoding='utf-8-sig') 1989 | tsconvertmp4=config.get('1', 'TS录制完成后自动增加生成MP4格式') 1990 | 1991 | except Exception as _e: 1992 | config = configparser.ConfigParser() 1993 | # -read读取ini文件 1994 | config.read('config.ini', encoding='utf-8-sig') 1995 | listx = [] 1996 | listx = config.sections()# 获取到配置文件中所有分组名称 1997 | if '1' not in listx:# 如果分组type不存在则插入type分组 1998 | config.add_section('1') 1999 | 2000 | else: 2001 | config.remove_option('1', 'TS录制完成后自动增加生成MP4格式')# 删除type分组的stuno 2002 | # config.remove_section('tpye')# 删除配置文件中type分组 2003 | 2004 | 2005 | config.set('1', 'TS录制完成后自动增加生成MP4格式',"否")# 给type分组设置值 2006 | 2007 | o = open('config.ini', 'w',encoding='utf-8-sig') 2008 | 2009 | config.write(o) 2010 | 2011 | o.close()#不要忘记关闭 2012 | tsconvertmp4="否" 2013 | 2014 | 2015 | if tsconvertmp4=="是": 2016 | tsconvertmp4=True 2017 | else: 2018 | tsconvertmp4=False 2019 | 2020 | #这里是控制TS是否追加m4a格式 2021 | try: 2022 | config = configparser.ConfigParser() 2023 | config.read('config.ini', encoding='utf-8-sig') 2024 | tsconvertm4a=config.get('1', 'TS录制完成后自动增加生成m4a格式') 2025 | 2026 | except Exception as _e: 2027 | config = configparser.ConfigParser() 2028 | # -read读取ini文件 2029 | config.read('config.ini', encoding='utf-8-sig') 2030 | listx = [] 2031 | listx = config.sections()# 获取到配置文件中所有分组名称 2032 | if '1' not in listx:# 如果分组type不存在则插入type分组 2033 | config.add_section('1') 2034 | 2035 | else: 2036 | config.remove_option('1', 'TS录制完成后自动增加生成m4a格式')# 删除type分组的stuno 2037 | # config.remove_section('tpye')# 删除配置文件中type分组 2038 | 2039 | 2040 | config.set('1', 'TS录制完成后自动增加生成m4a格式',"否")# 给type分组设置值 2041 | 2042 | o = open('config.ini', 'w',encoding='utf-8-sig') 2043 | 2044 | config.write(o) 2045 | 2046 | o.close()#不要忘记关闭 2047 | tsconvertm4a="否" 2048 | 2049 | if tsconvertm4a=="是": 2050 | tsconvertm4a=True 2051 | else: 2052 | tsconvertm4a=False 2053 | 2054 | #追加格式后,是否删除原文件 2055 | try: 2056 | config = configparser.ConfigParser() 2057 | config.read('config.ini', encoding='utf-8-sig') 2058 | delFilebeforeconversion=config.get('1', '追加格式后删除原文件') 2059 | 2060 | except Exception as _e: 2061 | config = configparser.ConfigParser() 2062 | # -read读取ini文件 2063 | config.read('config.ini', encoding='utf-8-sig') 2064 | listx = [] 2065 | listx = config.sections()# 获取到配置文件中所有分组名称 2066 | if '1' not in listx:# 如果分组type不存在则插入type分组 2067 | config.add_section('1') 2068 | 2069 | else: 2070 | config.remove_option('1', '追加格式后删除原文件')# 删除type分组的stuno 2071 | # config.remove_section('tpye')# 删除配置文件中type分组 2072 | 2073 | 2074 | config.set('1', '追加格式后删除原文件',"否")# 给type分组设置值 2075 | 2076 | o = open('config.ini', 'w',encoding='utf-8-sig') 2077 | 2078 | config.write(o) 2079 | 2080 | o.close()#不要忘记关闭 2081 | delFilebeforeconversion="否" 2082 | 2083 | if delFilebeforeconversion=="是": 2084 | delFilebeforeconversion=True 2085 | else: 2086 | delFilebeforeconversion=False 2087 | 2088 | def tranform_int_to_time(seconds): 2089 | m, s = divmod(seconds, 60) 2090 | h, m = divmod(m, 60) 2091 | return ("%d:%02d:%02d" % (h, m, s)) 2092 | 2093 | 2094 | #这里控制是否生成时间文件 2095 | try: 2096 | config = configparser.ConfigParser() 2097 | config.read('config.ini', encoding='utf-8-sig') 2098 | creatTimeFile=config.get('1', '生成时间文件') 2099 | 2100 | except Exception as _e: 2101 | config = configparser.ConfigParser() 2102 | # -read读取ini文件 2103 | config.read('config.ini', encoding='utf-8-sig') 2104 | listx = [] 2105 | listx = config.sections()# 获取到配置文件中所有分组名称 2106 | if '1' not in listx:# 如果分组type不存在则插入type分组 2107 | config.add_section('1') 2108 | 2109 | else: 2110 | config.remove_option('1', '生成时间文件')# 删除type分组的stuno 2111 | # config.remove_section('tpye')# 删除配置文件中type分组 2112 | 2113 | 2114 | config.set('1', '生成时间文件',"否")# 给type分组设置值 2115 | 2116 | o = open('config.ini', 'w',encoding='utf-8-sig') 2117 | 2118 | config.write(o) 2119 | 2120 | o.close()#不要忘记关闭 2121 | creatTimeFile="否" 2122 | 2123 | 2124 | if creatTimeFile=="是": 2125 | creatTimeFile=True 2126 | else: 2127 | creatTimeFile=False 2128 | 2129 | 2130 | 2131 | # #这里设置浏览器位置 2132 | # try: 2133 | # config = configparser.ConfigParser() 2134 | # config.read('config.ini', encoding='utf-8-sig') 2135 | # chromePath=config.get('1', '浏览器位置') 2136 | 2137 | # except Exception as _e: 2138 | 2139 | # config = configparser.ConfigParser() 2140 | # # -read读取ini文件 2141 | # config.read('config.ini', encoding='utf-8-sig') 2142 | # listx = [] 2143 | # listx = config.sections()# 获取到配置文件中所有分组名称 2144 | # if '1' not in listx:# 如果分组type不存在则插入type分组 2145 | # config.add_section('1') 2146 | 2147 | # else: 2148 | # config.remove_option('1', '浏览器位置')# 删除type分组的stuno 2149 | # # config.remove_section('tpye')# 删除配置文件中type分组 2150 | 2151 | 2152 | # config.set('1', '浏览器位置','data\chrome.exe')# 给type分组设置值 2153 | 2154 | # o = open('config.ini', 'w',encoding='utf-8-sig') 2155 | 2156 | # config.write(o) 2157 | 2158 | # o.close()#不要忘记关闭 2159 | # chromePath='data\chrome.exe' 2160 | 2161 | 2162 | #这里控制是否生显示浏览器 2163 | try: 2164 | config = configparser.ConfigParser() 2165 | config.read('config.ini', encoding='utf-8-sig') 2166 | displayChrome=config.get('1', '是否显示浏览器') 2167 | 2168 | except Exception as _e: 2169 | config = configparser.ConfigParser() 2170 | # -read读取ini文件 2171 | config.read('config.ini', encoding='utf-8-sig') 2172 | listx = [] 2173 | listx = config.sections()# 获取到配置文件中所有分组名称 2174 | if '1' not in listx:# 如果分组type不存在则插入type分组 2175 | config.add_section('1') 2176 | 2177 | else: 2178 | config.remove_option('1', '是否显示浏览器')# 删除type分组的stuno 2179 | # config.remove_section('tpye')# 删除配置文件中type分组 2180 | 2181 | 2182 | config.set('1', '是否显示浏览器',"否")# 给type分组设置值 2183 | 2184 | o = open('config.ini', 'w',encoding='utf-8-sig') 2185 | 2186 | config.write(o) 2187 | 2188 | o.close()#不要忘记关闭 2189 | displayChrome="否" 2190 | 2191 | 2192 | if displayChrome=="是": 2193 | displayChrome=True 2194 | else: 2195 | displayChrome=False 2196 | 2197 | 2198 | 2199 | #这里是控制是否转换短链接 2200 | try: 2201 | config = configparser.ConfigParser() 2202 | config.read('config.ini', encoding='utf-8-sig') 2203 | coverlongurl=config.get('1', '短链接自动转换为长连接') 2204 | 2205 | except Exception as _e: 2206 | config = configparser.ConfigParser() 2207 | # -read读取ini文件 2208 | config.read('config.ini', encoding='utf-8-sig') 2209 | listx = [] 2210 | listx = config.sections()# 获取到配置文件中所有分组名称 2211 | if '1' not in listx:# 如果分组type不存在则插入type分组 2212 | config.add_section('1') 2213 | 2214 | else: 2215 | config.remove_option('1', '短链接自动转换为长连接')# 删除type分组的stuno 2216 | # config.remove_section('tpye')# 删除配置文件中type分组 2217 | 2218 | 2219 | config.set('1', '短链接自动转换为长连接',"否")# 给type分组设置值 2220 | 2221 | o = open('config.ini', 'w',encoding='utf-8-sig') 2222 | 2223 | config.write(o) 2224 | 2225 | o.close()#不要忘记关闭 2226 | coverlongurl="否" 2227 | 2228 | 2229 | if coverlongurl=="是": 2230 | coverlongurl=True 2231 | else: 2232 | coverlongurl=False 2233 | 2234 | 2235 | 2236 | #这里是控制采用浏览器录制 2237 | try: 2238 | config = configparser.ConfigParser() 2239 | config.read('config.ini', encoding='utf-8-sig') 2240 | onlybrowser=config.get('1', '仅用浏览器录制') 2241 | 2242 | except Exception as _e: 2243 | config = configparser.ConfigParser() 2244 | # -read读取ini文件 2245 | config.read('config.ini', encoding='utf-8-sig') 2246 | listx = [] 2247 | listx = config.sections()# 获取到配置文件中所有分组名称 2248 | if '1' not in listx:# 如果分组type不存在则插入type分组 2249 | config.add_section('1') 2250 | 2251 | else: 2252 | config.remove_option('1', '仅用浏览器录制')# 删除type分组的stuno 2253 | # config.remove_section('tpye')# 删除配置文件中type分组 2254 | 2255 | 2256 | config.set('1', '仅用浏览器录制',"否")# 给type分组设置值 2257 | 2258 | o = open('config.ini', 'w',encoding='utf-8-sig') 2259 | 2260 | config.write(o) 2261 | 2262 | o.close()#不要忘记关闭 2263 | onlybrowser="否" 2264 | 2265 | 2266 | if onlybrowser=="是": 2267 | onlybrowser=True 2268 | else: 2269 | onlybrowser=False 2270 | 2271 | 2272 | # #这里设置浏览器驱动位置 2273 | # try: 2274 | # config = configparser.ConfigParser() 2275 | # config.read('config.ini', encoding='utf-8-sig') 2276 | # chrome_DrivePath=config.get('1', '浏览器驱动位置') 2277 | 2278 | # except Exception as _e: 2279 | 2280 | # config = configparser.ConfigParser() 2281 | # # -read读取ini文件 2282 | # config.read('config.ini', encoding='utf-8-sig') 2283 | # listx = [] 2284 | # listx = config.sections()# 获取到配置文件中所有分组名称 2285 | # if '1' not in listx:# 如果分组type不存在则插入type分组 2286 | # config.add_section('1') 2287 | 2288 | # else: 2289 | # config.remove_option('1', '浏览器驱动位置')# 删除type分组的stuno 2290 | # # config.remove_section('tpye')# 删除配置文件中type分组 2291 | 2292 | 2293 | # config.set('1', '浏览器驱动位置','"chromedriver.exe"')# 给type分组设置值 2294 | 2295 | # o = open('config.ini', 'w',encoding='utf-8-sig') 2296 | 2297 | # config.write(o) 2298 | 2299 | # o.close()#不要忘记关闭 2300 | # chrome_DrivePath='"chromedriver.exe"' 2301 | 2302 | 2303 | 2304 | 2305 | 2306 | # print("最开始排队 "+ str(localdelaydefault)+" 秒后读取下一个地址") 2307 | #cookiesSet 2308 | cookiesSet='' 2309 | try: 2310 | config = RawConfigParser() 2311 | config.read('config.ini', encoding='utf-8-sig') 2312 | cookiesSet=config.get('1', 'cookies') 2313 | except: 2314 | config = configparser.ConfigParser() 2315 | # -read读取ini文件 2316 | config.read('config.ini', encoding='utf-8-sig') 2317 | listx = [] 2318 | listx = config.sections()# 获取到配置文件中所有分组名称 2319 | if '1' not in listx:# 如果分组type不存在则插入type分组 2320 | config.add_section('1') 2321 | 2322 | else: 2323 | config.remove_option('1', 'cookies')# 删除type分组的stuno 2324 | # config.remove_section('tpye')# 删除配置文件中type分组 2325 | 2326 | config.set('1', 'cookies', '')# 给type分组设置值 2327 | 2328 | o = open('config.ini', 'w',encoding='utf-8-sig') 2329 | 2330 | config.write(o) 2331 | 2332 | o.close()#不要忘记关闭 2333 | 2334 | # if cookiesSet=="": 2335 | # print("一般需要设置一个cookies才可以运行") 2336 | 2337 | # 谷歌浏览器驱动地址 2338 | # chrome_drive = 'data/chromedriver.exe' 2339 | # # 谷歌浏览器位置 2340 | # chromePath= r'D:\Software\JobSoftware\Chromium\Application\Chromium.exe' 2341 | #设置浏览器位置 2342 | 2343 | # if os.path.exists(chrome_DrivePath)==False: 2344 | # print("错误-------------浏览器驱动不存在,请至少同目录有没有chromedriver文件") 2345 | # # time.sleep(3) 2346 | 2347 | # print(chrome_DrivePath) 2348 | # ChromeDrivePath = Service(chrome_DrivePath) 2349 | 2350 | # chrome_options = Options() 2351 | 2352 | # # 配置不显示浏览器 2353 | # chrome_options.add_argument('--headless') 2354 | # chrome_options.add_argument('--disable-gpu') 2355 | # chrome_options.add_argument('User-Agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36') 2356 | # chrome_options.add_experimental_option('excludeSwitches', ['enable-logging']) 2357 | # #忽略错误 2358 | # chrome_options.add_argument('--ignore-certificate-errors') 2359 | # chrome_options.add_argument('--ignore-ssl-errors') 2360 | 2361 | 2362 | #读取url文件 2363 | try: 2364 | file=open("URL_config.ini","r",encoding="utf-8-sig") 2365 | for line in file: 2366 | line=line.strip() 2367 | if line.startswith("#"): 2368 | continue 2369 | c=line.split() 2370 | if len(c)==0: 2371 | continue 2372 | else: 2373 | c=line.strip() 2374 | 2375 | c=c.split('#') 2376 | if len(line)<20: 2377 | continue 2378 | 2379 | 2380 | SplitTest = line.split(',') 2381 | #判断有没有逗号,有逗号获取逗号前面的地址 2382 | if len(SplitTest)>1: 2383 | resline= SplitTest[0] 2384 | else: 2385 | resline=line 2386 | 2387 | #短连接转换长连接 2388 | if coverlongurl: 2389 | #查询是否为主页:有下面这个网址的,返回位置 2390 | if resline.find("https://www.douyin.com/user/")>-1: 2391 | print("转换主页路径") 2392 | res=searchUserWeb(resline) 2393 | 2394 | #判断有没有正常返回路径. 如果上面函数出错,会返回输入的值. 那么判断出错,就跳过 2395 | if res!=resline and res!="": 2396 | lastdyurl=res 2397 | namelist.append(str(resline)+"|"+str(res)) 2398 | while len(namelist): 2399 | a=namelist.pop() 2400 | replacewords = a.split('|') 2401 | if replacewords[0]!=replacewords[1]: 2402 | updateFile(r"URL_config.ini", replacewords[0], replacewords[1]) 2403 | print("转换了路径为:"+str(replacewords[1])) 2404 | resline=replacewords[1] 2405 | 2406 | 2407 | # elif resline.find("https://v.douyin.com/")>-1: 2408 | # #查询短链接 2409 | # dyUrlGroup=checkUrlTxt(resline) 2410 | # if len(dyUrlGroup)==2: 2411 | # if dyUrlGroup[1]=="1": 2412 | # namelist.append(str(resline)+"|"+str(dyUrlGroup[0])) 2413 | # while len(namelist): 2414 | # a=namelist.pop() 2415 | # replacewords = a.split('|') 2416 | # if replacewords[0]!=replacewords[1]: 2417 | # updateFile(r"URL_config.ini", replacewords[0], replacewords[1]) 2418 | # print("转换了路径为:"+str(replacewords[1])) 2419 | 2420 | 2421 | 2422 | 2423 | if resline.find("https://live.douyin.com/")>-1 or resline.find("https://v.douyin.com/")>-1: 2424 | 2425 | #因为后面需要网址假主播名字,这里再补上 2426 | if len(SplitTest)>1: 2427 | lastdyurl= (resline,SplitTest[1]) 2428 | else: 2429 | lastdyurl=(resline,"") 2430 | 2431 | texturl.append(lastdyurl) 2432 | 2433 | 2434 | else: 2435 | print(str(resline)+" 未知链接.此条跳过") 2436 | 2437 | 2438 | 2439 | 2440 | 2441 | #print(c[0]) 2442 | file.close() 2443 | 2444 | while len(namelist): 2445 | a=namelist.pop() 2446 | replacewords = a.split('|') 2447 | if replacewords[0]!=replacewords[1]: 2448 | updateFile(r"URL_config.ini", replacewords[0], replacewords[1]) 2449 | #格式化后查找不一样 2450 | 2451 | if len(texturl)>0: 2452 | textNoRepeatUrl = list(set(texturl)) 2453 | 2454 | 2455 | if len(textNoRepeatUrl)>0: 2456 | for i in textNoRepeatUrl: 2457 | #formatcontent[0] #这个为分离出的地址 2458 | if i[0] not in runingList: 2459 | if firstStart==False: #第一次 不会出现新增链接的字眼 2460 | print("新增链接: "+ i[0]) 2461 | Monitoring=Monitoring+1 2462 | args = [i, Monitoring] 2463 | createVar['thread'+ str(Monitoring)] = threading.Thread(target=startgo,args=args) 2464 | createVar['thread'+ str(Monitoring)].daemon=True 2465 | createVar['thread'+ str(Monitoring)].start() 2466 | runingList.append(i[0]) 2467 | # print("\r"+str(localdelaydefault)+" 秒后读取下一个地址(如果存在) ") 2468 | time.sleep(localdelaydefault) 2469 | texturl=[] 2470 | firstStart=False #第一次 不会出现新增链接的字眼 2471 | 2472 | except Exception as e: 2473 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 2474 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 2475 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 2476 | 2477 | 2478 | #这个是第一次运行其他线程.因为有变量前后顺序的问题,这里等全局变量完成后再运行def函数 2479 | if firstRunOtherLine: 2480 | t = threading.Thread(target=displayinfo, args=(), daemon=True) 2481 | t.start() 2482 | t2 = threading.Thread(target=changemaxconnect, args=(), daemon=True) 2483 | t2.start() 2484 | 2485 | firstRunOtherLine=False 2486 | 2487 | #总体循环3s 2488 | time.sleep(3) 2489 | #print('程式结束,请按任意键退出') 2490 | input() 2491 | 2492 | 2493 | -------------------------------------------------------------------------------- /抖音直播录制_230530.6.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | import random 3 | import requests 4 | import re 5 | import os 6 | # import urllib.request 7 | import urllib.parse 8 | import sys 9 | # from bs4 import BeautifulSoup 10 | import time 11 | import json 12 | import configparser 13 | import subprocess 14 | import threading 15 | import string 16 | import logging 17 | import datetime 18 | from configparser import RawConfigParser 19 | from selenium.common.exceptions import TimeoutException 20 | from selenium.webdriver.chrome.options import Options 21 | # from selenium.webdriver.chrome.service import Service 22 | from selenium import webdriver 23 | import shutil 24 | import hashlib 25 | 26 | 27 | version=230530.6 #请注意把下面这个地方和文件名改了就行啦 28 | #pyinstaller -F 抖音直播录制_230530.6.py 29 | #pyinstaller 抖音直播录制_230530.6.py 30 | 31 | #log日志--------------------------------------------------------------- 32 | # sys.stdout = Logger(sys.stdout) # 将输出记录到log 33 | # sys.stderr = Logger(sys.stderr) # 将错误信息记录到log 34 | # 创建一个logger 35 | logger = logging.getLogger('抖音直播录制%s版'%str(version)) 36 | logger.setLevel(logging.INFO) 37 | # 创建一个handler,用于写入日志文件 38 | if not os.path.exists("log"): 39 | os.makedirs("log") 40 | fh = logging.FileHandler("log/错误日志文件.log",encoding="utf-8-sig",mode="a") 41 | fh.setLevel(logging.WARNING) 42 | # 再创建一个handler,用于输出到控制台 43 | # ch = logging.StreamHandler() 44 | # ch.setLevel(logging.INFO) 45 | # formatter = logging.Formatter() 46 | # ch.setFormatter(formatter) 47 | # 定义handler的输出格式 48 | formatter = logging.Formatter('%(asctime)s - %(message)s') 49 | fh.setFormatter(formatter) 50 | #ch.setFormatter(formatter) 51 | # 给logger添加handler 52 | logger.addHandler(fh) 53 | # logger.addHandler(ch) 54 | # socket.setdefaulttimeout(10) 55 | 56 | #全局变量-------------------------------------------------------------------------------- 57 | recording=set() 58 | unrecording=set() 59 | warning_count=0 60 | maxrequest=0 61 | runingList=[] 62 | texturl=[] 63 | textNoRepeatUrl=[] 64 | createVar = locals() 65 | firstStart=True #第一次 不会出现新增链接的字眼 66 | namelist=[] 67 | firstRunOtherLine=True #第一次运行显示线程等 68 | 69 | 70 | # videosavetype="TS" 71 | # delaydefault=60 72 | # localdelaydefault=0 73 | # videopath="" 74 | # videoQuality="原画" 75 | # videom3u8=False 76 | # looptime=False 77 | # Splitvideobysize=False 78 | # Splitsizes=0 79 | # tsconvertmp4=False 80 | # tsconvertm4a=False 81 | # delFilebeforeconversion=False 82 | # creatTimeFile=False 83 | # displayChrome=False 84 | # coverlongurl=False 85 | # onlybrowser=False 86 | # cookiesSet="" 87 | 88 | 89 | 90 | 91 | 92 | 93 | def updateFile(file,old_str,new_str): 94 | #不允许字符串替换为空 95 | if new_str==None or new_str=="": 96 | return "" 97 | file_data = "" 98 | with open(file, "r", encoding="utf-8-sig") as f: 99 | for line in f: 100 | if old_str in line: 101 | line = line.replace(old_str,new_str) 102 | file_data += line 103 | with open(file,"w",encoding="utf-8-sig") as f: 104 | f.write(file_data) 105 | 106 | 107 | def get_xx(): 108 | string.ascii_letters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 109 | return random.choice(string.ascii_lowercase) 110 | 111 | def get_dx(): 112 | string.ascii_letters= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 113 | return random.choice(string.ascii_uppercase) 114 | 115 | def subwords(words): 116 | words=re.sub('[-? * : " < > / | .]', '', words) 117 | words=re.sub(r'/', '', words) 118 | return words 119 | 120 | 121 | 122 | def convertsmp4(address): 123 | address2=address 124 | address2=address2.replace('.ts' , '') 125 | address2=address2.replace('.flv' , '') 126 | if tsconvertmp4: 127 | _output = subprocess.check_output([ 128 | "ffmpeg", "-i",address, 129 | "-c:v","copy", 130 | "-c:a","copy", 131 | "-f","mp4",address2+".mp4", 132 | ], stderr = subprocess.STDOUT) 133 | #print("转换到mp4") 134 | if delFilebeforeconversion: 135 | time.sleep(1) 136 | if os.path.exists(address): 137 | os.remove(address) 138 | 139 | 140 | def convertsm4a(address): 141 | if tsconvertm4a: 142 | address2=address 143 | address2=address2.replace('.ts' , '') 144 | address2=address2.replace('.flv' , '') 145 | _output = subprocess.check_output([ 146 | "ffmpeg", "-i",address, 147 | "-n","-vn", 148 | "-c:a","aac","-bsf:a","aac_adtstoasc","-ab","320k", 149 | address2+".m4a", 150 | ], stderr = subprocess.STDOUT) 151 | if delFilebeforeconversion: 152 | time.sleep(1) 153 | if os.path.exists(address): 154 | os.remove(address) 155 | #print("转换到m4a") 156 | 157 | def creatass(filegruop): 158 | startname=filegruop[0] 159 | assname=filegruop[1] 160 | index_time = -1 161 | finish=0 162 | today = datetime.datetime.now() 163 | re_datatime =today.strftime('%Y-%m-%d %H:%M:%S') 164 | 165 | # createVar[str(filegruop+"_th")] =threading.Thread() 166 | 167 | 168 | while(True): 169 | index_time+=1 170 | txt=str(index_time)+ "\n" + tranform_int_to_time(index_time)+',000 --> '+ tranform_int_to_time(index_time + 1)+',000' + "\n" + str(re_datatime) + "\n" 171 | 172 | with open(assname+".ass",'a',encoding='utf8') as f: 173 | f.write(txt) 174 | # print(txt) 175 | 176 | if startname not in recording: 177 | finish+=1 178 | offset = datetime.timedelta(seconds=1) 179 | # 获取修改后的时间并格式化 180 | re_datatime = (today + offset).strftime('%Y-%m-%d %H:%M:%S') 181 | today=today + offset 182 | # print(re_date) 183 | 184 | else: 185 | time.sleep(1) 186 | today = datetime.datetime.now() 187 | re_datatime =today.strftime('%Y-%m-%d %H:%M:%S') 188 | 189 | if finish>15: 190 | break 191 | 192 | def videodownload(url,filename,size_all): 193 | headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362'} 194 | size = 0 195 | chunk_size = 1024 196 | requests.urllib3.disable_warnings() 197 | response = requests.get(url, headers=headers, stream=True, verify=False,proxies=None) 198 | with open(filename, 'wb') as file: 199 | for data in response.iter_content(chunk_size = chunk_size): 200 | file.write(data) 201 | size += len(data) 202 | file.flush() 203 | if size_all > 0: 204 | sys.stdout.write(' [下载进度]:%.2fMB/%.2fMB' % (float(size/10/ (size_all*1024*1024) * 100), size_all) + '\r') 205 | if size > size_all*1024*1024: 206 | break 207 | else: 208 | sys.stdout.write(' [下载进度]:%.2fMB' % float(size/1024/1024) + '\r') 209 | print('下载完成') 210 | 211 | 212 | 213 | def auto_getcookies(): 214 | global cookiesSet 215 | try: 216 | time.sleep(1800) #半个小时 217 | print("获取直播间cookies一次") 218 | cookiesSet = get_cookies() 219 | 220 | except: 221 | pass 222 | 223 | 224 | 225 | def get_cookies(): 226 | try: 227 | url = 'https://live.douyin.com/' # 替换为你想要获取cookie的网址 228 | 229 | headers = { 230 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36', 231 | } 232 | 233 | # 发送GET请求,并设置headers参数 234 | response = requests.get(url, headers=headers) 235 | 236 | # 获取响应对象中的cookie 237 | cookies = response.cookies 238 | 239 | # 打印所有cookie的名称和值 240 | for cookie in cookies: 241 | # print(cookie.name, cookie.value) 242 | pass 243 | 244 | new_string = cookie.name + "=" + cookie.value 245 | 246 | if len(str(new_string)) > 5: 247 | return new_string 248 | else: 249 | return "" 250 | except: 251 | return "" 252 | 253 | 254 | 255 | 256 | headers = { 257 | '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' 258 | } 259 | 260 | print( '-------------- 吾爱破解论坛 程序当前配置----------------' ) 261 | print("循环值守录制抖音直播 版本:%s"%str(version)) 262 | 263 | 264 | 265 | 266 | 267 | 268 | def newgeturl(url): 269 | # print("请求地址"+url) 270 | global warning_count 271 | try: 272 | for _i in range(10): 273 | headers2 = {"referer": "https://www.douyin.com/", 274 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', 275 | "cookie":cookiesSet} 276 | 277 | try: 278 | res=requests.get(url,headers=headers2,proxies=None) 279 | except Exception as e: 280 | print("请求网页失败,请检查是否用了代理,错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 281 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 282 | warning_count+=1 283 | return "" 284 | # print(res.text) 285 | if len(res.text)<5 or res.text.find("繁忙")>=0: 286 | # print(1) 287 | # print(res.text) 288 | time.sleep(1) 289 | continue 290 | else: 291 | # print(res.text) 292 | # print("端口网址为:"+url) 293 | # #写入txt源码.不用请去掉 294 | # with open("yuanma.txt", "w",encoding="utf8") as file: 295 | # file.write(res.text) 296 | return res.text 297 | 298 | # len(res.text)>5 299 | print("未获取到正确的网页信息.此时获取的网页信息如下:"+ str(res.text)) 300 | warning_count+=1 301 | 302 | except Exception as e: 303 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 304 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 305 | 306 | warning_count+=1 307 | return "" 308 | 309 | def newgeturl_usercount(url): 310 | douyindata="" 311 | portGroup="" 312 | restext="" 313 | 314 | try: 315 | for _i in range(10): 316 | headers2 = {"referer": "https://www.douyin.com/", 317 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', 318 | "cookie":cookiesSet} 319 | 320 | try: 321 | res=requests.get(url,headers=headers2,proxies=None) 322 | except Exception as e: 323 | print("请求网页失败,请检查是否用了代理,错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 324 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 325 | warning_count+=1 326 | return "" 327 | # print(res.text) 328 | if len(res.text)<5 or res.text.find("繁忙")>=0: 329 | # print(1) 330 | # print(res.text) 331 | time.sleep(1) 332 | continue 333 | else: 334 | # #写入txt源码.不用请去掉 335 | # with open("yuanma.txt", "w",encoding="utf8") as file: 336 | # file.write(res.text) 337 | douyindata=res.text 338 | break 339 | 340 | 341 | 342 | 343 | 344 | if douyindata.find("系统繁忙,请稍后再试")>=0: 345 | print("未找到源码网址,等待设定好的秒数后会自动重试, 如果一直出现这个问题,请联系作者") 346 | return portGroup 347 | 348 | 349 | #因为获取的网页包含html的信息, 下面对这些框架的代码进行清理. 清理后的代码为{..}可以转json 350 | #获取不到网页.返回空 351 | if douyindata=="": 352 | return(portGroup) 353 | 354 | position1=douyindata.find("{") 355 | position2=douyindata.rfind("}") 356 | restext=douyindata[position1:position2+1] 357 | 358 | #转换网页源码为json 359 | try: 360 | resjs=json.loads(restext) 361 | except Exception as e: 362 | if len(str(restext))>0: 363 | print("json转换失败, 转换错误信息为: "+str(restext) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 364 | logger.warning("json转换失败,转换错误信息为: "+str(restext) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 365 | else: 366 | # print("json获取为空 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 367 | # logger.warning("json获取为空 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 368 | pass 369 | return portGroup 370 | 371 | #获取直播间状态.2是直播.4是直播结束 372 | status=resjs["data"]["data"][0]["status"]["user_count_str"] #直播间人数 373 | 374 | return(status) 375 | except Exception as e: 376 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 377 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 378 | return portGroup 379 | 380 | 381 | 382 | def chromeAuto(url): 383 | global warning_count 384 | try: 385 | if displayChrome: 386 | #是否有浏览器设置 387 | chrome = webdriver.Chrome() 388 | else: 389 | chrome = webdriver.Chrome(options=chrome_options) 390 | 391 | for _b in range(5): 392 | 393 | chrome.set_page_load_timeout(1) 394 | try: 395 | chrome.get("https://www.douyin.com/discover") # 往浏览器的网页地址栏填入url参数 396 | except: 397 | pass 398 | 399 | # print("超时了") 400 | # time.sleep(2) 401 | # chrome.close() 402 | # chrome.quit() 403 | 404 | chrome.set_page_load_timeout(3) 405 | for _t in range(10): 406 | #是否显示浏览器 407 | # chrome = Chrome() 408 | #设置超时5秒 409 | 410 | # url="https://live.douyin.com/webcast/web/enter/?aid=6383&web_rid=677464104206" 411 | try: 412 | try: 413 | chrome.get(url) # 往浏览器的网页地址栏填入url参数 414 | except: 415 | pass 416 | 417 | # try: 418 | # chrome.get(url) # 往浏览器的网页地址栏填入url参数 419 | # time.sleep(10) 420 | # except: 421 | # print("超时了") 422 | # time.sleep(2) 423 | # # chrome.close() 424 | # # chrome.quit() 425 | # continue 426 | 427 | data = chrome.page_source 428 | 429 | if data.find("status")<0 or data.find("繁忙")>-1: 430 | # print("未找到源码网址,2s后重试..") 431 | time.sleep(0.5) 432 | chrome.refresh() 433 | continue 434 | else: 435 | chrome.quit() 436 | return data 437 | 438 | except: 439 | time.sleep(2) 440 | continue 441 | # print(1) 442 | # chrome.close() 443 | 444 | # print(2) 445 | time.sleep(2) 446 | except Exception as e: 447 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 448 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 449 | 450 | warning_count+=1 451 | 452 | chrome.quit() 453 | return "" 454 | 455 | 456 | 457 | def getStream_url(douyindata): 458 | # global warning_count 459 | portGroup=[] 460 | restext="" 461 | if douyindata.find("系统繁忙,请稍后再试")>=0: 462 | print("未找到源码网址,等待设定好的秒数后会自动重试, 如果一直出现这个问题,请联系作者") 463 | return portGroup 464 | 465 | try: 466 | #因为获取的网页包含html的信息, 下面对这些框架的代码进行清理. 清理后的代码为{..}可以转json 467 | #获取不到网页.返回空 468 | if douyindata=="": 469 | 470 | return(portGroup) 471 | 472 | position1=douyindata.find("{") 473 | position2=douyindata.rfind("}") 474 | restext=douyindata[position1:position2+1] 475 | 476 | #转换网页源码为json 477 | try: 478 | resjs=json.loads(restext) 479 | except Exception as e: 480 | if len(str(restext))>0: 481 | print("json转换失败, 转换错误信息为: "+str(restext) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 482 | logger.warning("json转换失败,转换错误信息为: "+str(restext) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 483 | else: 484 | # print("json获取为空 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 485 | # logger.warning("json获取为空 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 486 | pass 487 | return portGroup 488 | #创建端口参数组 489 | #转换过json的代码,获取端口具体内容 490 | #获取主播名字 491 | startname=resjs["data"]["user"]['nickname'] 492 | 493 | #格式化字符串.防止文件名出现特殊字符 494 | startname = subwords(startname) 495 | portGroup.append(startname) 496 | 497 | #获取直播间状态.2是直播.4是直播结束 498 | status=resjs["data"]["data"][0]["status"] #直播状态 #m3u8流 499 | # print("直播状态:"+str(+status)) 500 | 501 | # #去除前后空格 502 | # status=status.strip() 503 | 504 | if status==2: 505 | #参数组添加信息 506 | portGroup.append(status) 507 | #获取直播流地址 508 | 509 | 510 | if videoQuality=="超清": 511 | res=resjs["data"]["data"][0]["stream_url"]["hls_pull_url_map"]["HD1"] #m3u8流 512 | res2=resjs["data"]["data"][0]["stream_url"]["flv_pull_url"]["HD1"] #flv流 513 | if videoQuality=="高清": 514 | res=resjs["data"]["data"][0]["stream_url"]["hls_pull_url_map"]["SD1"] #m3u8流 515 | res2=resjs["data"]["data"][0]["stream_url"]["flv_pull_url"]["SD1"] #flv流 516 | if videoQuality=="标清": 517 | res=resjs["data"]["data"][0]["stream_url"]["hls_pull_url_map"]["SD2"] #m3u8流 518 | res2=resjs["data"]["data"][0]["stream_url"]["flv_pull_url"]["SD2"] #flv流 519 | 520 | #蓝光,原画 521 | else: 522 | res=resjs["data"]["data"][0]["stream_url"]["hls_pull_url_map"]["FULL_HD1"] #m3u8流 523 | res2=resjs["data"]["data"][0]["stream_url"]["flv_pull_url"]["FULL_HD1"] #flv流 524 | 525 | 526 | 527 | 528 | 529 | # print("下载地址为:"+res) 530 | #参数组添加信息 531 | portGroup.append(res) 532 | portGroup.append(res2) 533 | 534 | else: 535 | #参数组添加信息 536 | portGroup.append(status) 537 | portGroup.append("") 538 | portGroup.append("") 539 | # print("没有直播") 540 | 541 | #返回各种端口信息,主播名,状态码,地址 542 | return(portGroup) 543 | except Exception as e: 544 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 545 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 546 | return portGroup 547 | return portGroup 548 | 549 | 550 | #拼接端口的网址,这里只接收输入pc端的网址,输出端口网址 551 | def SplicingUrl(inputUrl): 552 | try: 553 | resStr = (inputUrl.split("?")[0]).split("/")[-1] 554 | #当获取的rid拼接到网址里,获得端口网址 555 | # resStr="https://live.douyin.com/webcast/web/enter/?aid=6383&web_rid="+resStr 以前的版本 556 | resStr="https://live.douyin.com/webcast/room/web/enter/?aid=6383&device_platform=web&cookie_enabled=true&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=100.0.4896.127&web_rid="+resStr 557 | # print(resStr) 558 | #返回拼接网址 559 | return resStr 560 | except Exception as e: 561 | print("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 562 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 563 | 564 | 565 | def changemaxconnect(): 566 | 567 | global warning_count 568 | 569 | 570 | while True: 571 | time.sleep(120) 572 | warning_count=0 573 | 574 | 575 | 576 | def check_md5(file_path): 577 | """ 578 | 计算文件的md5值 579 | """ 580 | with open(file_path, 'rb') as fp: 581 | file_md5 = hashlib.md5(fp.read()).hexdigest() 582 | return file_md5 583 | 584 | def backup_file(file_path, backup_dir): 585 | """ 586 | 备份配置文件到备份目录 587 | """ 588 | try: 589 | if not os.path.exists(backup_dir): 590 | os.makedirs(backup_dir) 591 | # 拼接备份文件名,年-月-日-时-分-秒 592 | timestamp = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S') 593 | backup_file_name = os.path.basename(file_path) + '_' + timestamp 594 | # 拷贝文件到备份目录 595 | backup_file_path = os.path.join(backup_dir, backup_file_name) 596 | shutil.copy2(file_path, backup_file_path) 597 | print(f'已备份配置文件{file_path}到{backup_file_path}') 598 | except Exception as e: 599 | print(f'备份配置文件{file_path}失败:{str(e)}') 600 | 601 | def backup_file_start(): 602 | config_file_path = './config.ini' 603 | url_config_file_path = './URL_config.ini' 604 | backup_dir = './配置自动备份文件夹' 605 | 606 | config_md5 = '' 607 | url_config_md5 = '' 608 | 609 | while True: 610 | try: 611 | if os.path.exists(config_file_path): 612 | new_config_md5 = check_md5(config_file_path) 613 | if new_config_md5 != config_md5: 614 | backup_file(config_file_path, backup_dir) 615 | config_md5 = new_config_md5 616 | 617 | if os.path.exists(url_config_file_path): 618 | new_url_config_md5 = check_md5(url_config_file_path) 619 | if new_url_config_md5 != url_config_md5: 620 | backup_file(url_config_file_path, backup_dir) 621 | url_config_md5 = new_url_config_md5 622 | time.sleep(60) # 每1分钟检测一次文件是否有修改 623 | except Exception as e: 624 | print(f'执行脚本异常:{str(e)}') 625 | 626 | 627 | 628 | allLive=[] #全部的直播 629 | allRecordingUrl=[] 630 | 631 | class C_real_url(): 632 | def douyin(url): 633 | zhibo_ids="" 634 | #代码来自于https://github.com/wbt5/real-url/blob/master/douyin.py. 侵删 635 | x=0 636 | for y in range(2): 637 | x+=1 638 | if x>1: 639 | url=zhibo_ids 640 | # DEBUG = False 641 | DEBUG = False 642 | headers = { 643 | 'authority': 'v.douyin.com', 644 | 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1', 645 | } 646 | 647 | # url = input('请输入抖音直播链接或19位room_id:') 648 | # print("传入url",url) 649 | start_url=url #初始固定一个,防止后面的变量变了 650 | if re.match(r'\d{19}', url): 651 | room_id = url 652 | else: 653 | try: 654 | url = re.search(r'(https.*)', url).group(1) 655 | response = requests.head(url, headers=headers) 656 | url = response.headers['location'] 657 | room_id = re.search(r'\d{19}', url).group(0) 658 | except Exception as e: 659 | if DEBUG: 660 | print(e) 661 | print('地址请求失败,请检查这个网址是否正常: '+start_url) 662 | sys.exit(1) 663 | # print('room_id', room_id) 664 | 665 | try: 666 | headers.update({ 667 | 'authority': 'webcast.amemv.com', 668 | 'cookie': '_tea_utm_cache_1128={%22utm_source%22:%22copy%22%2C%22utm_medium%22:%22android%22%2C%22utm_campaign%22:%22client_share%22}', 669 | }) 670 | 671 | params = ( 672 | ('type_id', '0'), 673 | ('live_id', '1'), 674 | ('room_id', room_id), 675 | ('app_id', '1128'), 676 | ) 677 | 678 | response = requests.get('https://webcast.amemv.com/webcast/room/reflow/info/', headers=headers, params=params).json() 679 | 680 | 681 | try: 682 | rtmp_pull_url = response['data']['room']['stream_url']['flv_pull_url']['FULL_HD1'] 683 | except Exception as e: 684 | rtmp_pull_url = response['data']['room']['stream_url']['rtmp_pull_url'] 685 | 686 | 687 | try: 688 | hls_pull_url = response['data']['room']['stream_url']['hls_pull_url_map']['FULL_HD1'] 689 | except Exception as e: 690 | hls_pull_url = response['data']['room']['stream_url']['hls_pull_url'] 691 | 692 | 693 | 694 | hls_pull_url = response['data']['room']['stream_url']['hls_pull_url'] 695 | zhubo_name = response['data']['room']['owner']['nickname'] 696 | zhibo_id = response['data']['room']["id_str"] #只要判定这个有没有 697 | 698 | 699 | 700 | 701 | try: 702 | zhibo_ids = response['data']['room']['owner']['own_room']["room_ids_str"][0] #如果这个出错,就判断没有播. 只有播放的时候才会出现这个 703 | if zhibo_id==zhibo_ids: 704 | # print(zhibo_id) 705 | 706 | response = requests.head(rtmp_pull_url) 707 | if response.status_code == 200: 708 | # print(rtmp_pull_url+'直播流链接存在') 709 | valueA=True 710 | else: 711 | # print(rtmp_pull_url+'直播流链接不存在') 712 | valueA=False 713 | 714 | response = requests.head(hls_pull_url) 715 | if response.status_code == 200: 716 | valueB=True 717 | # print(hls_pull_url+'直播流链接存在') 718 | else: 719 | # print(hls_pull_url+'直播流链接不存在') 720 | valueB=False 721 | 722 | if valueA and valueB: 723 | urlA=hls_pull_url 724 | urlB=rtmp_pull_url 725 | elif valueA and valueB==False: 726 | urlA=hls_pull_url 727 | urlB=hls_pull_url 728 | elif valueA==False and valueB: 729 | urlA=rtmp_pull_url 730 | urlB=rtmp_pull_url 731 | else: #都不存在 732 | urlA=hls_pull_url 733 | urlB=rtmp_pull_url 734 | 735 | return([zhubo_name,2,urlA,urlB]) #因为上面出错到不了这里. 所以这里直接设置状态为2 736 | else: 737 | continue 738 | 739 | except: 740 | response = requests.head(rtmp_pull_url) 741 | if response.status_code == 200: 742 | # print(rtmp_pull_url+'直播流链接存在') 743 | valueA=True 744 | else: 745 | # print(rtmp_pull_url+'直播流链接不存在') 746 | valueA=False 747 | 748 | response = requests.head(hls_pull_url) 749 | if response.status_code == 200: 750 | valueB=True 751 | # print(hls_pull_url+'直播流链接存在') 752 | else: 753 | # print(hls_pull_url+'直播流链接不存在') 754 | valueB=False 755 | 756 | if valueA and valueB: 757 | urlA=hls_pull_url 758 | urlB=rtmp_pull_url 759 | elif valueA and valueB==False: 760 | urlA=hls_pull_url 761 | urlB=hls_pull_url 762 | elif valueA==False and valueB: 763 | urlA=rtmp_pull_url 764 | urlB=rtmp_pull_url 765 | else: #都不存在 766 | urlA=hls_pull_url 767 | urlB=rtmp_pull_url 768 | 769 | return([zhubo_name,4,urlA,urlB]) 770 | pass 771 | # print(zhibo_ids) #如果这个出错,就判断没有播. 只有播放的时候才会出现这个 772 | # response = requests.head(rtmp_pull_url, timeout=10) 773 | # if response.status_code == 200: 774 | # print('直播流链接存在') 775 | # else: 776 | # print('直播流链接不存在') 777 | 778 | 779 | # print(rtmp_pull_url) 780 | # print(hls_pull_url) 781 | return [""] 782 | except Exception as e: 783 | if DEBUG: 784 | print(e) 785 | return [""] 786 | # print('获取real url失败') 787 | return [""] 788 | 789 | print( '......................................................' ) 790 | 791 | def startgo(line,countvariable=-1): 792 | global warning_count 793 | # countvariable=line[1] 794 | # line=line[0] 795 | while True: 796 | try: 797 | # global allLive 798 | recordfinish=False 799 | recordfinish_2=False 800 | counttime=time.time() 801 | global videopath 802 | 803 | if len(line)<2 or type(line)==str: 804 | ridcontent=[line,""] 805 | else: 806 | ridcontent = line 807 | rid=ridcontent[0] 808 | if ridcontent[1]!="": 809 | print("运行新线程,传入地址"+ridcontent[0],ridcontent[1]+" 第"+str(countvariable)+"行") 810 | else: 811 | print("运行新线程,传入地址"+ridcontent[0]+" 第"+str(countvariable)+"行") 812 | 813 | #设置一个数组来获取端口参数 814 | portInfo=[] 815 | startname="" 816 | # changestaute="" 817 | Runonce=False 818 | while True: 819 | try: 820 | if ridcontent[0].find("https://live.douyin.com/")>-1: 821 | #获取源码 822 | codePath=SplicingUrl(ridcontent[0]) 823 | print("获取地址",codePath) 824 | # print("codepath:"+codePath) 825 | #浏览器方案 826 | if onlybrowser: 827 | dycode=chromeAuto(codePath) 828 | # cookies方案 829 | else: 830 | # with semaphore: 831 | dycode=newgeturl(codePath) 832 | 833 | # print("获取网页信息完毕") 834 | #获取端口信息 835 | portInfo=getStream_url(dycode) 836 | elif ridcontent[0].find("https://v.douyin.com/")>-1: 837 | portInfo=C_real_url.douyin(ridcontent[0]) 838 | # print("portInfo",portInfo) 839 | else: 840 | portInfo="" 841 | 842 | # print("端口信息:"+str(portInfo)) 843 | # 判断返回参数数组长度,防止出错闪退 844 | if len(portInfo)==4: 845 | #判断状态码 846 | startname=portInfo[0] 847 | if startname in allLive: 848 | print("新增的地址: %s 已经存在,本条线程将会退出"%startname) 849 | namelist.append(str(rid)+"|"+str("#"+rid)) 850 | exit(0) 851 | if ridcontent[1]=="" and Runonce==False: 852 | namelist.append(str(ridcontent[0])+"|"+str(ridcontent[0]+",主播: "+startname.strip())) 853 | Runonce=True 854 | 855 | if portInfo[1]==2: 856 | print(portInfo[0]+" 正在直播中 第"+str(countvariable)+"行") 857 | 858 | 859 | #是否显示地址 860 | if videom3u8: 861 | if videosavetype=="FLV": 862 | print(str(portInfo[0])+ " 直播地址为:"+str(portInfo[3])) 863 | else: 864 | print(str(portInfo[0])+ " 直播地址为:"+str(portInfo[2])) 865 | 866 | #-------------------- 867 | #“portInfo[2]”对应的是m3u8地址 868 | real_url=portInfo[2] 869 | # changestaute=portInfo[1] 870 | if real_url!="": 871 | now = time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime(time.time())) 872 | try: 873 | if len(videopath)>0: 874 | if videopath[-1]!="/": 875 | videopath=videopath+"/" 876 | if not os.path.exists(videopath+startname): 877 | os.makedirs(videopath+startname) 878 | else: 879 | if not os.path.exists(startname): 880 | os.makedirs('./'+startname) 881 | 882 | except Exception as e: 883 | print("路径错误信息708: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 884 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 885 | 886 | 887 | if not os.path.exists(videopath+startname): 888 | print("保存路径不存在,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置") 889 | videopath="" 890 | print("因为配置文件的路径错误,本次录制在程序目录") 891 | 892 | 893 | if videosavetype=="FLV": 894 | filename=startname + '_' + now + '.flv' 895 | filenameshort=startname + '_' + now 896 | 897 | if len(videopath)==0: 898 | print("\r"+startname+" 录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename) 899 | else: 900 | print("\r"+startname+" 录制视频中: "+videopath+startname +'/'+ filename) 901 | 902 | if not os.path.exists(videopath+startname): 903 | print("目录均不能生成文件,不能生成录制.请避免把本程序放在c盘,桌面,下载文件夹,qq默认传输目录.请重新检查设置,程序将会退出") 904 | input("请按回车退出") 905 | os._exit(0) 906 | #flv录制格式 907 | 908 | filenameshort=videopath+startname +'/'+ startname + '_' + now 909 | 910 | if creatTimeFile: 911 | filenamegruop=[startname,filenameshort] 912 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 913 | createVar[str(filenameshort)].daemon=True 914 | createVar[str(filenameshort)].start() 915 | 916 | 917 | try: 918 | #“portInfo[3]”对应的是flv地址,使用老方法下载(直接请求下载flv)只能是下载flv流的。 919 | real_url=portInfo[3] 920 | real_url=real_url.replace("/u0026","&") 921 | #real_url='"'+real_url+'"' 922 | recording.add(startname) 923 | #老的下载方法 924 | _filepath, _ = urllib.request.urlretrieve(real_url, videopath+startname +'/'+ filename) 925 | # videodownload(real_url, videopath+startname +'/'+ filename,0) 926 | recordfinish=True 927 | recordfinish_2=True 928 | counttime=time.time() 929 | if startname in recording: 930 | recording.remove(startname) 931 | if startname in unrecording: 932 | unrecording.add(startname) 933 | 934 | except: 935 | print('\r'+time.strftime('%Y-%m-%d %H:%M:%S ') +startname + ' 未开播') 936 | if startname in recording: 937 | recording.remove(startname) 938 | if startname in unrecording: 939 | unrecording.add(startname) 940 | 941 | elif videosavetype=="MKV": 942 | filename=startname + '_' + now + ".mkv" 943 | if len(videopath)==0: 944 | print("\r"+startname+ " 录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename) 945 | else: 946 | print("\r"+startname+ " 录制视频中: "+videopath+startname +'/'+ filename) 947 | 948 | 949 | ffmpeg_path = "ffmpeg" 950 | file = videopath+startname +'/'+ filename 951 | 952 | filenameshort=videopath+startname +'/'+ startname + '_' + now 953 | 954 | 955 | if creatTimeFile: 956 | filenamegruop=[startname,filenameshort] 957 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 958 | createVar[str(filenameshort)].daemon=True 959 | createVar[str(filenameshort)].start() 960 | 961 | 962 | 963 | 964 | try: 965 | real_url=real_url.replace("/u0026","&") 966 | #real_url='"'+real_url+'"' 967 | recording.add(startname) 968 | _output = subprocess.check_output([ 969 | ffmpeg_path, "-y", 970 | "-v","verbose", 971 | "-rw_timeout","10000000", # 10s 972 | "-loglevel","error", 973 | "-hide_banner", 974 | "-user_agent",headers["User-Agent"], 975 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 976 | "-thread_queue_size","1024", 977 | "-analyzeduration","2147483647", 978 | "-probesize","2147483647", 979 | "-fflags","+discardcorrupt", 980 | "-i",real_url, 981 | "-bufsize","5000k", 982 | "-map","0", 983 | "-sn","-dn", 984 | # "-bsf:v","h264_mp4toannexb", 985 | # "-c","copy", 986 | #"-c:v","libx264", #后期可以用crf来控制大小 987 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 988 | "-c:v","copy", #直接用copy的话体积特别大. 989 | "-c:a","copy", 990 | "-max_muxing_queue_size","64", 991 | "-correct_ts_overflow","1", 992 | "-f","matroska", 993 | "{path}".format(path=file), 994 | ], stderr = subprocess.STDOUT) 995 | 996 | recordfinish=True 997 | recordfinish_2=True 998 | counttime=time.time() 999 | if startname in recording: 1000 | recording.remove(startname) 1001 | if startname in unrecording: 1002 | unrecording.add(startname) 1003 | except subprocess.CalledProcessError as e: 1004 | #logging.warning(str(e.output)) 1005 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1006 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1007 | if startname in recording: 1008 | recording.remove(startname) 1009 | if startname in unrecording: 1010 | unrecording.add(startname) 1011 | 1012 | elif videosavetype=="MP4": 1013 | filename=startname + '_' + now + ".mp4" 1014 | if len(videopath)==0: 1015 | print("\r"+startname+ " 录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename) 1016 | else: 1017 | print("\r"+startname+ " 录制视频中: "+videopath+startname +'/'+ filename) 1018 | 1019 | 1020 | ffmpeg_path = "ffmpeg" 1021 | file = videopath+startname +'/'+ filename 1022 | 1023 | filenameshort=videopath+startname +'/'+ startname + '_' + now 1024 | 1025 | 1026 | if creatTimeFile: 1027 | filenamegruop=[startname,filenameshort] 1028 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 1029 | createVar[str(filenameshort)].daemon=True 1030 | createVar[str(filenameshort)].start() 1031 | 1032 | 1033 | 1034 | 1035 | try: 1036 | real_url=real_url.replace("/u0026","&") 1037 | #real_url='"'+real_url+'"' 1038 | recording.add(startname) 1039 | _output = subprocess.check_output([ 1040 | ffmpeg_path, "-y", 1041 | "-v","verbose", 1042 | "-rw_timeout","10000000", # 10s 1043 | "-loglevel","error", 1044 | "-hide_banner", 1045 | "-user_agent",headers["User-Agent"], 1046 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1047 | "-thread_queue_size","1024", 1048 | "-analyzeduration","2147483647", 1049 | "-probesize","2147483647", 1050 | "-fflags","+discardcorrupt", 1051 | "-i",real_url, 1052 | "-bufsize","5000k", 1053 | "-map","0", 1054 | "-sn","-dn", 1055 | # "-bsf:v","h264_mp4toannexb", 1056 | # "-c","copy", 1057 | #"-c:v","libx264", #后期可以用crf来控制大小 1058 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1059 | "-c:v","copy", #直接用copy的话体积特别大. 1060 | "-c:a","copy", 1061 | "-max_muxing_queue_size","64", 1062 | "-correct_ts_overflow","1", 1063 | "-f","mp4", 1064 | "{path}".format(path=file), 1065 | ], stderr = subprocess.STDOUT) 1066 | 1067 | recordfinish=True 1068 | recordfinish_2=True 1069 | counttime=time.time() 1070 | if startname in recording: 1071 | recording.remove(startname) 1072 | if startname in unrecording: 1073 | unrecording.add(startname) 1074 | except subprocess.CalledProcessError as e: 1075 | #logging.warning(str(e.output)) 1076 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1077 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1078 | if startname in recording: 1079 | recording.remove(startname) 1080 | if startname in unrecording: 1081 | unrecording.add(startname) 1082 | 1083 | elif videosavetype=="MKV音频": 1084 | filename=startname + '_' + now + ".mkv" 1085 | if len(videopath)==0: 1086 | print("\r"+startname+" 录制MKV音频中: "+os.getcwd()+"/"+startname +'/'+ filename) 1087 | else: 1088 | print("\r"+startname+" 录制MKV音频中: "+videopath+startname +'/'+ filename) 1089 | 1090 | ffmpeg_path = "ffmpeg" 1091 | file = videopath+startname +'/'+ filename 1092 | try: 1093 | real_url=real_url.replace("/u0026","&") 1094 | #real_url='"'+real_url+'"' 1095 | recording.add(startname) 1096 | _output = subprocess.check_output([ 1097 | ffmpeg_path,"-y", 1098 | "-v","verbose", 1099 | "-rw_timeout","10000000", # 10s 1100 | "-loglevel","error", 1101 | "-hide_banner", 1102 | "-user_agent",headers["User-Agent"], 1103 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1104 | "-thread_queue_size","1024", 1105 | "-analyzeduration","2147483647", 1106 | "-probesize","2147483647", 1107 | "-fflags","+discardcorrupt", 1108 | "-i",real_url, 1109 | "-bufsize","5000k", 1110 | "-map","0:a", 1111 | "-sn","-dn", 1112 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1113 | "-c:a","copy", 1114 | "-max_muxing_queue_size","64", 1115 | "-correct_ts_overflow","1", 1116 | "-f","matroska", 1117 | "{path}".format(path=file), 1118 | ], stderr = subprocess.STDOUT) 1119 | 1120 | 1121 | recordfinish=True 1122 | recordfinish_2=True 1123 | counttime=time.time() 1124 | if startname in recording: 1125 | recording.remove(startname) 1126 | if startname in unrecording: 1127 | unrecording.add(startname) 1128 | if tsconvertm4a: 1129 | threading.Thread(target=convertsm4a,args=(file,)).start() 1130 | except subprocess.CalledProcessError as e: 1131 | #logging.warning(str(e.output)) 1132 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1133 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1134 | if startname in recording: 1135 | recording.remove(startname) 1136 | if startname in unrecording: 1137 | unrecording.add(startname) 1138 | 1139 | elif videosavetype=="TS音频": 1140 | filename=startname + '_' + now + ".ts" 1141 | if len(videopath)==0: 1142 | print("\r"+startname+" 录制TS音频中: "+os.getcwd()+"/"+startname +'/'+ filename) 1143 | else: 1144 | print("\r"+startname+" 录制TS音频中: "+videopath+startname +'/'+ filename) 1145 | 1146 | ffmpeg_path = "ffmpeg" 1147 | file = videopath+startname +'/'+ filename 1148 | try: 1149 | real_url=real_url.replace("/u0026","&") 1150 | #real_url='"'+real_url+'"' 1151 | recording.add(startname) 1152 | _output = subprocess.check_output([ 1153 | ffmpeg_path,"-y", 1154 | "-v","verbose", 1155 | "-rw_timeout","10000000", # 10s 1156 | "-loglevel","error", 1157 | "-hide_banner", 1158 | "-user_agent",headers["User-Agent"], 1159 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1160 | "-thread_queue_size","1024", 1161 | "-analyzeduration","2147483647", 1162 | "-probesize","2147483647", 1163 | "-fflags","+discardcorrupt", 1164 | "-i",real_url, 1165 | "-bufsize","5000k", 1166 | "-map","0:a", 1167 | "-sn","-dn", 1168 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1169 | "-c:a","copy", 1170 | "-max_muxing_queue_size","64", 1171 | "-correct_ts_overflow","1", 1172 | "-f","mpegts", 1173 | "{path}".format(path=file), 1174 | ], stderr = subprocess.STDOUT) 1175 | 1176 | 1177 | recordfinish=True 1178 | recordfinish_2=True 1179 | counttime=time.time() 1180 | if startname in recording: 1181 | recording.remove(startname) 1182 | if startname in unrecording: 1183 | unrecording.add(startname) 1184 | if tsconvertm4a: 1185 | threading.Thread(target=convertsm4a,args=(file,)).start() 1186 | except subprocess.CalledProcessError as e: 1187 | #logging.warning(str(e.output)) 1188 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1189 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1190 | if startname in recording: 1191 | recording.remove(startname) 1192 | if startname in unrecording: 1193 | unrecording.add(startname) 1194 | 1195 | 1196 | 1197 | else: 1198 | 1199 | if(Splitvideobysize): #这里默认是启用/不启用视频分割功能 1200 | while(True): 1201 | now = time.strftime("%Y-%m-%d-%H-%M-%S",time.localtime(time.time())) 1202 | filename=startname + '_' + now + ".ts" 1203 | if len(videopath)==0: 1204 | print("\r"+startname+" 分段录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename + " 每录满: %d M 存一个视频"%Splitsize ) 1205 | else: 1206 | print("\r"+startname+" 分段录制视频中: "+videopath+startname +'/'+ filename+ " 每录满: %d M 存一个视频"%Splitsize) 1207 | 1208 | ffmpeg_path = "ffmpeg" 1209 | file = videopath+startname +'/'+ filename 1210 | filenameshort=videopath+startname +'/'+ startname + '_' + now 1211 | 1212 | if creatTimeFile: 1213 | filenamegruop=[startname,filenameshort] 1214 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 1215 | createVar[str(filenameshort)].daemon=True 1216 | createVar[str(filenameshort)].start() 1217 | 1218 | 1219 | try: 1220 | real_url=real_url.replace("/u0026","&") 1221 | #real_url='"'+real_url+'"' 1222 | recording.add(startname) 1223 | _output = subprocess.check_output([ 1224 | ffmpeg_path, "-y", 1225 | "-v","verbose", 1226 | "-rw_timeout","10000000", # 10s 1227 | "-loglevel","error", 1228 | "-hide_banner", 1229 | "-user_agent",headers["User-Agent"], 1230 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1231 | "-thread_queue_size","1024", 1232 | "-analyzeduration","2147483647", 1233 | "-probesize","2147483647", 1234 | "-fflags","+discardcorrupt", 1235 | "-i",real_url, 1236 | "-bufsize","5000k", 1237 | "-map","0", 1238 | "-sn","-dn", 1239 | # "-bsf:v","h264_mp4toannexb", 1240 | # "-c","copy", 1241 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1242 | "-c:v","copy", 1243 | "-c:a","copy", 1244 | "-max_muxing_queue_size","64", 1245 | "-correct_ts_overflow","1", 1246 | "-f","mpegts", 1247 | "-fs",str(Splitsizes), 1248 | "{path}".format(path=file), 1249 | ], stderr = subprocess.STDOUT) 1250 | 1251 | 1252 | recordfinish=True #这里表示正常录制成功一次 1253 | recordfinish_2=True 1254 | counttime=time.time() #这个记录当前时间, 用于后面 1分钟内快速2秒循环 这个值不能放到后面 1255 | if startname in recording: 1256 | recording.remove(startname) 1257 | if startname in unrecording: 1258 | unrecording.add(startname) 1259 | if tsconvertmp4: 1260 | threading.Thread(target=convertsmp4,args=(file,)).start() 1261 | if tsconvertm4a: 1262 | threading.Thread(target=convertsm4a,args=(file,)).start() 1263 | 1264 | except subprocess.CalledProcessError as e: 1265 | #这是里分段 如果录制错误会跳转到这里来 1266 | #logging.warning(str(e.output)) 1267 | # print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1268 | # logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1269 | if startname in recording: 1270 | recording.remove(startname) 1271 | if startname in unrecording: 1272 | unrecording.add(startname) 1273 | break 1274 | else: 1275 | filename=startname + '_' + now + ".ts" 1276 | if len(videopath)==0: 1277 | print("\r"+startname+" 录制视频中: "+os.getcwd()+"/"+startname +'/'+ filename) 1278 | else: 1279 | print("\r"+startname+" 录制视频中: "+videopath+startname +'/'+ filename) 1280 | 1281 | ffmpeg_path = "ffmpeg" 1282 | file = videopath+startname +'/'+ filename 1283 | filenameshort=videopath+startname +'/'+ startname + '_' + now 1284 | 1285 | if creatTimeFile: 1286 | filenamegruop=[startname,filenameshort] 1287 | createVar[str(filenameshort)] = threading.Thread(target=creatass,args=(filenamegruop,)) 1288 | createVar[str(filenameshort)].daemon=True 1289 | createVar[str(filenameshort)].start() 1290 | 1291 | 1292 | 1293 | try: 1294 | real_url=real_url.replace("/u0026","&") 1295 | # real_url="\""+real_url+"\"" 1296 | #real_url='"'+real_url+'"' 1297 | # print(real_url) 1298 | recording.add(startname) 1299 | 1300 | 1301 | 1302 | 1303 | abc=[ 1304 | ffmpeg_path, "-y", 1305 | "-v","verbose", 1306 | "-rw_timeout","10000000", # 10s 1307 | "-loglevel","error", 1308 | "-hide_banner", 1309 | "-user_agent",headers["User-Agent"], 1310 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1311 | "-thread_queue_size","1024", 1312 | "-analyzeduration","2147483647", 1313 | "-probesize","2147483647", 1314 | "-fflags","+discardcorrupt", 1315 | "-i",real_url, 1316 | "-bufsize","5000k", 1317 | "-map","0", 1318 | "-sn","-dn", 1319 | # "-bsf:v","h264_mp4toannexb", 1320 | # "-c","copy", 1321 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1322 | "-c:v","copy", 1323 | "-c:a","copy", 1324 | "-max_muxing_queue_size","64", 1325 | "-correct_ts_overflow","1", 1326 | "-f","mpegts", 1327 | "{path}".format(path=file),] 1328 | # print(abc) 1329 | 1330 | _output = subprocess.check_output([ 1331 | ffmpeg_path, "-y", 1332 | "-v","verbose", 1333 | "-rw_timeout","10000000", # 10s 1334 | "-loglevel","error", 1335 | "-hide_banner", 1336 | "-user_agent",headers["User-Agent"], 1337 | "-protocol_whitelist","rtmp,crypto,file,http,https,tcp,tls,udp,rtp", 1338 | "-thread_queue_size","1024", 1339 | "-analyzeduration","2147483647", 1340 | "-probesize","2147483647", 1341 | "-fflags","+discardcorrupt", 1342 | "-i",real_url, 1343 | "-bufsize","5000k", 1344 | "-map","0", 1345 | "-sn","-dn", 1346 | # "-bsf:v","h264_mp4toannexb", 1347 | # "-c","copy", 1348 | "-reconnect_delay_max","30","-reconnect_streamed","-reconnect_at_eof", 1349 | "-c:v","copy", 1350 | "-c:a","copy", 1351 | "-max_muxing_queue_size","64", 1352 | "-correct_ts_overflow","1", 1353 | "-f","mpegts", 1354 | "{path}".format(path=file), 1355 | ], stderr = subprocess.STDOUT) 1356 | 1357 | 1358 | recordfinish=True 1359 | recordfinish_2=True 1360 | counttime=time.time() 1361 | if startname in recording: 1362 | recording.remove(startname) 1363 | if startname in unrecording: 1364 | unrecording.add(startname) 1365 | if tsconvertmp4: 1366 | threading.Thread(target=convertsmp4,args=(file,)).start() 1367 | if tsconvertm4a: 1368 | threading.Thread(target=convertsm4a,args=(file,)).start() 1369 | 1370 | 1371 | except subprocess.CalledProcessError as e: 1372 | #logging.warning(str(e.output)) 1373 | print(str(e.output) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1374 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1375 | if startname in recording: 1376 | recording.remove(startname) 1377 | if startname in unrecording: 1378 | unrecording.add(startname) 1379 | 1380 | else: 1381 | print('直播间不存在或未开播') 1382 | pass 1383 | 1384 | if recordfinish_2==True: 1385 | if startname in recording: 1386 | recording.remove(startname) 1387 | if startname in unrecording: 1388 | unrecording.add(startname) 1389 | print('\n'+startname+" "+ time.strftime('%Y-%m-%d %H:%M:%S ') + '直播录制完成\n') 1390 | recordfinish_2=False 1391 | 1392 | else: 1393 | print("第"+str(countvariable) + "行 " + portInfo[0] + " 等待直播.. ") 1394 | startname=portInfo[0] 1395 | 1396 | 1397 | else: 1398 | if len(startname)==0: 1399 | print('第'+ str(countvariable) +' 行网址内容获取失败,进行重试中...获取失败的地址是:'+ str(line)) 1400 | else: 1401 | print('第'+ str(countvariable) +' 行网址内容获取失败,进行重试中...获取失败的地址是:'+ str(line)+" 主播为:"+str(startname)) 1402 | warning_count+=1 1403 | 1404 | except Exception as e: 1405 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1406 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1407 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1408 | warning_count+=1 1409 | 1410 | 1411 | num = random.randint(-5, 5) + delaydefault # 生成-5到5的随机数,加上delaydefault 1412 | if num < 0: # 如果得到的结果小于0,则将其设置为0 1413 | num = 0 1414 | x=num 1415 | 1416 | # 如果出错太多,就加秒数 1417 | if warning_count>100: 1418 | x=x+120 1419 | print("瞬时错误太多,延迟加120秒") 1420 | 1421 | 1422 | #这里是.如果录制结束后,循环时间会暂时变成30s后检测一遍. 这样一定程度上防止主播卡顿造成少录 1423 | #当30秒过后检测一遍后. 会回归正常设置的循环秒数 1424 | if recordfinish==True: 1425 | counttimeend=time.time()-counttime 1426 | if counttimeend<60: 1427 | x=30 #现在改成默认20s 1428 | recordfinish=False 1429 | else: 1430 | recordfinish=False 1431 | else: 1432 | x=num 1433 | 1434 | #这里是正常循环 1435 | while x: 1436 | x = x-1 1437 | # print('\r循环等待%d秒 '%x) 1438 | if looptime: 1439 | print('\r'+startname+' 循环等待%d秒 '%x ,end="") 1440 | time.sleep(1) 1441 | if looptime: 1442 | print('\r检测直播间中...',end="") 1443 | except Exception as e: 1444 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1445 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1446 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1447 | print("线程崩溃2秒后重试.错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1448 | warning_count+=1 1449 | time.sleep(2) 1450 | def searchUserWeb(url): 1451 | # re=requests.get("https://www.douyin.com/user/MS4wLjABAAAAfSxBYimoCmZ8PjEV0yd0ETo9gZHW9DJCHO0arjojTuQ") 1452 | try: 1453 | 1454 | # headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76'} 1455 | headers3 = {"referer": "https://www.douyin.com/", 1456 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', 1457 | "cookie":cookiesSet} 1458 | 1459 | 1460 | # re=requests.get(url=url,proxies=None) 1461 | re=requests.get(url=url,headers=headers3) 1462 | print(re.text) 1463 | p1=re.text.find(">300: 1471 | res="" 1472 | print("主页解析未完成,有可能是没有在直播.直播时会自动转成直播间网址:"+url) 1473 | else: 1474 | print("主页解析完成:"+url) 1475 | 1476 | url=res 1477 | except Exception as e: 1478 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1479 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1480 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1481 | # print(res) 1482 | return url 1483 | 1484 | 1485 | def checkUrlTxt(UrlTxt): 1486 | lastUrlGroup=[] 1487 | try: 1488 | if len(UrlTxt)<=30: #短连接 1489 | # chrome = webdriver.Chrome(service=ChromeDrivePath,options=chrome_options) 1490 | if displayChrome: 1491 | #是否有浏览器设置 1492 | chrome = webdriver.Chrome() 1493 | else: 1494 | chrome = webdriver.Chrome(options=chrome_options) 1495 | 1496 | try: 1497 | chrome.get(UrlTxt) # 往浏览器的网页地址栏填入url参数 1498 | # 设置显式等待,超时时长最大为5s,每隔1s查找元素一次 1499 | # WebDriverWait(chrome,5,1).until(EC.presence_of_element_located(("class name","fpRIB_wC"))) 1500 | time.sleep(1) 1501 | dyUrl=chrome.current_url 1502 | chrome.quit() 1503 | except: 1504 | dyUrl=chrome.current_url 1505 | print("超时了") 1506 | chrome.quit() 1507 | 1508 | 1509 | # 获取全部标签页 1510 | 1511 | # time.sleep(2) 1512 | # 将激活标签页设置为最新的一项(按自己业务改) 1513 | # chrome.switch_to.window(window.pop()) 1514 | 1515 | position1=dyUrl.find("https://live.douyin.com/") 1516 | if position1>-1: #表示是正常跳转过后的地址 1517 | position2=dyUrl.find("?") 1518 | if position2>-1: 1519 | lastUrl=dyUrl[0:position2] 1520 | lastUrlGroup=[lastUrl,'1'] #1标识为有替换长连接 1521 | else: 1522 | lastUrlGroup=[] #1标识为有替换长连接 1523 | else: 1524 | lastUrlGroup=[] #1标识为有替换长连接 1525 | 1526 | else: 1527 | lastUrlGroup=[] 1528 | 1529 | except Exception as e: 1530 | chrome.quit() 1531 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1532 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1533 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1534 | lastUrlGroup=[] 1535 | 1536 | return(lastUrlGroup) 1537 | 1538 | start5 = datetime.datetime.now() 1539 | def displayinfo(): 1540 | global start5 1541 | time.sleep(5) 1542 | while True: 1543 | try: 1544 | time.sleep(5) 1545 | os.system("cls") 1546 | # print("循环值守录制抖音直播 版本:%s"%str(version)) 1547 | print("\r共监测"+str(Monitoring)+"个直播中", end=" | ") 1548 | #以前的信息 1549 | # print("同一时间访问网络的线程数:",maxrequest, end=" | ") 1550 | if len(videopath)>0: 1551 | if not os.path.exists(videopath): 1552 | print("配置文件里,直播保存路径并不存在,请重新输入一个正确的路径.或留空表示当前目录,按回车退出") 1553 | input("程序结束") 1554 | os._exit(0) 1555 | else: 1556 | print("视频保存路径: "+videopath, end=" | ") 1557 | pass 1558 | else: 1559 | print("视频保存路径: 当前目录", end=" | ") 1560 | 1561 | 1562 | if Splitvideobysize: 1563 | print("TS录制分段开启,录制分段大小为 %d M"%Splitsize, end=" | ") 1564 | 1565 | if onlybrowser: 1566 | print("浏览器检测录制", end=" | ") 1567 | else: 1568 | print("Cookies录制", end=" | ") 1569 | 1570 | 1571 | 1572 | 1573 | 1574 | print("录制视频质量为: "+str(videoQuality), end=" | ") 1575 | print("录制视频格式为: "+str(videosavetype), end=" | ") 1576 | # print("同一时间访问网络的线程数为: "+str(maxrequest), end=" | ") 1577 | print("目前瞬时错误数为: "+str(warning_count), end=" | ") 1578 | nowdate=time.strftime("%H:%M:%S", time.localtime()) 1579 | print(str(nowdate)) 1580 | if len(recording)==0 and len(unrecording)==0: 1581 | time.sleep(5) 1582 | print("\r没有正在录制的直播 "+nowdate,end="") 1583 | print("") 1584 | 1585 | continue 1586 | else: 1587 | # livetask = len(recording) + len(unrecording) 1588 | # print("正则监控{}个直播".format(str(livetask))) 1589 | if len(recording)>0: 1590 | print("x"*60) 1591 | NoRepeatrecording = list(set(recording)) 1592 | print("正在录制{}个直播: ".format(str(len(NoRepeatrecording)))) 1593 | for x in NoRepeatrecording: 1594 | print(x+" 正在录制中") 1595 | #print("%i个直播正在录制中: "%len(NoRepeatrecording)+nowdate) 1596 | end = datetime.datetime.now() 1597 | print('总共录制时间: ' +str(end - start5)) 1598 | print("x"*60) 1599 | else: 1600 | start5 = datetime.datetime.now() 1601 | except Exception as e: 1602 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 1603 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 1604 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 1605 | 1606 | #-------------------------------------------------------------------------------------------------------------------------------------------- 1607 | #最开始运行下面的 1608 | 1609 | 1610 | #备份 1611 | t3 = threading.Thread(target=backup_file_start, args=(), daemon=True) 1612 | t3.start() 1613 | 1614 | t4 = threading.Thread(target=auto_getcookies, args=(), daemon=True) 1615 | t4.start() 1616 | 1617 | #检测ffmpeg是否存在 1618 | ffmepgFileCheck= subprocess.getoutput(["ffmpeg"]) 1619 | if ffmepgFileCheck.find("run")>-1: 1620 | # print("ffmpeg存在") 1621 | pass 1622 | else: 1623 | print("重要提示:") 1624 | print("检测到ffmpeg不存在,请将ffmpeg.exe放到同目录,或者设置为环境变量,没有ffmpeg将无法录制") 1625 | 1626 | Monitoring=0 1627 | while True: 1628 | #循环读取配置 1629 | try: 1630 | f =open("config.ini",'r', encoding='utf-8-sig') 1631 | f.close() 1632 | 1633 | except IOError: 1634 | f = open("config.ini",'w', encoding='utf-8-sig') 1635 | f.close() 1636 | 1637 | 1638 | try: 1639 | config = configparser.ConfigParser() 1640 | config.read('config.ini', encoding='utf-8-sig') 1641 | rid=config.get('1', '直播地址') 1642 | except: 1643 | rid="" 1644 | 1645 | 1646 | if os.path.isfile("URL_config.ini"): 1647 | f =open("URL_config.ini",'r', encoding='utf-8-sig') 1648 | inicontent=f.read() 1649 | f.close() 1650 | else: 1651 | inicontent="" 1652 | 1653 | 1654 | 1655 | if len(inicontent)==0: 1656 | print('请输入要录制的抖音主播pc端直播网址.请注意备份url_config.ini,最近版本增加短连接可能会误删配置里的内容:') 1657 | inurl=input() 1658 | f = open("URL_config.ini",'a+',encoding='utf-8-sig') 1659 | f.write(inurl) 1660 | f.close() 1661 | 1662 | config = configparser.ConfigParser() 1663 | 1664 | config.read('config.ini', encoding='utf-8-sig') 1665 | listx = [] 1666 | listx = config.sections()# 获取到配置文件中所有分组名称 1667 | if '1' not in listx:# 如果分组type不存在则插入type分组 1668 | config.add_section('1') 1669 | 1670 | else: 1671 | config.remove_option('1', '直播地址')# 删除type分组的stuno 1672 | # config.remove_section('tpye')# 删除配置文件中type分组 1673 | 1674 | #config.set('1', '直播地址', inurl)# 给type分组设置值 1675 | config.set('1', '循环时间(秒)', '60')# 给type分组设置值 1676 | 1677 | o = open('config.ini', 'w',encoding='utf-8-sig') 1678 | 1679 | config.write(o) 1680 | 1681 | o.close()#不要忘记关闭 1682 | 1683 | 1684 | 1685 | # try: 1686 | # config = configparser.ConfigParser() 1687 | # config.read('config.ini', encoding='utf-8-sig') 1688 | # maxrequest=config.getint('1', '同一时间访问网络的线程数') 1689 | # except: 1690 | # config = configparser.ConfigParser() 1691 | # # -read读取ini文件 1692 | # config.read('config.ini', encoding='utf-8-sig') 1693 | # listx = [] 1694 | # listx = config.sections()# 获取到配置文件中所有分组名称 1695 | # if '1' not in listx:# 如果分组type不存在则插入type分组 1696 | # config.add_section('1') 1697 | 1698 | # else: 1699 | # config.remove_option('1', '同一时间访问网络的线程数')# 删除type分组的stuno 1700 | # # config.remove_section('tpye')# 删除配置文件中type分组 1701 | 1702 | # config.set('1', '同一时间访问网络的线程数', '3')# 给type分组设置值 1703 | 1704 | # o = open('config.ini', 'w',encoding='utf-8-sig') 1705 | 1706 | # config.write(o) 1707 | 1708 | # o.close()#不要忘记关闭 1709 | # maxrequest=3 1710 | 1711 | # semaphore = threading.Semaphore(maxrequest) 1712 | # print("同一时间访问网络的线程数:",maxrequest) 1713 | 1714 | 1715 | 1716 | 1717 | 1718 | 1719 | 1720 | try: 1721 | config = configparser.ConfigParser() 1722 | config.read('config.ini', encoding='utf-8-sig') 1723 | delaydefault=config.getint('1', '循环时间(秒)') 1724 | except: 1725 | config = configparser.ConfigParser() 1726 | # -read读取ini文件 1727 | config.read('config.ini', encoding='utf-8-sig') 1728 | listx = [] 1729 | listx = config.sections()# 获取到配置文件中所有分组名称 1730 | if '1' not in listx:# 如果分组type不存在则插入type分组 1731 | config.add_section('1') 1732 | 1733 | else: 1734 | config.remove_option('1', '循环时间(秒)')# 删除type分组的stuno 1735 | # config.remove_section('tpye')# 删除配置文件中type分组 1736 | 1737 | config.set('1', '循环时间(秒)', '60')# 给type分组设置值 1738 | 1739 | o = open('config.ini', 'w',encoding='utf-8-sig') 1740 | 1741 | config.write(o) 1742 | 1743 | o.close()#不要忘记关闭 1744 | delaydefault=60 1745 | 1746 | 1747 | 1748 | # config.get(section,option) 得到section中option的值,返回为string类型 1749 | # config.getint(section,option) 得到section中option的值,返回为int类型 1750 | # config.getboolean(section,option) 得到section中option的值,返回为bool类型 1751 | # config.getfloat(section,option) 得到section中option的值,返回为float类型 1752 | 1753 | 1754 | try: 1755 | config = configparser.ConfigParser() 1756 | config.read('config.ini', encoding='utf-8-sig') 1757 | localdelaydefault=config.getint('1', '排队读取网址时间(秒)') 1758 | except: 1759 | config = configparser.ConfigParser() 1760 | # -read读取ini文件 1761 | config.read('config.ini', encoding='utf-8-sig') 1762 | listx = [] 1763 | listx = config.sections()# 获取到配置文件中所有分组名称 1764 | if '1' not in listx:# 如果分组type不存在则插入type分组 1765 | config.add_section('1') 1766 | 1767 | else: 1768 | config.remove_option('1', '排队读取网址时间(秒)')# 删除type分组的stuno 1769 | # config.remove_section('tpye')# 删除配置文件中type分组 1770 | 1771 | config.set('1', '排队读取网址时间(秒)', '3')# 给type分组设置值 1772 | 1773 | o = open('config.ini', 'w',encoding='utf-8-sig') 1774 | 1775 | config.write(o) 1776 | 1777 | o.close()#不要忘记关闭 1778 | localdelaydefault=0 1779 | 1780 | 1781 | 1782 | try: 1783 | config = configparser.ConfigParser() 1784 | config.read('config.ini', encoding='utf-8-sig') 1785 | videopath=config.get('1', '直播保存路径') 1786 | except: 1787 | config = configparser.ConfigParser() 1788 | # -read读取ini文件 1789 | config.read('config.ini', encoding='utf-8-sig') 1790 | listx = [] 1791 | listx = config.sections()# 获取到配置文件中所有分组名称 1792 | if '1' not in listx:# 如果分组type不存在则插入type分组 1793 | config.add_section('1') 1794 | else: 1795 | config.remove_option('1', '直播保存路径')# 删除type分组的stuno 1796 | # config.remove_section('tpye')# 删除配置文件中type分组 1797 | 1798 | config.set('1', '直播保存路径', '')# 给type分组设置值 1799 | 1800 | o = open('config.ini', 'w',encoding='utf-8-sig') 1801 | 1802 | config.write(o) 1803 | 1804 | o.close()#不要忘记关闭 1805 | videopath="" 1806 | 1807 | 1808 | 1809 | 1810 | 1811 | try: 1812 | config = configparser.ConfigParser() 1813 | config.read('config.ini', encoding='utf-8-sig') 1814 | videosavetype=config.get('1', '视频保存格式TS|MKV|FLV|MP4|TS音频|MKV音频') 1815 | 1816 | except Exception as _e: 1817 | 1818 | config = configparser.ConfigParser() 1819 | # -read读取ini文件 1820 | config.read('config.ini', encoding='utf-8-sig') 1821 | listx = [] 1822 | listx = config.sections()# 获取到配置文件中所有分组名称 1823 | if '1' not in listx:# 如果分组type不存在则插入type分组 1824 | config.add_section('1') 1825 | 1826 | else: 1827 | config.remove_option('1', '视频保存格式TS|MKV|FLV|MP4|TS音频|MKV音频')# 删除type分组的stuno 1828 | # config.remove_section('tpye')# 删除配置文件中type分组 1829 | 1830 | 1831 | config.set('1', '视频保存格式TS|MKV|FLV|MP4|TS音频|MKV音频',"TS")# 给type分组设置值 1832 | 1833 | o = open('config.ini', 'w',encoding='utf-8-sig') 1834 | 1835 | config.write(o) 1836 | 1837 | o.close()#不要忘记关闭 1838 | videosavetype="TS" 1839 | 1840 | if len(videosavetype)>0: 1841 | if videosavetype.upper().lower()=="FLV".lower(): 1842 | videosavetype="FLV" 1843 | # print("直播视频保存为FLV格式") 1844 | elif videosavetype.upper().lower()=="MKV".lower(): 1845 | videosavetype="MKV" 1846 | # print("直播视频保存为MKV格式") 1847 | elif videosavetype.upper().lower()=="TS".lower(): 1848 | videosavetype="TS" 1849 | # print("直播视频保存为TS格式") 1850 | elif videosavetype.upper().lower()=="MP4".lower(): 1851 | videosavetype="MP4" 1852 | # print("直播视频保存为MP4格式") 1853 | elif videosavetype.upper().lower()=="TS音频".lower(): 1854 | videosavetype="TS音频" 1855 | # print("直播视频保存为TS音频格式") 1856 | elif videosavetype.upper().lower()=="MKV音频".lower(): 1857 | videosavetype="MKV音频" 1858 | # print("直播视频保存为MKV音频格式") 1859 | else: 1860 | videosavetype="TS" 1861 | print("直播视频保存格式设置有问题,这次录制重置为默认的TS格式") 1862 | else: 1863 | videosavetype="TS" 1864 | print("直播视频保存为TS格式") 1865 | #------------------------------------------ 1866 | #选择画质 1867 | try: 1868 | config = configparser.ConfigParser() 1869 | config.read('config.ini', encoding='utf-8-sig') 1870 | videoQuality=config.get('1', '原画|超清|高清|标清') 1871 | 1872 | except Exception as _e: 1873 | 1874 | config = configparser.ConfigParser() 1875 | # -read读取ini文件 1876 | config.read('config.ini', encoding='utf-8-sig') 1877 | listx = [] 1878 | listx = config.sections()# 获取到配置文件中所有分组名称 1879 | if '1' not in listx:# 如果分组type不存在则插入type分组 1880 | config.add_section('1') 1881 | 1882 | else: 1883 | config.remove_option('1', '原画|超清|高清|标清')# 删除type分组的stuno 1884 | # config.remove_section('tpye')# 删除配置文件中type分组 1885 | 1886 | 1887 | config.set('1', '原画|超清|高清|标清',"原画")# 给type分组设置值 1888 | 1889 | o = open('config.ini', 'w',encoding='utf-8-sig') 1890 | 1891 | config.write(o) 1892 | 1893 | o.close()#不要忘记关闭 1894 | videoQuality="原画" 1895 | 1896 | #是否显示直播地址 1897 | try: 1898 | config = configparser.ConfigParser() 1899 | config.read('config.ini', encoding='utf-8-sig') 1900 | videom3u8=config.get('1', '是否显示直播地址') 1901 | 1902 | except Exception as _e: 1903 | 1904 | config = configparser.ConfigParser() 1905 | # -read读取ini文件 1906 | config.read('config.ini', encoding='utf-8-sig') 1907 | listx = [] 1908 | listx = config.sections()# 获取到配置文件中所有分组名称 1909 | if '1' not in listx:# 如果分组type不存在则插入type分组 1910 | config.add_section('1') 1911 | 1912 | else: 1913 | config.remove_option('1', '是否显示直播地址')# 删除type分组的stuno 1914 | # config.remove_section('tpye')# 删除配置文件中type分组 1915 | 1916 | 1917 | config.set('1', '是否显示直播地址',"否")# 给type分组设置值 1918 | 1919 | o = open('config.ini', 'w',encoding='utf-8-sig') 1920 | 1921 | config.write(o) 1922 | 1923 | o.close()#不要忘记关闭 1924 | videom3u8="否" 1925 | 1926 | 1927 | if videom3u8=="是": 1928 | videom3u8=True 1929 | else: 1930 | videom3u8=False 1931 | 1932 | 1933 | 1934 | 1935 | 1936 | 1937 | #是否显示循环秒数 1938 | try: 1939 | config = configparser.ConfigParser() 1940 | config.read('config.ini', encoding='utf-8-sig') 1941 | looptime=config.get('1', '是否显示循环秒数') 1942 | 1943 | except Exception as _e: 1944 | 1945 | config = configparser.ConfigParser() 1946 | # -read读取ini文件 1947 | config.read('config.ini', encoding='utf-8-sig') 1948 | listx = [] 1949 | listx = config.sections()# 获取到配置文件中所有分组名称 1950 | if '1' not in listx:# 如果分组type不存在则插入type分组 1951 | config.add_section('1') 1952 | 1953 | else: 1954 | config.remove_option('1', '是否显示循环秒数')# 删除type分组的stuno 1955 | # config.remove_section('tpye')# 删除配置文件中type分组 1956 | 1957 | 1958 | config.set('1', '是否显示循环秒数',"否")# 给type分组设置值 1959 | 1960 | o = open('config.ini', 'w',encoding='utf-8-sig') 1961 | 1962 | config.write(o) 1963 | 1964 | o.close()#不要忘记关闭 1965 | looptime="否" 1966 | 1967 | 1968 | if looptime=="是": 1969 | looptime=True 1970 | else: 1971 | looptime=False 1972 | 1973 | 1974 | # #这里是控制是否设置代理 1975 | # try: 1976 | # config = configparser.ConfigParser() 1977 | # config.read('config.ini', encoding='utf-8-sig') 1978 | # proxies2=config.get('1', '本地代理端口') 1979 | # proxiesn=proxies2 1980 | # if len(proxies2)>0: 1981 | 1982 | # proxies2={'https': 'http://127.0.0.1:'+str(proxies2)} 1983 | # print("检测到有设置代理地址为: "+'http://127.0.0.1:'+str(proxiesn)) 1984 | 1985 | # except Exception as _e: 1986 | 1987 | # config = configparser.ConfigParser() 1988 | # # -read读取ini文件 1989 | # config.read('config.ini', encoding='utf-8-sig') 1990 | # listx = [] 1991 | # listx = config.sections()# 获取到配置文件中所有分组名称 1992 | # if '1' not in listx:# 如果分组type不存在则插入type分组 1993 | # config.add_section('1') 1994 | 1995 | # else: 1996 | # config.remove_option('1', '本地代理端口')# 删除type分组的stuno 1997 | # # config.remove_section('tpye')# 删除配置文件中type分组 1998 | 1999 | 2000 | # config.set('1', '本地代理端口',"")# 给type分组设置值 2001 | 2002 | # o = open('config.ini', 'w',encoding='utf-8-sig') 2003 | 2004 | # config.write(o) 2005 | 2006 | # o.close()#不要忘记关闭 2007 | 2008 | # proxies2="" 2009 | 2010 | 2011 | #这里是控制TS是否分段 2012 | try: 2013 | config = configparser.ConfigParser() 2014 | config.read('config.ini', encoding='utf-8-sig') 2015 | Splitvideobysize=config.get('1', 'TS格式分段录制是否开启') 2016 | 2017 | except Exception as _e: 2018 | config = configparser.ConfigParser() 2019 | # -read读取ini文件 2020 | config.read('config.ini', encoding='utf-8-sig') 2021 | listx = [] 2022 | listx = config.sections()# 获取到配置文件中所有分组名称 2023 | if '1' not in listx:# 如果分组type不存在则插入type分组 2024 | config.add_section('1') 2025 | 2026 | else: 2027 | config.remove_option('1', 'TS格式分段录制是否开启')# 删除type分组的stuno 2028 | # config.remove_section('tpye')# 删除配置文件中type分组 2029 | 2030 | 2031 | config.set('1', 'TS格式分段录制是否开启',"否")# 给type分组设置值 2032 | 2033 | o = open('config.ini', 'w',encoding='utf-8-sig') 2034 | 2035 | config.write(o) 2036 | 2037 | o.close()#不要忘记关闭 2038 | Splitvideobysize="否" 2039 | 2040 | 2041 | if Splitvideobysize=="是": 2042 | Splitvideobysize=True 2043 | else: 2044 | Splitvideobysize=False 2045 | 2046 | 2047 | 2048 | 2049 | #这里是控制TS分段大小 2050 | 2051 | try: 2052 | config = configparser.ConfigParser() 2053 | config.read('config.ini', encoding='utf-8-sig') 2054 | Splitsize=config.getint('1', '视频分段大小(兆)') 2055 | except: 2056 | config = configparser.ConfigParser() 2057 | # -read读取ini文件 2058 | config.read('config.ini', encoding='utf-8-sig') 2059 | listx = [] 2060 | listx = config.sections()# 获取到配置文件中所有分组名称 2061 | if '1' not in listx:# 如果分组type不存在则插入type分组 2062 | config.add_section('1') 2063 | else: 2064 | config.remove_option('1', '视频分段大小(兆)')# 删除type分组的stuno 2065 | # config.remove_section('tpye')# 删除配置文件中type分组 2066 | config.set('1', '视频分段大小(兆)', '1000')# 给type分组设置值 2067 | o = open('config.ini', 'w',encoding='utf-8-sig') 2068 | config.write(o) 2069 | o.close()#不要忘记关闭 2070 | Splitsize=1000 #1g 2071 | 2072 | #分段大小不能小于5m 2073 | if Splitsize<5: 2074 | Splitsize=5 #最低不能小于5m 2075 | 2076 | Splitsizes=Splitsize*1024*1024 #分割视频大小,转换为字节 2077 | 2078 | 2079 | 2080 | 2081 | 2082 | 2083 | #这里是控制TS是否追加mp4格式 2084 | try: 2085 | config = configparser.ConfigParser() 2086 | config.read('config.ini', encoding='utf-8-sig') 2087 | tsconvertmp4=config.get('1', 'TS录制完成后自动增加生成MP4格式') 2088 | 2089 | except Exception as _e: 2090 | config = configparser.ConfigParser() 2091 | # -read读取ini文件 2092 | config.read('config.ini', encoding='utf-8-sig') 2093 | listx = [] 2094 | listx = config.sections()# 获取到配置文件中所有分组名称 2095 | if '1' not in listx:# 如果分组type不存在则插入type分组 2096 | config.add_section('1') 2097 | 2098 | else: 2099 | config.remove_option('1', 'TS录制完成后自动增加生成MP4格式')# 删除type分组的stuno 2100 | # config.remove_section('tpye')# 删除配置文件中type分组 2101 | 2102 | 2103 | config.set('1', 'TS录制完成后自动增加生成MP4格式',"否")# 给type分组设置值 2104 | 2105 | o = open('config.ini', 'w',encoding='utf-8-sig') 2106 | 2107 | config.write(o) 2108 | 2109 | o.close()#不要忘记关闭 2110 | tsconvertmp4="否" 2111 | 2112 | 2113 | if tsconvertmp4=="是": 2114 | tsconvertmp4=True 2115 | else: 2116 | tsconvertmp4=False 2117 | 2118 | #这里是控制TS是否追加m4a格式 2119 | try: 2120 | config = configparser.ConfigParser() 2121 | config.read('config.ini', encoding='utf-8-sig') 2122 | tsconvertm4a=config.get('1', 'TS录制完成后自动增加生成m4a格式') 2123 | 2124 | except Exception as _e: 2125 | config = configparser.ConfigParser() 2126 | # -read读取ini文件 2127 | config.read('config.ini', encoding='utf-8-sig') 2128 | listx = [] 2129 | listx = config.sections()# 获取到配置文件中所有分组名称 2130 | if '1' not in listx:# 如果分组type不存在则插入type分组 2131 | config.add_section('1') 2132 | 2133 | else: 2134 | config.remove_option('1', 'TS录制完成后自动增加生成m4a格式')# 删除type分组的stuno 2135 | # config.remove_section('tpye')# 删除配置文件中type分组 2136 | 2137 | 2138 | config.set('1', 'TS录制完成后自动增加生成m4a格式',"否")# 给type分组设置值 2139 | 2140 | o = open('config.ini', 'w',encoding='utf-8-sig') 2141 | 2142 | config.write(o) 2143 | 2144 | o.close()#不要忘记关闭 2145 | tsconvertm4a="否" 2146 | 2147 | if tsconvertm4a=="是": 2148 | tsconvertm4a=True 2149 | else: 2150 | tsconvertm4a=False 2151 | 2152 | #追加格式后,是否删除原文件 2153 | try: 2154 | config = configparser.ConfigParser() 2155 | config.read('config.ini', encoding='utf-8-sig') 2156 | delFilebeforeconversion=config.get('1', '追加格式后删除原文件') 2157 | 2158 | except Exception as _e: 2159 | config = configparser.ConfigParser() 2160 | # -read读取ini文件 2161 | config.read('config.ini', encoding='utf-8-sig') 2162 | listx = [] 2163 | listx = config.sections()# 获取到配置文件中所有分组名称 2164 | if '1' not in listx:# 如果分组type不存在则插入type分组 2165 | config.add_section('1') 2166 | 2167 | else: 2168 | config.remove_option('1', '追加格式后删除原文件')# 删除type分组的stuno 2169 | # config.remove_section('tpye')# 删除配置文件中type分组 2170 | 2171 | 2172 | config.set('1', '追加格式后删除原文件',"否")# 给type分组设置值 2173 | 2174 | o = open('config.ini', 'w',encoding='utf-8-sig') 2175 | 2176 | config.write(o) 2177 | 2178 | o.close()#不要忘记关闭 2179 | delFilebeforeconversion="否" 2180 | 2181 | if delFilebeforeconversion=="是": 2182 | delFilebeforeconversion=True 2183 | else: 2184 | delFilebeforeconversion=False 2185 | 2186 | def tranform_int_to_time(seconds): 2187 | m, s = divmod(seconds, 60) 2188 | h, m = divmod(m, 60) 2189 | return ("%d:%02d:%02d" % (h, m, s)) 2190 | 2191 | 2192 | #这里控制是否生成时间文件 2193 | try: 2194 | config = configparser.ConfigParser() 2195 | config.read('config.ini', encoding='utf-8-sig') 2196 | creatTimeFile=config.get('1', '生成时间文件') 2197 | 2198 | except Exception as _e: 2199 | config = configparser.ConfigParser() 2200 | # -read读取ini文件 2201 | config.read('config.ini', encoding='utf-8-sig') 2202 | listx = [] 2203 | listx = config.sections()# 获取到配置文件中所有分组名称 2204 | if '1' not in listx:# 如果分组type不存在则插入type分组 2205 | config.add_section('1') 2206 | 2207 | else: 2208 | config.remove_option('1', '生成时间文件')# 删除type分组的stuno 2209 | # config.remove_section('tpye')# 删除配置文件中type分组 2210 | 2211 | 2212 | config.set('1', '生成时间文件',"否")# 给type分组设置值 2213 | 2214 | o = open('config.ini', 'w',encoding='utf-8-sig') 2215 | 2216 | config.write(o) 2217 | 2218 | o.close()#不要忘记关闭 2219 | creatTimeFile="否" 2220 | 2221 | 2222 | if creatTimeFile=="是": 2223 | creatTimeFile=True 2224 | else: 2225 | creatTimeFile=False 2226 | 2227 | 2228 | 2229 | # #这里设置浏览器位置 2230 | # try: 2231 | # config = configparser.ConfigParser() 2232 | # config.read('config.ini', encoding='utf-8-sig') 2233 | # chromePath=config.get('1', '浏览器位置') 2234 | 2235 | # except Exception as _e: 2236 | 2237 | # config = configparser.ConfigParser() 2238 | # # -read读取ini文件 2239 | # config.read('config.ini', encoding='utf-8-sig') 2240 | # listx = [] 2241 | # listx = config.sections()# 获取到配置文件中所有分组名称 2242 | # if '1' not in listx:# 如果分组type不存在则插入type分组 2243 | # config.add_section('1') 2244 | 2245 | # else: 2246 | # config.remove_option('1', '浏览器位置')# 删除type分组的stuno 2247 | # # config.remove_section('tpye')# 删除配置文件中type分组 2248 | 2249 | 2250 | # config.set('1', '浏览器位置','data\chrome.exe')# 给type分组设置值 2251 | 2252 | # o = open('config.ini', 'w',encoding='utf-8-sig') 2253 | 2254 | # config.write(o) 2255 | 2256 | # o.close()#不要忘记关闭 2257 | # chromePath='data\chrome.exe' 2258 | 2259 | 2260 | #这里控制是否生显示浏览器 2261 | try: 2262 | config = configparser.ConfigParser() 2263 | config.read('config.ini', encoding='utf-8-sig') 2264 | displayChrome=config.get('1', '是否显示浏览器') 2265 | 2266 | except Exception as _e: 2267 | config = configparser.ConfigParser() 2268 | # -read读取ini文件 2269 | config.read('config.ini', encoding='utf-8-sig') 2270 | listx = [] 2271 | listx = config.sections()# 获取到配置文件中所有分组名称 2272 | if '1' not in listx:# 如果分组type不存在则插入type分组 2273 | config.add_section('1') 2274 | 2275 | else: 2276 | config.remove_option('1', '是否显示浏览器')# 删除type分组的stuno 2277 | # config.remove_section('tpye')# 删除配置文件中type分组 2278 | 2279 | 2280 | config.set('1', '是否显示浏览器',"否")# 给type分组设置值 2281 | 2282 | o = open('config.ini', 'w',encoding='utf-8-sig') 2283 | 2284 | config.write(o) 2285 | 2286 | o.close()#不要忘记关闭 2287 | displayChrome="否" 2288 | 2289 | 2290 | if displayChrome=="是": 2291 | displayChrome=True 2292 | else: 2293 | displayChrome=False 2294 | 2295 | 2296 | 2297 | #这里是控制是否转换短链接 2298 | try: 2299 | config = configparser.ConfigParser() 2300 | config.read('config.ini', encoding='utf-8-sig') 2301 | coverlongurl=config.get('1', '短链接自动转换为长连接') 2302 | 2303 | except Exception as _e: 2304 | config = configparser.ConfigParser() 2305 | # -read读取ini文件 2306 | config.read('config.ini', encoding='utf-8-sig') 2307 | listx = [] 2308 | listx = config.sections()# 获取到配置文件中所有分组名称 2309 | if '1' not in listx:# 如果分组type不存在则插入type分组 2310 | config.add_section('1') 2311 | 2312 | else: 2313 | config.remove_option('1', '短链接自动转换为长连接')# 删除type分组的stuno 2314 | # config.remove_section('tpye')# 删除配置文件中type分组 2315 | 2316 | 2317 | config.set('1', '短链接自动转换为长连接',"否")# 给type分组设置值 2318 | 2319 | o = open('config.ini', 'w',encoding='utf-8-sig') 2320 | 2321 | config.write(o) 2322 | 2323 | o.close()#不要忘记关闭 2324 | coverlongurl="否" 2325 | 2326 | 2327 | if coverlongurl=="是": 2328 | coverlongurl=True 2329 | else: 2330 | coverlongurl=False 2331 | 2332 | 2333 | 2334 | #这里是控制采用浏览器录制 2335 | try: 2336 | config = configparser.ConfigParser() 2337 | config.read('config.ini', encoding='utf-8-sig') 2338 | onlybrowser=config.get('1', '仅用浏览器录制') 2339 | 2340 | except Exception as _e: 2341 | config = configparser.ConfigParser() 2342 | # -read读取ini文件 2343 | config.read('config.ini', encoding='utf-8-sig') 2344 | listx = [] 2345 | listx = config.sections()# 获取到配置文件中所有分组名称 2346 | if '1' not in listx:# 如果分组type不存在则插入type分组 2347 | config.add_section('1') 2348 | 2349 | else: 2350 | config.remove_option('1', '仅用浏览器录制')# 删除type分组的stuno 2351 | # config.remove_section('tpye')# 删除配置文件中type分组 2352 | 2353 | 2354 | config.set('1', '仅用浏览器录制',"否")# 给type分组设置值 2355 | 2356 | o = open('config.ini', 'w',encoding='utf-8-sig') 2357 | 2358 | config.write(o) 2359 | 2360 | o.close()#不要忘记关闭 2361 | onlybrowser="否" 2362 | 2363 | 2364 | if onlybrowser=="是": 2365 | onlybrowser=True 2366 | else: 2367 | onlybrowser=False 2368 | 2369 | 2370 | # #这里设置浏览器驱动位置 2371 | # try: 2372 | # config = configparser.ConfigParser() 2373 | # config.read('config.ini', encoding='utf-8-sig') 2374 | # chrome_DrivePath=config.get('1', '浏览器驱动位置') 2375 | 2376 | # except Exception as _e: 2377 | 2378 | # config = configparser.ConfigParser() 2379 | # # -read读取ini文件 2380 | # config.read('config.ini', encoding='utf-8-sig') 2381 | # listx = [] 2382 | # listx = config.sections()# 获取到配置文件中所有分组名称 2383 | # if '1' not in listx:# 如果分组type不存在则插入type分组 2384 | # config.add_section('1') 2385 | 2386 | # else: 2387 | # config.remove_option('1', '浏览器驱动位置')# 删除type分组的stuno 2388 | # # config.remove_section('tpye')# 删除配置文件中type分组 2389 | 2390 | 2391 | # config.set('1', '浏览器驱动位置','"chromedriver.exe"')# 给type分组设置值 2392 | 2393 | # o = open('config.ini', 'w',encoding='utf-8-sig') 2394 | 2395 | # config.write(o) 2396 | 2397 | # o.close()#不要忘记关闭 2398 | # chrome_DrivePath='"chromedriver.exe"' 2399 | 2400 | 2401 | 2402 | 2403 | 2404 | # print("最开始排队 "+ str(localdelaydefault)+" 秒后读取下一个地址") 2405 | #cookiesSet 2406 | cookiesSet='' 2407 | try: 2408 | config = RawConfigParser() 2409 | config.read('config.ini', encoding='utf-8-sig') 2410 | cookiesSet_temp=config.get('1', 'cookies') 2411 | if cookiesSet_temp != "": 2412 | cookiesSet=cookiesSet_temp 2413 | except: 2414 | config = configparser.ConfigParser() 2415 | # -read读取ini文件 2416 | config.read('config.ini', encoding='utf-8-sig') 2417 | listx = [] 2418 | listx = config.sections()# 获取到配置文件中所有分组名称 2419 | if '1' not in listx:# 如果分组type不存在则插入type分组 2420 | config.add_section('1') 2421 | 2422 | else: 2423 | config.remove_option('1', 'cookies')# 删除type分组的stuno 2424 | # config.remove_section('tpye')# 删除配置文件中type分组 2425 | 2426 | config.set('1', 'cookies', '')# 给type分组设置值 2427 | 2428 | o = open('config.ini', 'w',encoding='utf-8-sig') 2429 | 2430 | config.write(o) 2431 | 2432 | o.close()#不要忘记关闭 2433 | 2434 | if cookiesSet == "": 2435 | cookiesSet = get_cookies() 2436 | 2437 | # if cookiesSet=="": 2438 | # print("一般需要设置一个cookies才可以运行") 2439 | 2440 | # 谷歌浏览器驱动地址 2441 | # chrome_drive = 'data/chromedriver.exe' 2442 | # # 谷歌浏览器位置 2443 | # chromePath= r'D:\Software\JobSoftware\Chromium\Application\Chromium.exe' 2444 | #设置浏览器位置 2445 | 2446 | # if os.path.exists(chrome_DrivePath)==False: 2447 | # print("错误-------------浏览器驱动不存在,请至少同目录有没有chromedriver文件") 2448 | # # time.sleep(3) 2449 | 2450 | # print(chrome_DrivePath) 2451 | # ChromeDrivePath = Service(chrome_DrivePath) 2452 | 2453 | chrome_options = Options() 2454 | 2455 | # 配置不显示浏览器 2456 | chrome_options.add_argument('--headless') 2457 | chrome_options.add_argument('--disable-gpu') 2458 | chrome_options.add_argument('User-Agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36') 2459 | chrome_options.add_experimental_option('excludeSwitches', ['enable-logging']) 2460 | #忽略错误 2461 | chrome_options.add_argument('--ignore-certificate-errors') 2462 | chrome_options.add_argument('--ignore-ssl-errors') 2463 | 2464 | 2465 | #读取url文件 2466 | try: 2467 | file=open("URL_config.ini","r",encoding="utf-8-sig") 2468 | a=0 2469 | for line in file: 2470 | a+=1 #在文本里第几行 2471 | line=line.strip() 2472 | if line.startswith("#"): 2473 | continue 2474 | c=line.split() 2475 | if len(c)==0: 2476 | continue 2477 | else: 2478 | c=line.strip() 2479 | 2480 | c=c.split('#') 2481 | if len(line)<20: 2482 | continue 2483 | 2484 | 2485 | SplitTest = line.split(',') 2486 | #判断有没有逗号,有逗号获取逗号前面的地址 2487 | if len(SplitTest)>1: 2488 | resline= SplitTest[0] 2489 | else: 2490 | resline=line 2491 | 2492 | #短连接转换长连接 2493 | if coverlongurl: 2494 | #查询是否为主页:有下面这个网址的,返回位置 2495 | if resline.find("https://www.douyin.com/user/")>-1: 2496 | print("转换主页路径") 2497 | res=searchUserWeb(resline) 2498 | 2499 | #判断有没有正常返回路径. 如果上面函数出错,会返回输入的值. 那么判断出错,就跳过 2500 | if res!=resline and res!="": 2501 | lastdyurl=res 2502 | namelist.append(str(resline)+"|"+str(res)) 2503 | while len(namelist): 2504 | a=namelist.pop() 2505 | replacewords = a.split('|') 2506 | if replacewords[0]!=replacewords[1]: 2507 | updateFile(r"URL_config.ini", replacewords[0], replacewords[1]) 2508 | print("转换了路径为:"+str(replacewords[1])) 2509 | resline=replacewords[1] 2510 | 2511 | 2512 | elif resline.find("https://v.douyin.com/")>-1: 2513 | #查询短链接 2514 | dyUrlGroup=checkUrlTxt(resline) 2515 | if len(dyUrlGroup)==2: 2516 | if dyUrlGroup[1]=="1": 2517 | namelist.append(str(resline)+"|"+str(dyUrlGroup[0])) 2518 | while len(namelist): 2519 | a=namelist.pop() 2520 | replacewords = a.split('|') 2521 | if replacewords[0]!=replacewords[1]: 2522 | updateFile(r"URL_config.ini", replacewords[0], replacewords[1]) 2523 | print("转换了路径为:"+str(replacewords[1])) 2524 | 2525 | 2526 | 2527 | 2528 | if resline.find("https://live.douyin.com/")>-1 or resline.find("https://v.douyin.com/")>-1: 2529 | 2530 | #因为后面需要网址假主播名字,这里再补上 2531 | if len(SplitTest)>1: 2532 | lastdyurl= (resline,SplitTest[1],a) 2533 | else: 2534 | lastdyurl=(resline,"",a) 2535 | 2536 | texturl.append(lastdyurl) 2537 | 2538 | 2539 | else: 2540 | print(str(resline)+" 未知链接.此条跳过") 2541 | 2542 | 2543 | 2544 | 2545 | 2546 | #print(c[0]) 2547 | file.close() 2548 | 2549 | while len(namelist): 2550 | a=namelist.pop() 2551 | replacewords = a.split('|') 2552 | if replacewords[0]!=replacewords[1]: 2553 | updateFile(r"URL_config.ini", replacewords[0], replacewords[1]) 2554 | #格式化后查找不一样 2555 | 2556 | if len(texturl)>0: 2557 | textNoRepeatUrl = list(set(texturl)) 2558 | 2559 | 2560 | if len(textNoRepeatUrl)>0: 2561 | for i in textNoRepeatUrl: 2562 | #formatcontent[0] #这个为分离出的地址 2563 | if i[0] not in runingList: 2564 | if firstStart==False: #第一次 不会出现新增链接的字眼 2565 | print("新增链接: "+ str(i[0]) +" 在url配置的第"+str(i[2]) +"行") 2566 | Monitoring=Monitoring+1 2567 | args = [i, i[2]] 2568 | createVar['thread'+ str(Monitoring)] = threading.Thread(target=startgo,args=args) 2569 | createVar['thread'+ str(Monitoring)].daemon=True 2570 | createVar['thread'+ str(Monitoring)].start() 2571 | runingList.append(i[0]) 2572 | # print("\r"+str(localdelaydefault)+" 秒后读取下一个地址(如果存在) ") 2573 | time.sleep(localdelaydefault) 2574 | texturl=[] 2575 | firstStart=False #第一次 不会出现新增链接的字眼 2576 | 2577 | except Exception as e: 2578 | print("错误信息644:"+str(e)+"\r\n读取的地址为: "+str(rid) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno) ) 2579 | # print(rid+' 的直播地址连接失败,请检测这个地址是否正常,可以重启本程序--requests获取失败') 2580 | logger.warning("错误信息: "+str(e) +" 发生错误的行数: "+str(e.__traceback__.tb_lineno)) 2581 | 2582 | 2583 | #这个是第一次运行其他线程.因为有变量前后顺序的问题,这里等全局变量完成后再运行def函数 2584 | if firstRunOtherLine: 2585 | t = threading.Thread(target=displayinfo, args=(), daemon=True) 2586 | t.start() 2587 | t2 = threading.Thread(target=changemaxconnect, args=(), daemon=True) 2588 | t2.start() 2589 | 2590 | firstRunOtherLine=False 2591 | 2592 | #总体循环3s 2593 | time.sleep(3) 2594 | #print('程式结束,请按任意键退出') 2595 | input() 2596 | 2597 | 2598 | --------------------------------------------------------------------------------