├── 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 |
--------------------------------------------------------------------------------