├── N_m3u8DL-RE_Beta_win-x64
├── N_m3u8DL-RE.exe
└── ffmpeg.exe
├── README.md
├── RealListWindow.py
├── RealRid.py
├── RealTool.py
├── chromedriver.exe
├── config_ini.py
├── image
├── 73114636.png
├── 73114636_bg.png
├── close_icon.png
├── close_icon_2.png
├── close_icon_3.png
├── copy.png
├── icon.ico
├── icon.png
├── image_1.jpg
├── image_2.jpg
├── image_3.jpg
├── image_4.jpg
├── logo.png
├── logo_get.png
├── lol_s12.png
├── min_icon.png
├── play.png
├── rec.png
├── save.png
├── setting_icon.png
└── size_icon.png
├── qtbase_zh_CN.qm
└── real
├── bili.py
├── douyin.py
├── douyu.py
├── huya.py
├── kugou.py
├── kuwo.py
├── requests_code.py
├── rquests_html_1.py
├── yizhibo.py
└── yy.py
/N_m3u8DL-RE_Beta_win-x64/N_m3u8DL-RE.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/N_m3u8DL-RE_Beta_win-x64/N_m3u8DL-RE.exe
--------------------------------------------------------------------------------
/N_m3u8DL-RE_Beta_win-x64/ffmpeg.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/N_m3u8DL-RE_Beta_win-x64/ffmpeg.exe
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RealTool
2 |
3 |
4 | ## 软件声明
5 | 此工具语言为Python【3.8.10】,GUI是PyQt5【PyQt5==5.14.2】,打包是用pyinstaller,360可能会报毒,所有设置都保存在本地,介意勿下!
6 | 蓝奏网盘:https://wwjd.lanzout.com/b00w74arg 密码:1234
7 |
8 | ## 关于RealTool
9 | RealTool是一款获取直播源链接的软件,支持获取【**斗鱼**】【**虎牙**】【**B站**】【**抖音**】【**YY**】【**酷我**】【**酷狗**】【**一直播**】的直播源链接,并且支持录制等功能!
10 | 有任何问题可以发Issues,也可以加入聊天群【1134070718】进行问题反馈!
11 |
12 | ## 关于开源
13 | 本软件的获取链接是基于两位大佬的开源项目
14 | 1. 获取直播源开源链接wbt5:https://github.com/wbt5/real-url
15 | 2. m3u8录制开源链接nilaoda:https://github.com/nilaoda/N_m3u8DL-RE
16 |
17 |
18 | ### 2022年10月22日
19 | **修复虎牙斗鱼直播源无法正确获取的问题,这是上版本修改过程中出现的BUG,现已修复!**
20 | **新增批量获取直播源,需要先手动添加直播间,然后输入框为空的情况下点击获取或者回车即可批量获取!**
21 |
22 | ### 2022年10月24日
23 | **新增【YY直播】【酷我直播】【酷狗直播】**
24 | **新增多线程访问,现在能更快的获取到直播源了**
25 |
26 | ### 2022年10月24日14:34
27 | **修复同一平台批量获取只能获取到一个的问题**
28 |
29 | ### 2022年10月26日
30 | **新采用Nuitka打包,理论上运行速度和兼容版本更强,只有64位【以前都是Pyinstaller打包】**
31 | **修复批量获取只有第一个平台才能获取成功的问题**
32 | **修复抖音直播源画质问题**
33 |
34 | ### 2022年10月27日
35 | **新增获取主播昵称功能,获取列表和保存列表均已添加**
36 |
37 | ### 2022年10月29日
38 | **修复YY直播获取失败的问题**
39 | **调整虎牙画质获取方式,现在强制获取所有虎牙直播画质**
40 |
41 | ### 2022年10月31日
42 | **修复YY直播因正则匹配原因导致软件崩溃的问题**
43 | **新增斗鱼m3u8格式**
44 | **根据坛友反馈MPV播放器播放几个小时都不会出现直播源失效的问题,所以推荐使用MPV播放器**
45 | **[MPV播放器官网](https://mpv.io)**
46 |
47 | **MPV懒人包网址**
48 | **[MPV懒人包](https://github.com/hooke007/MPV_lazy/)**
49 |
50 | **修复YY直播首页直播房间号获取的问题**
51 |
52 |
53 | ### 2022年11月3日
54 | **新增【一直播】直播源获取**
55 |
56 | ### 2022年11月12日
57 | **新增【播放设置】,现在可以选择PotPlayer或者MPVPlayer播放器**
58 |
59 | ### 2022年11月13日
60 | **修复因目录有空格而导致打开目录错误的问题**
61 |
62 | ### 2023年5月10日
63 | **修复斗鱼、B站无法获取直播源的问题**
64 |
65 |
66 | **修复因链接有特殊字符而无法用mpv播放的问题**
67 |
68 | **【严禁mpv目录中有空格及特殊字符,否则将无法播放】**
69 |
70 |
--------------------------------------------------------------------------------
/RealListWindow.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/15 5:41
3 | # @Author: 须尽欢
4 | # @File:RealListWindow.py
5 | # Software:PyCharm
6 | import re
7 | import sys
8 | from PyQt5.QtWidgets import QWidget, QPushButton, QLabel, QHBoxLayout, QVBoxLayout, QLineEdit, QScrollArea, QApplication
9 | from PyQt5.QtCore import Qt, QRect
10 | from PyQt5.Qt import QRegExp, QRegExpValidator
11 | from PyQt5 import QtWidgets, QtCore, QtGui
12 | import pyperclip
13 | import os
14 | import subprocess
15 | import win32api
16 | import requests
17 | import time
18 | from configobj import ConfigObj
19 |
20 |
21 | class ThreadRec(QtCore.QThread):
22 | def __init__(self, live_name, url_link, rid, url_name):
23 | super(ThreadRec, self).__init__()
24 | self.live_name = live_name
25 | self.url_link = url_link
26 | self.rid = rid
27 | self.url_name = url_name
28 |
29 | def run(self):
30 | path_down = '{}/real_save/{}/Downloads'.format(os.getcwd(), self.rid)
31 | if not os.path.exists(path_down):
32 | os.makedirs(path_down)
33 |
34 | if ".m3u8" in self.url_link:
35 | path_down.replace("/", "\\")
36 | live_url = self.url_link
37 | live_url = live_url.replace("&", "^&^")
38 | live_url = live_url.replace("^^", "^")
39 | rec = 'N_m3u8DL-RE_Beta_win-x64\\N_m3u8DL-RE.exe "{}" --tmp-dir "{}" --save-dir "{}" --save-name "{}_{}_{}" --live-real-time-merge --del-after-done'.format(
40 | live_url, path_down, path_down, self.live_name, self.rid,
41 | time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()))
42 | with open("rec.bat", "w", encoding="utf-8") as f:
43 | f.write(rec)
44 | win32api.ShellExecute(0, 'open', 'rec.bat', '', '', 1) # 前台打开
45 |
46 | elif ".flv" in self.url_link or ".xs" in self.url_link:
47 | try:
48 | response = requests.get(self.url_link, stream=True)
49 | f = open("{}/{}_{}_{}.flv".format(path_down, self.live_name, self.url_name,
50 | time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())), "ab")
51 | for chunk in response.iter_content(chunk_size=1024):
52 | if chunk:
53 | f.write(chunk)
54 | f.flush()
55 | else:
56 | f.close()
57 | except:
58 | pass
59 |
60 |
61 | class ButtonTitle(QtWidgets.QPushButton):
62 | def __init__(self, name, border_color, parent=None):
63 | super().__init__(parent)
64 | self.name = name
65 | self.setFixedSize(100, 33)
66 | self.setToolTip("全部保存")
67 | self.setStyleSheet("""QPushButton{
68 | border-radius:0px;
69 | border-top-left-radius:10px;
70 | border-top-right-radius:10px;
71 | font-family:"Microsoft YaHei";
72 | color:#ffffff;
73 | font-size:18px;
74 | font-weight: bold;
75 | background-color: %s; }
76 | }""" % border_color)
77 | self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) # 鼠标指针变成手抓状
78 | self.setText(name)
79 |
80 | def enterEvent(self, event): # 进入
81 | self.setText("保存")
82 | super().enterEvent(event)
83 |
84 | def leaveEvent(self, event): # 离开
85 | self.setText(self.name)
86 | super().leaveEvent(event)
87 |
88 |
89 | class PushButton(QPushButton):
90 | def __init__(self, background_img=None, background_color_rgb=None, background_color_rgba=None, duration=None,
91 | button_bg=None, pushbutton_stylesheet=None, parent=None):
92 | """
93 | :param background_img:图片地址
94 | :param background_color_rgb:红绿蓝颜色
95 | :param background_color_rgba:透明度 0~1
96 | :param duration:持续时间
97 | :param button_bg:默认背景透明度0~1
98 | :param pushbutton_stylesheet:其余样式
99 | :param parent:
100 | """
101 | super().__init__(parent)
102 | self.background_img = background_img
103 | self.background_color_rgb = background_color_rgb
104 | self.background_color_rgba = background_color_rgba
105 | self.button_bg = button_bg
106 | self.button_stylesheet = pushbutton_stylesheet
107 |
108 | self._animation = QtCore.QVariantAnimation( # 创建一个动画
109 | startValue=0,
110 | endValue=100,
111 | valueChanged=self._on_value_changed, # 更改值的函数
112 | duration=duration,
113 | )
114 | self._update_stylesheet(self.button_bg)
115 | self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) # 鼠标指针变成手抓状
116 |
117 | def _on_value_changed(self, a): # 更新 改变后的值
118 | a = a / 100 * self.background_color_rgba + self.button_bg
119 | self._update_stylesheet(a) # 更新并调用样式
120 |
121 | def _update_stylesheet(self, a): # 更新样式函数
122 |
123 | self.setStyleSheet("""QPushButton{background-image: url(%s);
124 | background-color:rgba(%s,%s);
125 | %s}
126 |
127 | """ % (
128 | self.background_img, self.background_color_rgb, a, self.button_stylesheet))
129 |
130 | def enterEvent(self, event): # 进入
131 | self._animation.setDirection(QtCore.QAbstractAnimation.Forward) # 正
132 | self._animation.start()
133 | super().enterEvent(event)
134 |
135 | def leaveEvent(self, event): # 离开
136 | self._animation.setDirection(QtCore.QAbstractAnimation.Backward) # 反
137 | self._animation.start()
138 | super().leaveEvent(event)
139 |
140 |
141 | class RoundShadow(QWidget):
142 | """圆角边框类"""
143 |
144 | def __init__(self, parent=None):
145 | super(RoundShadow, self).__init__(parent)
146 | self.border_width = 10
147 | # 设置 窗口无边框和背景透明 *必须
148 | self.setAttribute(Qt.WA_TranslucentBackground)
149 | self.setWindowFlags(Qt.FramelessWindowHint | Qt.Window)
150 |
151 | def paintEvent(self, event):
152 | # 阴影
153 | path = QtGui.QPainterPath()
154 | path.setFillRule(Qt.WindingFill)
155 |
156 | pat = QtGui.QPainter(self)
157 | pat.setRenderHint(pat.Antialiasing)
158 | pat.fillPath(path, QtGui.QBrush(Qt.white))
159 |
160 | color = QtGui.QColor(192, 192, 192, 50)
161 |
162 | for i in range(10):
163 | i_path = QtGui.QPainterPath()
164 | i_path.setFillRule(Qt.WindingFill)
165 | ref = QtCore.QRectF(10 - i, 10 - i, self.width() - (10 - i) * 2, self.height() - (10 - i) * 2)
166 | # i_path.addRect(ref)
167 | i_path.addRoundedRect(ref, self.border_width, self.border_width)
168 | color.setAlpha(int(150 - i ** 0.5 * 50))
169 | pat.setPen(color)
170 | pat.drawPath(i_path)
171 |
172 | # 圆角
173 | pat2 = QtGui.QPainter(self)
174 | pat2.setRenderHint(pat2.Antialiasing) # 抗锯齿
175 | pat2.setBrush(Qt.white)
176 | pat2.setPen(Qt.transparent)
177 |
178 |
179 | class MoveTop(QWidget):
180 | def __init__(self, parent):
181 | super(MoveTop, self).__init__()
182 | self.win = parent
183 | self.InitializeWindow()
184 | self.setStyleSheet("""
185 | QWidget{
186 | background-color:rgba(255,0,255,0);
187 | border:none;
188 | margin:0px;
189 | }""")
190 |
191 | def InitializeWindow(self):
192 | self.isPressed = False
193 | top = QLabel()
194 | layout_h = QHBoxLayout()
195 | layout_h.addWidget(top)
196 | self.setLayout(layout_h)
197 |
198 | def mousePressEvent(self, event): # 设置鼠标拖动Label
199 | if event.button() == Qt.LeftButton: # 如果鼠标按钮等于左键
200 | self.isPressed = True
201 | self.startPos = event.globalPos()
202 | return QWidget().mousePressEvent(event)
203 |
204 | def mouseReleaseEvent(self, event):
205 | if event.button() == Qt.LeftButton: # 如果鼠标按钮等于左键
206 | self.isPressed = False
207 | return QWidget().mouseReleaseEvent(event)
208 |
209 | def mouseMoveEvent(self, event):
210 | if self.isPressed:
211 | if self.win.isMaximized:
212 | self.win.showNormal()
213 |
214 | movePos = event.globalPos() - self.startPos
215 | self.startPos = event.globalPos()
216 | self.win.move(self.win.pos() + movePos)
217 |
218 | return QWidget().mouseMoveEvent(event)
219 |
220 |
221 | class RealList(RoundShadow, QWidget):
222 | _signal = QtCore.pyqtSignal(str)
223 |
224 | def __init__(self, real_dict, parent=None):
225 | super(RealList, self).__init__(parent)
226 | self.real_dict = real_dict
227 | self.setup_ui()
228 |
229 | def setup_ui(self):
230 | self.setWindowFlags(
231 | QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint) # 隐藏标题栏且可以任务栏最小化
232 | self.setMinimumSize(QtCore.QSize(850, 610)) # 设置透明窗口最小尺寸
233 | self.setWindowTitle("直播源列表")
234 | self.setWindowIcon(QtGui.QIcon("./image/icon.png"))
235 |
236 | self.label_bottom = QtWidgets.QLabel(self) # 创建背景标签
237 | self.label_bottom.setGeometry(5, 60, 840, 540)
238 | self.label_bottom.setStyleSheet("""QLabel{
239 | border:none;
240 | border-bottom:1px solid #E7E7E7;
241 | background-color:rgba(255,255,255,1);
242 | border-bottom-left-radius:5px;
243 | border-bottom-right-radius:5px;
244 | }
245 | """)
246 | self.button_close = PushButton("./image/close_icon_2.png", "0,0,0", 0, 200, 0,
247 | '''border:none;background-repeat:no-repeat;background-position:center;margin-right:0px;border-radius:5px;}QPushButton:hover{background-image:url(./image/close_icon_3.png);''')
248 | self.button_close.setObjectName("pushButton_close")
249 | self.button_close.setMinimumSize(QtCore.QSize(39, 39))
250 | self.button_close.setMaximumSize(QtCore.QSize(39, 39))
251 | self.button_close.setToolTip("关闭")
252 |
253 | self.label_title = QLabel()
254 | self.label_title.setFixedSize(100, 35)
255 | self.label_title.setAlignment(Qt.AlignCenter)
256 | self.label_title.setText("直播源列表")
257 | self.label_title.setStyleSheet("""QLabel{
258 | border:none;
259 | border-radius:16px;
260 | font-family:"Microsoft YaHei";
261 | font-weight:bold;
262 | text-align:center;
263 | color:#474747;
264 | font-size:16px;
265 | background-color:none;;
266 | }
267 | """)
268 |
269 | self.label_top = QLabel(self)
270 | self.label_top.setGeometry(QtCore.QRect(5, 0, 840, 60))
271 | self.label_top.setStyleSheet("""
272 | QLabel{
273 | border-top:1px solid #E9E9E9;
274 | border-bottom:1px solid #E7E7E7;
275 | background-color:rgba(255,255,255,1);
276 | border-top-left-radius:5px;
277 | border-top-right-radius:5px;
278 | }
279 | """)
280 |
281 | self.move_top_left = MoveTop(self)
282 | self.move_top_left.setFixedWidth(310)
283 | self.move_top_left.setObjectName("move_top_title")
284 | self.move_top_right = MoveTop(self)
285 | self.move_top_right.setObjectName("move_top_title")
286 |
287 | self.hbox_top = QHBoxLayout(self.label_top)
288 | self.hbox_top.addWidget(self.move_top_left)
289 | self.hbox_top.addWidget(self.label_title, Qt.AlignHCenter | Qt.AlignVCenter)
290 | self.hbox_top.addWidget(self.move_top_right)
291 | self.hbox_top.addWidget(self.button_close, Qt.AlignRight)
292 | self.hbox_top.setContentsMargins(60, 0, 10, 0) # 内边距
293 | self.hbox_top.setSpacing(0)
294 |
295 | self.label_scroll = QLabel(self.label_bottom)
296 | self.label_scroll.setMinimumSize(840, 500)
297 | self.label_scroll.setStyleSheet("""QLabel{
298 | border-bottom-left-radius:5px;
299 | border-bottom-right-radius:5px;
300 | }""")
301 |
302 | self.scroll = QScrollArea() # 创建一个滚动条
303 | self.scroll.setWidget(self.label_scroll)
304 | self.scroll.setGeometry(QRect(0, 0, 840, 500)) # 滚动条阈值
305 | self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 不显示水平滚动条
306 | self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # 不显示垂直滚动条
307 | self.scroll.setStyleSheet("""
308 | QScrollArea{
309 | border-bottom-left-radius:5px;
310 | border-bottom-right-radius:5px;
311 | background-color:#ffffff;
312 | border-image:url("./image/73114636_bg.png");
313 | padding:0px 0px 5px 0px;
314 | }
315 | """)
316 |
317 | self.add_layout()
318 | self.vbox_widget = QVBoxLayout(self)
319 | self.vbox_widget.addLayout(self.hbox_top)
320 | self.vbox_widget.addWidget(self.scroll)
321 | self.vbox_widget.setContentsMargins(5, 60, 5, 10) # 内边距
322 | self.vbox_widget.setSpacing(0)
323 | self.button_close.clicked.connect(self.closes)
324 |
325 | def label_text(self, text):
326 | self.label_title.setText(text)
327 | self.label_text_animation = QtCore.QVariantAnimation( # 创建一个动画
328 | startValue=0,
329 | endValue=100,
330 | valueChanged=self._update_stylesheet, # 更改值的函数
331 | duration=1500,
332 | )
333 | self.label_text_animation.start()
334 |
335 | def _update_stylesheet(self, a): # 更新样式函数
336 | b = 1 - (100 - (a * 2)) / 100
337 | if b >= 1:
338 | b = (100 - a) / 50
339 | if b <= 0:
340 | self.label_title.setText("直播源列表")
341 | b = 1
342 | self.label_title.setStyleSheet("""QLabel{
343 | border:none;
344 | border-radius:16px;
345 | font-family:"Microsoft YaHei";
346 | font-weight:bold;
347 | text-align:center;
348 | color:rgba(71,71,71, %s);
349 | font-size:16px;
350 | background-color:none;;
351 | }
352 | """ % b)
353 |
354 | def add_layout_title_save_all(self, live_name, url_dict, border_color, rid,
355 | nick_name): # 平台名称,链接字典,border颜色,rid, 用户名称
356 |
357 | def save(url_link, url_name, count):
358 | save_path = "{}/real_save/{}".format(os.getcwd(), rid)
359 | if not os.path.exists("{}/real_save/{}".format(os.getcwd(), rid)):
360 | os.makedirs(
361 | "{}/real_save/{}".format(os.getcwd(), rid))
362 | asx_path = "{}/{}_{}_{}_{}.asx".format(save_path, live_name, url_name, count, nick_name)
363 | if url_link:
364 | asx_str = """
365 |
366 |
367 | {}_{}_{}
368 |
369 |
370 |
371 | """.format(live_name, url_name, nick_name, url_link)
372 | with open(asx_path, "w", encoding="UTF8") as f:
373 | f.write(asx_str)
374 |
375 | def save_all():
376 | for count, real_one in enumerate(url_dict):
377 | for url_name in real_one:
378 | save(real_one[url_name], url_name, count)
379 | self.label_text("全部保存成功")
380 |
381 | button_title_save_all = ButtonTitle(live_name, border_color)
382 | button_title_save_all.clicked.connect(save_all)
383 |
384 | label_rid = QLabel()
385 | label_rid.setText(rid)
386 | label_rid.setFixedHeight(33)
387 | label_rid.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
388 | label_rid.setStyleSheet("""QLabel{
389 | color:%s;
390 | font-size:18px;
391 | font-family:"Microsoft YaHei";
392 | font-weight: bold;
393 | }""" % border_color)
394 |
395 | label_name = QLabel()
396 | label_name.setText(nick_name)
397 | label_name.setFixedWidth(400)
398 | label_name.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
399 | label_name.setStyleSheet("""QLabel{
400 | color:%s;
401 | font-size:18px;
402 | font-family:"Microsoft YaHei";
403 | font-weight: bold;
404 | }""" % border_color)
405 |
406 | hbox_title = QHBoxLayout()
407 | hbox_title.addWidget(button_title_save_all)
408 | hbox_title.addWidget(label_rid)
409 | hbox_title.addStretch()
410 | hbox_title.addWidget(label_name)
411 | hbox_title.setContentsMargins(0, 0, 20, 0) # 内边距
412 | hbox_title.setSpacing(20)
413 |
414 | return hbox_title
415 |
416 | def add_layout_url(self, live_name, url_name, url_link, border_color, count, rid,
417 | nick_name): # 平台名称,链接名称, 链接地址, border颜色,链接计数,用户名称
418 | label_name = QLabel()
419 | label_name.setText(url_name)
420 | label_name.setFixedSize(160, 32)
421 | label_name.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
422 | label_name.setStyleSheet("""QLabel{
423 | border:none;
424 | border-radius:5px;
425 | border-bottom-right-radius:0px;
426 | font-family:"Microsoft YaHei";
427 | color:#ffffff;
428 | font-size:16px;
429 | background-color: %s;
430 | }""" % border_color)
431 | lineedit_url = QLineEdit()
432 | lineedit_url.setFixedSize(600, 32)
433 | lineedit_url.setText(url_link)
434 | lineedit_url.setStyleSheet("""QLineEdit{
435 | padding:0px 150px 0px 10px;
436 | border:none;
437 | font-family:"Microsoft YaHei";
438 | font-size:16px;
439 | border-bottom:1px solid %s;
440 | color:#4C4B50;
441 | text-align:left;
442 | background-color:rgba(255,255,255,0);
443 | }
444 | QLineEdit:hover{
445 | border-bottom:2px solid %s;}
446 | QLineEdit:focus{
447 | border-bottom:2px solid %s;}
448 | """ % (border_color, border_color, border_color))
449 | rx = QRegExp("[\S]*")
450 | validator = QRegExpValidator()
451 | validator.setRegExp(rx)
452 | lineedit_url.setValidator(validator) # 设置屏蔽空格
453 |
454 | def button_qss(image):
455 | button_qss = """QPushButton{
456 | border: 2px solid %s;
457 | border-radius:5px;
458 | background-color:%s;
459 | border-image:url(./image/%s);
460 | }
461 | QPushButton:hover{
462 | border:0px solid %s;
463 | }""" % (border_color, border_color, image, border_color)
464 | return button_qss
465 |
466 | def copy_():
467 | pyperclip.copy(url_link)
468 | self.label_text("复制成功")
469 |
470 | button_copy = QPushButton()
471 | button_copy.setFixedSize(28, 28)
472 | button_copy.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
473 | button_copy.setToolTip('复制')
474 | button_copy.setStyleSheet(button_qss('copy.png'))
475 | button_copy.clicked.connect(copy_)
476 |
477 | button_save = QPushButton()
478 | button_save.setFixedSize(28, 28)
479 | button_save.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
480 | button_save.setToolTip('保存')
481 | button_save.setStyleSheet(button_qss('save.png'))
482 | button_save.clicked.connect(lambda: asx_("save", rid))
483 |
484 | button_play = QPushButton()
485 | button_play.setFixedSize(28, 28)
486 | button_play.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
487 | button_play.setToolTip('播放')
488 | button_play.setStyleSheet(button_qss('play.png'))
489 | button_play.clicked.connect(lambda: player_())
490 |
491 | button_rec = QPushButton()
492 | button_rec.setFixedSize(28, 28)
493 | button_rec.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
494 | button_rec.setToolTip('录制')
495 | button_rec.setStyleSheet(button_qss('rec.png'))
496 | button_rec.clicked.connect(lambda: asx_("rec", rid))
497 |
498 | def player_():
499 | config = ConfigObj("config.ini", encoding='UTF8')
500 | if config['player'] == 'pot':
501 | asx_('play', rid)
502 | elif config['player'] == 'mpv':
503 | url_link_ = '\"%s\"' % url_link
504 | mpv_path = config['mpv']
505 | subprocess.Popen('cmd.exe /C %s %s' % (mpv_path, url_link_), shell=True)
506 |
507 | def asx_(text, rid_):
508 | save_path = "{}/real_save/{}".format(os.getcwd(), rid_)
509 | if not os.path.exists("{}/real_save/{}".format(os.getcwd(), rid_)):
510 | os.makedirs(
511 | "{}/real_save/{}".format(os.getcwd(), rid_))
512 |
513 | asx_path = "{}/{}_{}_{}_{}.asx".format(save_path, live_name, url_name, count, nick_name)
514 |
515 | def save():
516 | if url_link:
517 | asx_str = """
518 |
519 |
520 | {}_{}_{}
521 |
522 |
523 |
524 | """.format(live_name, url_name, nick_name, url_link)
525 |
526 | with open(asx_path, "w", encoding="UTF8") as f:
527 | f.write(asx_str)
528 |
529 | if text == "save":
530 | save()
531 | self.label_text("保存成功")
532 | elif text == "play":
533 | save()
534 | os.startfile(asx_path)
535 | self.label_text("打开文件")
536 | elif text == 'rec':
537 | self.thread_rec = ThreadRec(live_name, url_link.strip(), rid_, url_name)
538 | self.thread_rec.start()
539 | self.label_text("正在录制...")
540 | button_rec.setStyleSheet("""QPushButton{
541 | border:0px solid #20D68D;
542 | border-radius:5px;
543 | background-color:#20D68D;
544 | border-image:url(./image/rec);
545 | }
546 | """)
547 |
548 | hbox_tool = QHBoxLayout(lineedit_url)
549 | hbox_tool.addStretch()
550 | hbox_tool.addWidget(button_copy, Qt.AlignHCenter | Qt.AlignTop)
551 |
552 | hbox_tool.addWidget(button_save)
553 | hbox_tool.addWidget(button_play)
554 | hbox_tool.addWidget(button_rec)
555 | hbox_tool.setContentsMargins(0, 0, 0, 0) # 内边距
556 | hbox_tool.setSpacing(3)
557 |
558 | hbox_url = QHBoxLayout()
559 | hbox_url.addWidget(label_name)
560 | hbox_url.addWidget(lineedit_url)
561 | hbox_url.addStretch()
562 | hbox_url.setContentsMargins(0, 0, 0, 0) # 内边距
563 | hbox_url.setSpacing(0)
564 | return hbox_url
565 |
566 | def add_layout(self):
567 | if self.real_dict:
568 | vbox_real = QVBoxLayout(self.label_scroll)
569 | vbox_real.setSpacing(0)
570 | vbox_real.setContentsMargins(20, 0, 0, 0) # 内边距
571 | real_height = 0
572 | border_color = '#FF7701'
573 | for name in self.real_dict:
574 | if "douyu" in name:
575 | border_color = '#FF7701'
576 | elif "bili" in name:
577 | border_color = '#00AEEC'
578 | elif 'douyin' in name:
579 | border_color = '#FE2B54'
580 | elif 'huya' in name:
581 | border_color = '#FFAB08'
582 | elif 'yy' in name:
583 | border_color = '#FFE600'
584 | elif 'kuwo' in name:
585 | border_color = '#CB83F7'
586 | elif 'kugou' in name:
587 | border_color = '#01D07F'
588 | elif 'yizhibo' in name:
589 | border_color = '#2B2C2E'
590 |
591 | vbox_list = QVBoxLayout()
592 | vbox_list.setSpacing(0)
593 | hbox_title = QHBoxLayout()
594 | real_height += 62
595 | list_height = 0
596 | label_url_background = QLabel()
597 | label_url_background.setStyleSheet("""QLabel{
598 | border:2px solid %s;
599 | border-top-right-radius:5px;
600 | border-bottom-left-radius:5px;
601 | border-bottom-right-radius:5px;
602 | }""" % border_color)
603 |
604 | hbox_title.addLayout(self.add_layout_title_save_all(name, self.real_dict[name], border_color,
605 | self.real_dict[name][-1]['rid'],
606 | self.real_dict[name][-2]['name']))
607 | vbox_url = QVBoxLayout(label_url_background)
608 | for count, url_dict in enumerate(self.real_dict[name]):
609 | real_height += 80
610 | list_height += 80
611 | label_url_background.setFixedSize(800, list_height)
612 | vbox_list.addLayout(hbox_title)
613 | vbox_list.addWidget(label_url_background)
614 | vbox_list.setContentsMargins(0, 30, 0, 0) # 内边距
615 | vbox_list.setSpacing(0)
616 | for url_name in url_dict:
617 | if url_name != 'rid' and url_name != 'name':
618 | vbox_url.addLayout(
619 | self.add_layout_url(name, url_name, url_dict[url_name], border_color, count,
620 | self.real_dict[name][-1]['rid'], self.real_dict[name][-2]['name']))
621 | self.label_scroll.setMinimumHeight(real_height)
622 | vbox_real.addLayout(vbox_list)
623 | vbox_real.addStretch()
624 |
625 | def add_text_(self, text):
626 | self._signal.emit(text)
627 |
628 | def closes(self): # 关闭窗口
629 | self.close()
630 |
631 |
632 | if __name__ == '__main__':
633 | real_dict_ = {'douyu': [{'原画_flv': 'http://ws-tct.douyucdn.cn/live/6rdygfhd.flv?uuid='},
634 | {'原画_m3u8': 'http://ws-tct.douyucdn.cn/live/6rdygfhd.m3u8?uuid='},
635 | {'原画_x_p2p': 'http://ws-tct.douyucdn.cn/live/6rdygfhd.xs?uuid='},
636 | {'高清_flv': 'http://ws-tct.douyucdn.cn/live/6rdygfhd_2000.flv?uuid='},
637 | {'高清_m3u8': 'http://ws-tct.douyucdn.cn/live/6rdygfhd_2000.m3u8?uuid='},
638 | {'高清_x_p2p': 'http://ws-tct.douyucdn.cn/live/6rdygfhd_2000.xs?uuid='}, {'name': '斗鱼官方直播'},
639 | {'rid': '6'}], 'bili': [{
640 | '线路1_10000': 'https://d1--cn-gotcha208.bilivideo.com/live-bvc/779788/live_50329118_9516950_bluray/index.m3u8?expires=1668276677&len=0&oi=1996077206&pt=web&qn=10000&trid=1007920b1c6ee6d349bea1cfd76324335aac&sigparams=cdn,expires,len,oi,pt,qn,trid&cdn=cn-gotcha208&sign=606162f239f4bb86e617278699a75412&sk=c9c6154426932efa80d25af02e87a3bd&p2p_type=1&src=57345&sl=10&free_type=0&pp=rtmp&machinezone=ylf&source=onetier&site=41084a238fdf53253ad6d6924866b8ea&order=1'},
641 | {
642 | '线路2_10000': 'https://cn-hbyc-ct-02-28.bilivideo.com/live-bvc/779788/live_50329118_9516950_bluray/index.m3u8?expires=1668276677&len=0&oi=1996077206&pt=web&qn=10000&trid=1007920b1c6ee6d349bea1cfd76324335aac&sigparams=cdn,expires,len,oi,pt,qn,trid&cdn=cn-gotcha01&sign=c2792b36c2e5151db1bde3b257d5e1a3&sk=c9c6154426932efa80d25af02e87a3bd&flvsk=2935686d6cb9146c7a6a6a0b4e120e2557adc0b48de99725a5887e843ee476a1&p2p_type=1&src=57345&sl=10&free_type=0&sid=cn-hbyc-ct-02-28&chash=0&sche=ban&bvchls=1&score=18&pp=rtmp&machinezone=ylf&source=onetier&site=41084a238fdf53253ad6d6924866b8ea&order=2'},
643 | {
644 | '线路2_2500': 'https://cn-hbyc-ct-02-28.bilivideo.com/live-bvc/779788/live_50329118_9516950_2500/index.m3u8?expires=1668276677&len=0&oi=1996077206&pt=web&qn=2500&trid=1007920b1c6ee6d349bea1cfd76324335aac&sigparams=cdn,expires,len,oi,pt,qn,trid&cdn=cn-gotcha01&sign=c2792b36c2e5151db1bde3b257d5e1a3&sk=c9c6154426932efa80d25af02e87a3bd&flvsk=2935686d6cb9146c7a6a6a0b4e120e2557adc0b48de99725a5887e843ee476a1&p2p_type=1&src=57345&sl=10&free_type=0&sid=cn-hbyc-ct-02-28&chash=0&sche=ban&bvchls=1&score=18&pp=rtmp&machinezone=ylf&source=onetier&site=41084a238fdf53253ad6d6924866b8ea&order=2'},
645 | {
646 | '线路2_1500': 'https://cn-hbyc-ct-02-28.bilivideo.com/live-bvc/779788/live_50329118_9516950_1500/index.m3u8?expires=1668276677&len=0&oi=1996077206&pt=web&qn=1500&trid=1007920b1c6ee6d349bea1cfd76324335aac&sigparams=cdn,expires,len,oi,pt,qn,trid&cdn=cn-gotcha01&sign=c2792b36c2e5151db1bde3b257d5e1a3&sk=c9c6154426932efa80d25af02e87a3bd&flvsk=2935686d6cb9146c7a6a6a0b4e120e2557adc0b48de99725a5887e843ee476a1&p2p_type=1&src=57345&sl=10&free_type=0&sid=cn-hbyc-ct-02-28&chash=0&sche=ban&bvchls=1&score=18&pp=rtmp&machinezone=ylf&source=onetier&site=41084a238fdf53253ad6d6924866b8ea&order=2'},
647 | {'name': '哔哩哔哩英雄联盟赛事'}, {'rid': '6'}]}
648 |
649 | app = QApplication(sys.argv)
650 | favorites = RealList(real_dict_)
651 | favorites.show()
652 | sys.exit(app.exec_())
653 |
--------------------------------------------------------------------------------
/RealRid.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/21 9:35
3 | # @Author: Lanser
4 | # @File:RealRid.py
5 | # Software:PyCharm
6 |
7 | import sys
8 | from PyQt5.QtWidgets import QWidget, QPushButton, QLabel, QHBoxLayout, QVBoxLayout, QLineEdit, QScrollArea, QApplication
9 | from PyQt5.QtCore import Qt, QRect
10 | from PyQt5 import QtWidgets, QtCore, QtGui
11 | from configobj import ConfigObj
12 | import os
13 | import ast
14 | import re
15 | import requests
16 |
17 |
18 | class PushButton(QPushButton):
19 | def __init__(self, background_img=None, background_color_rgb=None, background_color_rgba=None, duration=None,
20 | button_bg=None, pushbutton_stylesheet=None, parent=None):
21 | """
22 | :param background_img:图片地址
23 | :param background_color_rgb:红绿蓝颜色
24 | :param background_color_rgba:透明度 0~1
25 | :param duration:持续时间
26 | :param button_bg:默认背景透明度0~1
27 | :param pushbutton_stylesheet:其余样式
28 | :param parent:
29 | """
30 | super().__init__(parent)
31 | self.background_img = background_img
32 | self.background_color_rgb = background_color_rgb
33 | self.background_color_rgba = background_color_rgba
34 | self.button_bg = button_bg
35 | self.button_stylesheet = pushbutton_stylesheet
36 |
37 | self._animation = QtCore.QVariantAnimation( # 创建一个动画
38 | startValue=0,
39 | endValue=100,
40 | valueChanged=self._on_value_changed, # 更改值的函数
41 | duration=duration,
42 | )
43 | self._update_stylesheet(self.button_bg)
44 | self.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) # 鼠标指针变成手抓状
45 |
46 | def _on_value_changed(self, a): # 更新 改变后的值
47 | a = a / 100 * self.background_color_rgba + self.button_bg
48 | self._update_stylesheet(a) # 更新并调用样式
49 |
50 | def _update_stylesheet(self, a): # 更新样式函数
51 |
52 | self.setStyleSheet("""QPushButton{background-image: url(%s);
53 | background-color:rgba(%s,%s);
54 | %s}
55 |
56 | """ % (
57 | self.background_img, self.background_color_rgb, a, self.button_stylesheet))
58 |
59 | def enterEvent(self, event): # 进入
60 | self._animation.setDirection(QtCore.QAbstractAnimation.Forward) # 正
61 | self._animation.start()
62 | super().enterEvent(event)
63 |
64 | def leaveEvent(self, event): # 离开
65 | self._animation.setDirection(QtCore.QAbstractAnimation.Backward) # 反
66 | self._animation.start()
67 | super().leaveEvent(event)
68 |
69 |
70 | class RoundShadow(QWidget):
71 | """圆角边框类"""
72 |
73 | def __init__(self, parent=None):
74 | super(RoundShadow, self).__init__(parent)
75 | self.border_width = 10
76 | # 设置 窗口无边框和背景透明 *必须
77 | self.setAttribute(Qt.WA_TranslucentBackground)
78 | self.setWindowFlags(Qt.FramelessWindowHint | Qt.Window)
79 |
80 | def paintEvent(self, event):
81 | # 阴影
82 | path = QtGui.QPainterPath()
83 | path.setFillRule(Qt.WindingFill)
84 |
85 | pat = QtGui.QPainter(self)
86 | pat.setRenderHint(pat.Antialiasing)
87 | pat.fillPath(path, QtGui.QBrush(Qt.white))
88 |
89 | color = QtGui.QColor(192, 192, 192, 50)
90 |
91 | for i in range(10):
92 | i_path = QtGui.QPainterPath()
93 | i_path.setFillRule(Qt.WindingFill)
94 | ref = QtCore.QRectF(10 - i, 10 - i, self.width() - (10 - i) * 2, self.height() - (10 - i) * 2)
95 | # i_path.addRect(ref)
96 | i_path.addRoundedRect(ref, self.border_width, self.border_width)
97 | color.setAlpha(int(150 - i ** 0.5 * 50))
98 | pat.setPen(color)
99 | pat.drawPath(i_path)
100 |
101 | # 圆角
102 | pat2 = QtGui.QPainter(self)
103 | pat2.setRenderHint(pat2.Antialiasing) # 抗锯齿
104 | pat2.setBrush(Qt.white)
105 | pat2.setPen(Qt.transparent)
106 |
107 |
108 | class MoveTop(QWidget):
109 | def __init__(self, parent):
110 | super(MoveTop, self).__init__()
111 | self.win = parent
112 | self.InitializeWindow()
113 | self.setStyleSheet("""
114 | QWidget{
115 | background-color:rgba(255,0,255,0);
116 | border:none;
117 | margin:0px;
118 | }""")
119 |
120 | def InitializeWindow(self):
121 | self.isPressed = False
122 | top = QLabel()
123 | layout_h = QHBoxLayout()
124 | layout_h.addWidget(top)
125 | self.setLayout(layout_h)
126 |
127 | def mousePressEvent(self, event): # 设置鼠标拖动Label
128 | if event.button() == Qt.LeftButton: # 如果鼠标按钮等于左键
129 | self.isPressed = True
130 | self.startPos = event.globalPos()
131 | return QWidget().mousePressEvent(event)
132 |
133 | def mouseReleaseEvent(self, event):
134 | if event.button() == Qt.LeftButton: # 如果鼠标按钮等于左键
135 | self.isPressed = False
136 | return QWidget().mouseReleaseEvent(event)
137 |
138 | def mouseMoveEvent(self, event):
139 | if self.isPressed:
140 | if self.win.isMaximized:
141 | self.win.showNormal()
142 |
143 | movePos = event.globalPos() - self.startPos
144 | self.startPos = event.globalPos()
145 | self.win.move(self.win.pos() + movePos)
146 |
147 | return QWidget().mouseMoveEvent(event)
148 |
149 |
150 | class RidList(RoundShadow, QWidget):
151 | _signal = QtCore.pyqtSignal(str)
152 |
153 | def __init__(self, parent=None):
154 | super(RidList, self).__init__(parent)
155 | self.rid_dict = {}
156 | self.config_ini_path = '{}/config.ini'.format(os.getcwd())
157 | self.setup_ui()
158 |
159 | def setup_ui(self):
160 | self.resize(550, 608)
161 | self.setWindowFlags(
162 | QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint) # 隐藏标题栏且可以任务栏最小化
163 | # self.setMinimumSize(QtCore.QSize(550, 600)) # 设置透明窗口最小尺寸
164 | self.setWindowTitle("直播间列表")
165 | self.setWindowIcon(QtGui.QIcon("./image/icon.png"))
166 |
167 | self.label_bottom = QtWidgets.QLabel(self) # 创建背景标签
168 | self.label_bottom.setGeometry(5, 60, 540, 540)
169 | self.label_bottom.setStyleSheet("""QLabel{
170 | border:none;
171 | border-bottom:1px solid #E9E9E9;
172 | background-color:rgba(255,255,255,1);
173 | border-bottom-left-radius:5px;
174 | border-bottom-right-radius:5px;
175 | }
176 | """)
177 | self.button_close = PushButton("./image/close_icon_2.png", "0,0,0", 0, 200, 0,
178 | '''border:none;background-repeat:no-repeat;background-position:center;margin-right:0px;border-radius:5px;}QPushButton:hover{background-image:url(./image/close_icon_3.png);''')
179 | self.button_close.setObjectName("pushButton_close")
180 | self.button_close.setMinimumSize(QtCore.QSize(39, 39))
181 | self.button_close.setMaximumSize(QtCore.QSize(39, 39))
182 | self.button_close.setToolTip("关闭")
183 |
184 | self.label_title = QLabel()
185 | self.label_title.setText("直播间列表")
186 | self.label_title.setFixedSize(100, 35)
187 | self.label_title.setAlignment(Qt.AlignCenter)
188 | self.label_title.setStyleSheet("""QLabel{
189 | border:none;
190 | border-radius:16px;
191 | font-family:"Microsoft YaHei";
192 | font-weight:bold;
193 | text-align:center;
194 | color:#474747;
195 | font-size:16px;
196 | background-color:none;;
197 | }
198 | """)
199 |
200 | self.label_top = QLabel(self)
201 | self.label_top.setGeometry(QtCore.QRect(5, 0, 540, 60))
202 | self.label_top.setStyleSheet("""
203 | QLabel{
204 | border-top:1px solid #E9E9E9;
205 | border-bottom:1px solid #E7E7E7;
206 | background-color:rgba(255,255,255,1);
207 | border-top-left-radius:5px;
208 | border-top-right-radius:5px;
209 | }
210 | """)
211 |
212 | self.move_top_left = MoveTop(self)
213 | self.move_top_left.setFixedWidth(210)
214 | self.move_top_left.setObjectName("move_top_title")
215 | self.move_top_right = MoveTop(self)
216 | self.move_top_right.setObjectName("move_top_title")
217 |
218 | self.hbox_top = QHBoxLayout(self.label_top)
219 | self.hbox_top.addWidget(self.move_top_left)
220 | self.hbox_top.addWidget(self.label_title, Qt.AlignHCenter | Qt.AlignVCenter)
221 | self.hbox_top.addWidget(self.move_top_right)
222 | self.hbox_top.addWidget(self.button_close, Qt.AlignRight)
223 | self.hbox_top.setContentsMargins(10, 0, 10, 0) # 内边距
224 | self.hbox_top.setSpacing(0)
225 |
226 | self.label_scroll = QLabel(self.label_bottom)
227 | self.label_scroll.setMinimumSize(540, 500)
228 | self.label_scroll.setStyleSheet("""QLabel{
229 |
230 | border-bottom-left-radius:5px;
231 | border-bottom-right-radius:5px;
232 | }""")
233 |
234 | self.scroll = QScrollArea()
235 | self.scroll.setWidget(self.label_scroll)
236 | self.scroll.setGeometry(QRect(0, 0, 540, 500)) # 滚动条阈值
237 | self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
238 | self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
239 | self.scroll.setStyleSheet("""
240 | QScrollArea{
241 | border-bottom-left-radius:5px;
242 | border-bottom-right-radius:5px;
243 | background-color:#ffffff;
244 | border-image:url("./image/73114636_bg.png");
245 | padding:0px 0px 5px 0px;
246 | }
247 | """)
248 |
249 | self.vbox_rid = QVBoxLayout(self.label_scroll)
250 | self.vbox_rid.setSpacing(0)
251 | self.vbox_rid.setContentsMargins(0, 0, 0, 0) # 内边距
252 | self.initial_rid()
253 |
254 | self.vbox_widget = QVBoxLayout(self)
255 | self.vbox_widget.addLayout(self.hbox_top)
256 | self.vbox_widget.addWidget(self.scroll)
257 | self.vbox_widget.setContentsMargins(5, 60, 5, 10) # 内边距
258 | self.vbox_widget.setSpacing(0)
259 | self.button_close.clicked.connect(self.closes)
260 |
261 | def label_text(self, text):
262 | self.label_title.setText(text)
263 | self.label_text_animation = QtCore.QVariantAnimation( # 创建一个动画
264 | startValue=0,
265 | endValue=100,
266 | valueChanged=self._update_stylesheet, # 更改值的函数
267 | duration=1500,
268 | )
269 | self.label_text_animation.start()
270 |
271 | def initial_rid(self):
272 | if os.access(self.config_ini_path, os.F_OK):
273 | self.config = ConfigObj(self.config_ini_path, encoding='UTF8')
274 | self.rid_dict = self.config['rid']
275 | real_height = 20
276 | if self.rid_dict:
277 | self.rid_dict = ast.literal_eval(self.config['rid'])
278 | else:
279 | self.rid_dict = {}
280 | for i in range(1000):
281 | if i in self.rid_dict.keys():
282 | self.vbox_rid.addWidget(
283 | self.lineedit_add(i, str(self.rid_dict[i])))
284 | else:
285 | self.vbox_rid.addWidget(self.lineedit_add(i))
286 | real_height += 50
287 | self.label_scroll.setMinimumHeight(real_height)
288 |
289 | def kugou_get_room_id(self, url):
290 | headers = {
291 | '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',
292 | }
293 | res = requests.get(url, headers, timeout=2).text
294 | rid = re.findall('roomId=(\d+)', res)[0]
295 | return rid
296 |
297 | def get_room_id(self, url):
298 | headers = {
299 | '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',
300 | }
301 | url = re.search(r'(https.*)', url).group(1)
302 | response = requests.head(url, headers=headers, timeout=2)
303 | url = response.headers['location']
304 | room_id = re.search(r'\d{19}', url).group(0)
305 |
306 | headers.update({
307 | 'cookie': '_tea_utm_cache_1128={%22utm_source%22:%22copy%22%2C%22utm_medium%22:%22android%22%2C%22utm_campaign%22:%22client_share%22}',
308 | 'host': 'webcast.amemv.com',
309 | })
310 | params = (
311 | ('type_id', '0'),
312 | ('live_id', '1'),
313 | ('room_id', room_id),
314 | ('app_id', '1128'),
315 | ('X-Bogus', '1'),
316 | )
317 |
318 | response = requests.get('https://webcast.amemv.com/webcast/room/reflow/info/?', headers=headers,
319 | params=params, timeout=2).json()
320 | if response:
321 | return response['data']['room']['owner']['web_rid']
322 |
323 | def save_rid(self, index, rid):
324 | if rid:
325 | if 'live.douyin.com' in rid:
326 | rid = re.findall('(\d+)', rid)[0]
327 | elif 'v.douyin.com' in rid:
328 | try:
329 | rid = self.get_room_id(rid)
330 | except:
331 | rid = ""
332 | elif 'live.bilibili.com' in rid:
333 | rid = re.search('\d+', rid).group()
334 | elif 'douyu.com' in rid:
335 | if 'rid=' in rid:
336 | rid = re.findall('rid=(\d+)', rid)[0]
337 | else:
338 | rid = re.search('\d+', rid).group()
339 | elif 'huya.com' in rid:
340 | rid = rid.split("/")[-1]
341 | elif 'yy.com' in rid:
342 | rid = re.findall('/(\d+)/', rid)[0]
343 | elif 'kuwo.cn' in rid:
344 | rid = re.findall('/(\d+)', rid)[0]
345 | elif 'kugou.com' in rid:
346 | if 'channel' in rid:
347 | try:
348 | rid = self.kugou_get_room_id(rid)
349 | except:
350 | rid = ""
351 | else:
352 | rid = re.findall('/(\d+)', rid)[0]
353 | self.rid_dict.update({index: rid})
354 |
355 |
356 | else:
357 | self.rid_dict.pop(index)
358 | self.config['rid'] = str(self.rid_dict)
359 | self.config.write()
360 | return rid
361 |
362 | def lineedit_add(self, index, rid_value=None):
363 | lineedit_text = QLineEdit()
364 | lineedit_text.setFixedHeight(50)
365 | lineedit_text.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
366 | lineedit_text.setStyleSheet("""QLineEdit{
367 | font-family:"Microsoft YaHei";
368 | border-radius:0px;
369 | border-bottom:1px solid #E5E5E5;
370 | font-size:25px;
371 | color:#4C4B50;
372 | background-color:rgba(0,0,0,0);
373 | }
374 | QLineEdit:hover{
375 | border-bottom:1px solid #C1C1C1;
376 | }
377 | QLineEdit:focus{
378 | border-bottom:2px solid #12B7F5
379 | }""")
380 |
381 | def set_text():
382 | try:
383 | rid = self.save_rid(index, lineedit_text.text())
384 | lineedit_text.setText(rid)
385 | except:
386 | lineedit_text.setText("")
387 |
388 | # lineedit_text.textEdited[str].connect(lambda: self.save_rid(index, lineedit_text.text()))
389 | lineedit_text.textEdited[str].connect(set_text)
390 | if rid_value:
391 | lineedit_text.setText(rid_value)
392 | return lineedit_text
393 |
394 | def closes(self):
395 | self.close()
396 |
397 |
398 | if __name__ == '__main__':
399 | app = QApplication(sys.argv)
400 | real_rid = RidList()
401 | real_rid.show()
402 | sys.exit(app.exec_())
403 |
--------------------------------------------------------------------------------
/RealTool.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/9/22 15:40
3 | # @Author: 须尽欢
4 | # @File:RealTool.py
5 | # Software:PyCharm
6 | # Created by: PyQt5 UI code generator 5.14.2
7 | # 直播源项目地址@https://github.com/wbt5/real-url
8 | # m3u8录制工具地址@https://github.com/nilaoda/N_m3u8DL-RE
9 |
10 |
11 | from PyQt5 import QtGui, QtWidgets, QtCore
12 | from PyQt5.QtWidgets import QApplication, QInputDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, \
13 | QGraphicsDropShadowEffect, QPushButton, QLineEdit, QMessageBox
14 | from PyQt5.Qt import QPropertyAnimation, pyqtSignal, QTranslator, QColorDialog, QFileDialog, QRegExp, QRegExpValidator
15 | from PyQt5.QtGui import QIcon
16 | from PyQt5.QtCore import Qt, QEvent, QThread
17 | import sys
18 | import time
19 | import os
20 | import subprocess
21 | from configobj import ConfigObj
22 | import config_ini
23 | import re
24 | import requests
25 | import ast
26 | from multiprocessing.pool import ThreadPool
27 |
28 | from real.douyu import DouYu
29 | from real.huya import HuYa
30 | from real.bili import BiliBili
31 | from real.douyin import DouYin
32 | from real.yy import YY
33 | from real.kuwo import KuWo
34 | from real.kugou import KuGou
35 | from real.yizhibo import YiZhiBo
36 | from RealListWindow import RealList
37 | from RealRid import RidList
38 | import urllib3
39 | from selenium import webdriver
40 | from selenium.webdriver.chrome.options import Options
41 | urllib3.disable_warnings()
42 |
43 | class ThreadGet(QThread):
44 | _signal = pyqtSignal(dict, str)
45 |
46 | def __init__(self, real_list):
47 | super(ThreadGet, self).__init__()
48 | self.real_list = real_list
49 | self.config = ConfigObj("config.ini", encoding='UTF8')
50 |
51 | def run(self):
52 | real_dict = {}
53 | thread_list = []
54 | pool = ThreadPool(processes=10)
55 | for rid in self.real_list:
56 | for real_name_dict in self.config['real']:
57 | try:
58 | if real_name_dict == "douyu" and self.config['real'][real_name_dict] == "1":
59 | real_class = DouYu(rid)
60 | thread_list.append(pool.apply_async(real_class.get_real_url))
61 |
62 | if real_name_dict == "huya" and self.config['real'][real_name_dict] == "1":
63 | real_class = HuYa(rid)
64 | thread_list.append(pool.apply_async(real_class.get_real_url))
65 |
66 | if real_name_dict == "bili" and self.config['real'][real_name_dict] == "1":
67 | real_class = BiliBili(rid)
68 | thread_list.append(pool.apply_async(real_class.get_real_url))
69 |
70 | if real_name_dict == "douyin" and self.config['real'][real_name_dict] == "1":
71 | real_class = DouYin(rid)
72 | thread_list.append(pool.apply_async(real_class.get_real_url))
73 |
74 | if real_name_dict == "yy" and self.config['real'][real_name_dict] == "1":
75 | real_class = YY(rid)
76 | thread_list.append(pool.apply_async(real_class.get_real_url))
77 |
78 | if real_name_dict == "kuwo" and self.config['real'][real_name_dict] == "1":
79 | real_class = KuWo(rid)
80 | thread_list.append(pool.apply_async(real_class.get_real_url))
81 |
82 | if real_name_dict == "kugou" and self.config['real'][real_name_dict] == "1":
83 | real_class = KuGou(rid)
84 | thread_list.append(pool.apply_async(real_class.get_real_url))
85 |
86 | if real_name_dict == "yizhibo" and self.config['real'][real_name_dict] == "1":
87 | real_class = YiZhiBo(rid)
88 | thread_list.append(pool.apply_async(real_class.get_real_url))
89 | except Exception as e:
90 | print("e:%s" % e)
91 | for thread in thread_list:
92 | try:
93 | return_dict = thread.get()
94 | count = 0
95 | if return_dict:
96 | if real_dict:
97 | for i in range(len(real_dict)):
98 | while list(return_dict.keys())[0] == list(real_dict.keys())[i]:
99 | count += 1
100 | if f'{list(return_dict.keys())[0].split("_")[0]}_{count}' != list(real_dict.keys())[i]:
101 | return_dict.update(
102 | {f'{list(return_dict.keys())[0].split("_")[0]}_{count}': return_dict.pop(
103 | list(return_dict.keys())[0])})
104 | break
105 |
106 | real_dict.update(return_dict)
107 | except Exception as err:
108 | print("err:%s" % err)
109 | if real_dict:
110 | print(real_dict)
111 | self._signal.emit(real_dict, 'true')
112 | else:
113 | self._signal.emit(real_dict, 'false')
114 | pool.close()
115 |
116 |
117 | class ButtonGet(QtWidgets.QPushButton):
118 | def __init__(self, parent=None):
119 | super().__init__(parent)
120 |
121 | self._animation = QtCore.QVariantAnimation( # 创建一个动画
122 | startValue=0,
123 | endValue=100,
124 | valueChanged=self._on_value_changed, # 更改值的函数
125 | duration=400,
126 | )
127 | self._update_stylesheet(hover_g_start=200, hover_g_stop=180)
128 |
129 | def _on_value_changed(self, a): # 更新 改变后的值
130 | hover_g_start = int(200 + (a / 5))
131 | hover_g_stop = int(180 + (a / 5))
132 | self._update_stylesheet(hover_g_start=hover_g_start, hover_g_stop=hover_g_stop) # 更新并调用样式
133 |
134 | def _update_stylesheet(self, hover_g_start=None, hover_g_stop=None): # 更新样式函数
135 | self.setStyleSheet("""
136 | QPushButton{
137 | border-solid: 3px;
138 | color:#ffffff;
139 | background-color:qlineargradient(
140 | spread:pad, x1:0, y1:0, x2:0, y2:1,
141 | stop:0 rgba(11,%s,255, 1),
142 | stop:1 rgba(0,%s,255, 1));
143 | }
144 | QPushButton:pressed{
145 | background-color:qlineargradient(
146 | spread:pad, x1:0, y1:0, x2:0, y2:1,
147 | stop:0 rgba(11,190,255, 1),
148 | stop:1 rgba(0,185,255, 1));
149 |
150 | }""" % (hover_g_start, hover_g_stop))
151 |
152 | def enterEvent(self, event): # 进入
153 | self._animation.setDirection(QtCore.QAbstractAnimation.Forward) # 正
154 | self._animation.start()
155 | super().enterEvent(event)
156 |
157 | def leaveEvent(self, event): # 离开
158 | self._animation.setDirection(QtCore.QAbstractAnimation.Backward) # 反
159 | self._animation.start()
160 | super().leaveEvent(event)
161 |
162 |
163 | class PushButton(QtWidgets.QPushButton):
164 | def __init__(self, background_img=None, background_color_rgb=None, background_color_rgba=None, duration=None,
165 | button_bg=None, pushbutton_stylesheet=None, parent=None):
166 | """
167 | :param background_img:图片地址
168 | :param background_color_rgb:红绿蓝颜色
169 | :param background_color_rgba:透明度 0~1
170 | :param duration:持续时间
171 | :param button_bg:默认背景透明度0~1
172 | :param pushbutton_stylesheet:其余样式
173 | :param parent:
174 | """
175 | super().__init__(parent)
176 | self.background_img = background_img
177 | self.background_color_rgb = background_color_rgb
178 | self.background_color_rgba = background_color_rgba
179 | self.button_bg = button_bg
180 | self.pushbutton_stylesheet = pushbutton_stylesheet
181 |
182 | self._animation = QtCore.QVariantAnimation( # 创建一个动画
183 | startValue=0,
184 | endValue=100,
185 | valueChanged=self._on_value_changed, # 更改值的函数
186 | duration=duration,
187 | )
188 | self._update_stylesheet(self.button_bg)
189 |
190 | def _on_value_changed(self, a): # 更新 改变后的值
191 | a = a / 100 * self.background_color_rgba + self.button_bg
192 | self._update_stylesheet(a) # 更新并调用样式
193 |
194 | def _update_stylesheet(self, a): # 更新样式函数
195 | self.setStyleSheet("""QPushButton{
196 | background-image: url(%s);
197 | background-color:rgba(%s,%s);
198 | %s}
199 | """ % (self.background_img, self.background_color_rgb, a, self.pushbutton_stylesheet))
200 |
201 | def enterEvent(self, event): # 进入
202 | self._animation.setDirection(QtCore.QAbstractAnimation.Forward) # 正
203 | self._animation.start()
204 | super().enterEvent(event)
205 |
206 | def leaveEvent(self, event): # 离开
207 | self._animation.setDirection(QtCore.QAbstractAnimation.Backward) # 反
208 | self._animation.start()
209 | super().leaveEvent(event)
210 |
211 |
212 | class ToolButton(QtWidgets.QToolButton):
213 | def __init__(self, background_img=None, background_color_rgb=None, background_color_rgba=None, duration=None,
214 | button_bg=None, pushbutton_stylesheet=None, parent=None):
215 | """
216 | :param background_img:图片地址
217 | :param background_color_rgb:红绿蓝颜色
218 | :param background_color_rgba:透明度 0~1
219 | :param duration:持续时间
220 | :param button_bg:默认背景透明度0~1
221 | :param pushbutton_stylesheet:其余样式
222 | :param parent:
223 | """
224 | super().__init__(parent)
225 | self.background_img = background_img
226 | self.background_color_rgb = background_color_rgb
227 | self.background_color_rgba = background_color_rgba
228 | self.button_bg = button_bg
229 | self.pushbutton_stylesheet = pushbutton_stylesheet
230 |
231 | self._animation = QtCore.QVariantAnimation( # 创建一个动画
232 | startValue=0,
233 | endValue=100,
234 | valueChanged=self._on_value_changed, # 更改值的函数
235 | duration=duration,
236 | )
237 | self._update_stylesheet(self.button_bg)
238 |
239 | def _on_value_changed(self, a): # 更新 改变后的值
240 | a = a / 100 * self.background_color_rgba + self.button_bg
241 | self._update_stylesheet(a) # 更新并调用样式
242 |
243 | def _update_stylesheet(self, a): # 更新样式函数
244 | self.setStyleSheet("""QToolButton{
245 | background-image: url(%s);
246 | background-color:rgba(%s,%s);
247 | }
248 | QToolButton::menu-indicator{
249 | image:none;
250 |
251 | %s}
252 | """ % (self.background_img, self.background_color_rgb, a, self.pushbutton_stylesheet))
253 |
254 | def enterEvent(self, event): # 进入
255 | self._animation.setDirection(QtCore.QAbstractAnimation.Forward) # 正
256 | self._animation.start()
257 | super().enterEvent(event)
258 |
259 | def leaveEvent(self, event): # 离开
260 | self._animation.setDirection(QtCore.QAbstractAnimation.Backward) # 反
261 | self._animation.start()
262 | super().leaveEvent(event)
263 |
264 |
265 | class MainUi(QWidget):
266 | def __init__(self):
267 | super(MainUi, self).__init__()
268 | self.setup_ui()
269 | self.real_dict = {}
270 | self.real_id = ''
271 | self.add_shadow() # 阴影边框
272 |
273 | def setup_ui(self):
274 |
275 | self.config_ini()
276 | self.setWindowFlags(
277 | QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint) # 隐藏标题栏且可以任务栏最小化
278 | self.setFixedSize(self.width_, self.height_)
279 | self.setStyleSheet("""QWidget{
280 | border-radius:3px;
281 | border:none;
282 | }""")
283 | self.setWindowIcon(QIcon("./image/icon.png"))
284 | self.setWindowTitle("直播源获取")
285 | self.setAttribute(QtCore.Qt.WA_TranslucentBackground) # 设置窗口背景透明
286 | self.label_background = QLabel(self) # 创建背景标签
287 | self.label_background.setGeometry(QtCore.QRect(5, 0, self.width_ - 10, self.height_ - 10))
288 | self.label_background.setStyleSheet("""QLabel{
289 | border:none;
290 | background-color:rgba(135,249,255, 1);
291 | }""")
292 |
293 | self.label_top_background = QLabel()
294 | self.label_top_background.setFixedSize(self.width_ - 10, int(self.height_ / 2.7))
295 | self.label_top_background.setStyleSheet("""QLabel{
296 | border:none;
297 | border-bottom-left-radius:0px;
298 | border-bottom-right-radius:0px;
299 | }""")
300 |
301 | self.label_top_background_image = QLabel(self.label_top_background)
302 | self.label_top_background_image.setFixedSize(self.width_ - 10, int(self.height_ / 2.7))
303 | self.label_top_background_image.setStyleSheet("""QLabel{
304 | border:none;
305 | border-bottom-left-radius:0px;
306 | border-bottom-right-radius:0px;
307 | border-image:url('%s');
308 | }""" % self.config[
309 | 'background'])
310 | opacity_effect = QtWidgets.QGraphicsOpacityEffect()
311 | opacity_effect.setOpacity(0.85)
312 | self.label_top_background_image.setGraphicsEffect(opacity_effect)
313 | self.label_top_background_image.setAutoFillBackground(True)
314 |
315 | self.label_bottom_background = QLabel()
316 | self.label_bottom_background.setFixedSize(self.label_top_background.width(),
317 | self.height_ - self.label_top_background.height() - 10)
318 | self.label_bottom_background.setStyleSheet("""QLabel{
319 | background-color:#ffffff;
320 | border-top-left-radius:0px;
321 | border-top-right-radius:0px;
322 | }""")
323 |
324 | self.label_icon = QLabel(self.label_top_background)
325 | self.label_icon.setFixedSize(self.label_top_background.width(), 32)
326 | self.label_icon.setStyleSheet("""QLabel{
327 | border:none;
328 | border-image:none;
329 | background-color:rgba(255, 255, 255, 0);
330 | }""")
331 |
332 | self.layout_icon = QtWidgets.QHBoxLayout(self.label_icon)
333 |
334 | self.pushbutton_setting = ToolButton(background_img="./image/setting_icon.png",
335 | background_color_rgb="255,255,255", background_color_rgba=0.2,
336 | duration=200, button_bg=0,
337 | pushbutton_stylesheet="""border:none;background-repeat:no-repeat;width:35px;background-position:center;}QToolButton:pressed{background-color:rgba(255,255,255,0.4);}""") # 设置按钮样式
338 | self.pushbutton_setting.setFixedSize(32, 32)
339 | self.pushbutton_setting.setToolTip("设置")
340 |
341 | self.pushbutton_colse = PushButton(background_img="./image/close_icon.png", background_color_rgb="255,84,57",
342 | background_color_rgba=1, duration=200, button_bg=0,
343 | pushbutton_stylesheet="""border:none;background-repeat:no-repeat;width:32px;background-position:center;}QPushButton:pressed{background-color:rgba(224,74,50,1);}""") # 关闭按钮样式
344 | self.pushbutton_colse.setFixedSize(32, 32)
345 | self.pushbutton_colse.clicked.connect(self.close_)
346 | self.pushbutton_colse.setToolTip('关闭 Ese')
347 |
348 | self.pushbutton_min = PushButton(background_img="./image/min_icon.png", background_color_rgb="255,255,255",
349 | background_color_rgba=0.2, duration=200, button_bg=0,
350 | pushbutton_stylesheet="""border:none;background-repeat:no-repeat;width:35px;background-position:center;}QPushButton:pressed{background-color:rgba(255,255,255,0.4);}""") # 最小化按钮样式
351 | self.pushbutton_min.setFixedSize(32, 32)
352 | self.pushbutton_min.clicked.connect(self.min_)
353 | self.pushbutton_min.setToolTip('最小化')
354 |
355 | self.pushbutton_size = PushButton(background_img="./image/size_icon.png", background_color_rgb="255,255,255",
356 | background_color_rgba=0.2, duration=200, button_bg=0,
357 | pushbutton_stylesheet="""border:none;background-repeat:no-repeat;width:35px;background-position:center;}QPushButton:pressed{background-color:rgba(255,255,255,0.4);}""") # 切换窗口按钮样式
358 | self.pushbutton_size.setFixedSize(32, 32)
359 | self.pushbutton_size.clicked.connect(self.size_)
360 | self.pushbutton_size.setToolTip('切换窗口大小')
361 |
362 | self.lineedit_input = QLineEdit()
363 | self.lineedit_input.setFixedSize(int(self.width_ - 194), 80)
364 | self.lineedit_input.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignBottom)
365 | self.lineedit_input.setStyleSheet("""
366 | QLineEdit{
367 | color:%s;
368 | font-family:"Microsoft YaHei";
369 | font-size:%spx;
370 | border-radius:0px;
371 | border-bottom:1px solid #E5E5E5;
372 | }
373 | QLineEdit:hover{
374 | border-bottom:1px solid #C1C1C1;
375 | }
376 | QLineEdit:focus{
377 | border-bottom:1px solid #12B7F5
378 | }""" % (
379 | self.config['font']['color'], self.config['font']['size']))
380 | rx = QRegExp("[\S]*")
381 | validator = QRegExpValidator(self)
382 | validator.setRegExp(rx)
383 | self.lineedit_input.setValidator(validator) # 设置屏蔽空格
384 |
385 | self.pushbutton_get = ButtonGet()
386 | self.pushbutton_get.setFixedSize(int(self.width_ - 194), 38)
387 | self.pushbutton_get.setText("获取")
388 | button_font = QtGui.QFont()
389 | button_font.setFamily("微软雅黑")
390 | button_font.setPointSize(11)
391 | self.pushbutton_get.setFont(button_font)
392 |
393 | self.hbox_lineedit = QHBoxLayout()
394 | self.hbox_lineedit.addStretch()
395 | self.hbox_lineedit.addWidget(self.lineedit_input)
396 | self.hbox_lineedit.addStretch()
397 |
398 | self.hbox_button = QHBoxLayout()
399 | self.hbox_button.addStretch()
400 | self.hbox_button.addWidget(self.pushbutton_get)
401 | self.hbox_button.addStretch()
402 |
403 | self.vbox_button = QVBoxLayout(self.label_bottom_background)
404 | self.vbox_button.addStretch()
405 | self.vbox_button.addLayout(self.hbox_lineedit)
406 | self.vbox_button.addLayout(self.hbox_button)
407 | self.vbox_button.addStretch()
408 | self.vbox_button.setSpacing(10)
409 | self.vbox_button.setContentsMargins(0, 0, 0, 0)
410 |
411 | self.layout_icon.addStretch()
412 | self.layout_icon.addWidget(self.pushbutton_setting, 0, Qt.AlignRight)
413 | self.layout_icon.addWidget(self.pushbutton_size, 0, Qt.AlignRight)
414 | self.layout_icon.addWidget(self.pushbutton_min, 0, Qt.AlignRight)
415 | self.layout_icon.addWidget(self.pushbutton_colse, 0, Qt.AlignRight)
416 |
417 | self.layout_icon.setSpacing(0) # 外间距
418 | self.layout_icon.setContentsMargins(0, 0, 0, 0) # 内间距
419 |
420 | self.vbox_background = QVBoxLayout(self.label_background)
421 | self.vbox_background.addWidget(self.label_top_background)
422 | self.vbox_background.addWidget(self.label_bottom_background)
423 | self.vbox_background.setSpacing(0)
424 | self.vbox_background.setContentsMargins(0, 0, 0, 0)
425 |
426 | self.pushbutton_logo = QPushButton(self.label_background)
427 | self.pushbutton_logo.setToolTip("")
428 | self.pushbutton_logo.setGeometry(int(self.width_ / 2 - 47 - 5), int(self.height_ / 2.7 - 47), 94, 94)
429 | self.pushbutton_logo.setStyleSheet("""QPushButton{
430 | border-image: url('./image/logo.png');
431 | }""")
432 |
433 | self.menu_tool = QtWidgets.QMenu(self.pushbutton_setting)
434 |
435 | self.menu_dir = QtWidgets.QMenu("打开目录", self.menu_tool)
436 | self.open_dir = QtWidgets.QAction("软件目录", self.menu_dir)
437 | self.open_path = QtWidgets.QAction("下载目录", self.menu_dir)
438 | self.menu_dir.addAction(self.open_dir)
439 | self.menu_dir.addAction(self.open_path)
440 |
441 | self.menu_real = QtWidgets.QMenu("直播平台", self.menu_tool)
442 | self.menu_real.mouseReleaseEvent = self.mouse_release_event_real_
443 | self.real_douyu = QtWidgets.QAction("斗鱼直播", self.menu_real)
444 | self.real_huya = QtWidgets.QAction("虎牙直播", self.menu_real)
445 | self.real_bili = QtWidgets.QAction("哔哩直播", self.menu_real)
446 | self.real_douyin = QtWidgets.QAction("抖音直播", self.menu_real)
447 | self.real_yy = QtWidgets.QAction("歪歪直播", self.menu_real)
448 | self.real_kuwo = QtWidgets.QAction("酷我直播", self.menu_real)
449 | self.real_kugou = QtWidgets.QAction("酷狗直播", self.menu_real)
450 | self.real_yizhibo = QtWidgets.QAction("一直播", self.menu_real)
451 |
452 | self.menu_real.addAction(self.real_douyu)
453 | self.menu_real.addAction(self.real_huya)
454 | self.menu_real.addAction(self.real_bili)
455 | self.menu_real.addAction(self.real_douyin)
456 | self.menu_real.addAction(self.real_yy)
457 | self.menu_real.addAction(self.real_kuwo)
458 | self.menu_real.addAction(self.real_kugou)
459 | self.menu_real.addAction(self.real_yizhibo)
460 | self.real_douyu.setCheckable(True)
461 | self.real_huya.setCheckable(True)
462 | self.real_bili.setCheckable(True)
463 | self.real_douyin.setCheckable(True)
464 | self.real_yy.setCheckable(True)
465 | self.real_kuwo.setCheckable(True)
466 | self.real_kugou.setCheckable(True)
467 | self.real_yizhibo.setCheckable(True)
468 |
469 | self.menu_about = QtWidgets.QMenu("关于软件", self.menu_tool)
470 | self.about_down = QtWidgets.QAction("下载地址", self.menu_about)
471 | self.about_project = QtWidgets.QAction("开源项目", self.menu_about)
472 | self.about_explain = QtWidgets.QAction("软件说明", self.menu_about)
473 | self.menu_about.addAction(self.about_down)
474 | self.menu_about.addAction(self.about_project)
475 | self.menu_about.addAction(self.about_explain)
476 | self.about_down.triggered.connect(self.url_down_)
477 | self.about_project.triggered.connect(self.url_project_)
478 | self.about_explain.triggered.connect(self.explain_)
479 |
480 | self.menu_setting = QtWidgets.QMenu("其它设置", self.menu_tool)
481 | self.menu_play = QtWidgets.QMenu("播放设置", self.menu_setting)
482 | self.menu_play.mouseReleaseEvent = self.mouse_play_event_real_
483 | self.play_pot = QtWidgets.QAction("PotPlayer", self.menu_play)
484 | self.play_mpv = QtWidgets.QAction("MpvPlayer", self.menu_play)
485 | self.play_pot.setCheckable(True)
486 | self.play_mpv.setCheckable(True)
487 |
488 | self.menu_play.addAction(self.play_pot)
489 | self.menu_play.addAction(self.play_mpv)
490 |
491 | self.menu_setting.addMenu(self.menu_play)
492 | self.setting_rid = QtWidgets.QAction("批量获取", self.menu_setting)
493 | self.setting_background = QtWidgets.QAction("背景设置", self.menu_setting)
494 | self.setting_font_size = QtWidgets.QAction("字体大小", self.menu_setting)
495 | self.setting_font_color = QtWidgets.QAction("字体颜色", self.menu_setting)
496 | self.setting_reset = QtWidgets.QAction("重置设置", self.menu_setting)
497 | self.menu_setting.addAction(self.setting_rid)
498 | self.menu_setting.addAction(self.setting_background)
499 | self.menu_setting.addAction(self.setting_font_size)
500 | self.menu_setting.addAction(self.setting_font_color)
501 | self.menu_setting.addAction(self.setting_reset)
502 |
503 | self.menu_tool.addMenu(self.menu_dir)
504 | self.menu_tool.addMenu(self.menu_real)
505 | self.menu_tool.addMenu(self.menu_setting)
506 | self.menu_tool.addMenu(self.menu_about)
507 |
508 | self.pushbutton_setting.setMenu(self.menu_tool)
509 | self.pushbutton_setting.setPopupMode(QtWidgets.QToolButton.InstantPopup)
510 |
511 | self.initial_live()
512 | self.open_dir.triggered.connect(self.open_dir_)
513 | self.open_path.triggered.connect(self.open_path_)
514 | self.setting_reset.triggered.connect(self.reset_)
515 | self.setting_font_color.triggered.connect(self.font_color_)
516 | self.setting_rid.triggered.connect(self.real_rid_)
517 | self.setting_font_size.triggered.connect(self.font_size_)
518 | self.setting_background.triggered.connect(self.background_image_)
519 | self.pushbutton_get.clicked.connect(self.thread_get)
520 | self.real_douyu.triggered.connect(self.real_live_)
521 | self.real_huya.triggered.connect(self.real_live_)
522 | self.real_bili.triggered.connect(self.real_live_)
523 | self.real_douyin.triggered.connect(self.real_live_)
524 | self.real_yy.triggered.connect(self.real_live_)
525 | self.real_kuwo.triggered.connect(self.real_live_)
526 | self.real_kugou.triggered.connect(self.real_live_)
527 | self.real_yizhibo.triggered.connect(self.real_live_)
528 | self.pushbutton_logo.clicked.connect(lambda: self.real_window())
529 | self.play_pot.triggered.connect(lambda: self.player_('pot'))
530 | self.play_mpv.triggered.connect(lambda: self.player_('mpv'))
531 |
532 | def initial_live(self):
533 | self.config = ConfigObj("config.ini", encoding="UTF8")
534 | for real_ in self.config['real']:
535 | if real_ == 'douyu':
536 | if self.config['real'][real_] == "1":
537 | self.real_douyu.setChecked(True)
538 | if real_ == 'huya':
539 | if self.config['real'][real_] == "1":
540 | self.real_huya.setChecked(True)
541 | if real_ == 'bili':
542 | if self.config['real'][real_] == "1":
543 | self.real_bili.setChecked(True)
544 | if real_ == 'douyin':
545 | if self.config['real'][real_] == "1":
546 | self.real_douyin.setChecked(True)
547 | if real_ == 'yy':
548 | if self.config['real'][real_] == "1":
549 | self.real_yy.setChecked(True)
550 | if real_ == 'kuwo':
551 | if self.config['real'][real_] == "1":
552 | self.real_kuwo.setChecked(True)
553 | if real_ == 'kugou':
554 | if self.config['real'][real_] == "1":
555 | self.real_kugou.setChecked(True)
556 | if real_ == 'yizhibo':
557 | if self.config['real'][real_] == "1":
558 | self.real_yizhibo.setChecked(True)
559 | if self.config['player'] == 'pot':
560 | self.play_pot.setChecked(True)
561 | elif self.config['player'] == 'mpv':
562 | self.play_mpv.setChecked(True)
563 |
564 | def player_(self, name):
565 | self.play_pot.setChecked(False)
566 | self.play_mpv.setChecked(False)
567 | self.config = ConfigObj("config.ini", encoding="UTF8")
568 | if name == 'pot':
569 | self.play_pot.setChecked(True)
570 | self.config['player'] = 'pot'
571 | elif name == 'mpv':
572 | file_, filetype = QFileDialog.getOpenFileName(self,
573 | "选取文件",
574 | os.getcwd(),
575 | "All Files(mpv*.exe)")
576 | if file_:
577 | self.config['mpv'] = file_
578 | self.play_mpv.setChecked(True)
579 | self.config['player'] = 'mpv'
580 | else:
581 | self.play_pot.setChecked(True)
582 | self.config['player'] = 'pot'
583 | self.config.write()
584 |
585 | def real_rid_(self):
586 | self.real_rid_list = RidList()
587 | self.real_rid_list.setWindowModality(Qt.ApplicationModal) # 禁止非当前窗口操作
588 | self.real_rid_list.show()
589 |
590 | def url_project_(self):
591 | url = 'https://github.com/s282730788/RealTool'
592 | QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))
593 |
594 | def explain_(self):
595 |
596 | QMessageBox.information(self,
597 | "关于RealTool",
598 | "本软件由吾爱岚瑟首发,现已开源\n仅作为个人学习使用,不得用于任何商业用途!",
599 | QMessageBox.Ok)
600 |
601 | def url_down_(self):
602 | url = 'https://wwjd.lanzout.com/b00w74arg'
603 | QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))
604 | self.lineedit_input.setText("密码1234")
605 |
606 | def real_window(self):
607 | if self.real_dict:
608 | self.real_list = RealList(self.real_dict)
609 | self.real_list.show()
610 |
611 | def douyin_get_room_id(self, url):
612 | try:
613 | print(url)
614 | chrome_options = Options()
615 | chrome_options.add_argument('--headless') # 无头模式
616 | driver = webdriver.Chrome(options=chrome_options)
617 | driver.implicitly_wait(10)
618 | driver.get(url)
619 | err = 0
620 | while True:
621 | if 'live.douyin' in driver.current_url:
622 | room_id = re.findall("\d+",driver.current_url)[0]
623 | print(room_id)
624 | return room_id
625 | time.sleep(1)
626 | err += 1
627 | if err >= 10:
628 | break
629 | except Exception as err:
630 | print(err)
631 | # headers = {
632 | # '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',
633 | # }
634 | # url = re.search(r'(https.*)', url).group(1)
635 | # print(f"111:{url}")
636 | #
637 | # response = requests.head(url, headers=headers, timeout=2, verify=False)
638 | # print(response.status_code)
639 | #
640 | # url = response.headers['location']
641 | # print(f"222:{url}")
642 | # room_id = re.search(r'\d{19}', url).group(0)
643 | # print(f"333:{room_id}")
644 | # headers.update({
645 | # 'cookie': '_tea_utm_cache_1128={%22utm_source%22:%22copy%22%2C%22utm_medium%22:%22android%22%2C%22utm_campaign%22:%22client_share%22}',
646 | # 'host': 'webcast.amemv.com',
647 | # })
648 | # params = (
649 | # ('type_id', '0'),
650 | # ('live_id', '1'),
651 | # ('room_id', room_id),
652 | # ('app_id', '1128'),
653 | # ('X-Bogus', '1'),
654 | # )
655 | #
656 | # response = requests.get('https://webcast.amemv.com/webcast/room/reflow/info/?', headers=headers,
657 | # params=params, timeout=2)
658 | # print(response.text)
659 | # print(f"444:{response.json()}")
660 | # if response.json():
661 | # return response.json()['data']['room']['owner']['web_rid']
662 |
663 | def kugou_get_room_id(self, url):
664 | headers = {
665 | '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',
666 | }
667 | res = requests.get(url, headers, timeout=2).text
668 | rid = re.findall('roomId=(\d+)', res)[0]
669 | return rid
670 |
671 | def thread_get(self):
672 | self.config = ConfigObj("config.ini", encoding="UTF8")
673 | rid = self.lineedit_input.text()
674 | rid_list = [] # 存放RID列表
675 | rid_list_del = [] # 去重
676 | if rid:
677 | if 'live.douyin.com' in rid:
678 | try:
679 | rid = re.findall('(\d+)', rid)[0]
680 | except:
681 | pass
682 | elif 'v.douyin.com' in rid:
683 | try:
684 | rid = self.douyin_get_room_id(rid)
685 | except Exception as err:
686 | print(f"douyin_err:{err}")
687 | rid = ""
688 | elif 'live.bilibili.com' in rid:
689 | rid = re.search('\d+', rid).group()
690 | elif 'douyu.com' in rid:
691 | if 'rid=' in rid:
692 | rid = re.findall('rid=(\d+)', rid)[0]
693 | else:
694 | rid = re.search('\d+', rid).group()
695 | elif 'huya.com' in rid:
696 | rid = rid.split("/")[-1]
697 | elif 'yy.com' in rid:
698 | rid = re.findall('/(\d+)', rid)[0]
699 | elif 'kuwo.cn' in rid:
700 | rid = re.findall('/(\d+)', rid)[0]
701 | elif 'kugou.com' in rid:
702 | if 'channel' in rid:
703 | try:
704 | rid = self.kugou_get_room_id(rid)
705 | except:
706 | rid = ""
707 | else:
708 | rid = re.findall('/(\d+)', rid)[0]
709 | elif 'yizhibo' in rid:
710 | rid = re.findall('yizhibo.com.*/(.*?).html', rid)[0]
711 | print(rid)
712 | rid_list.append(rid)
713 | self.lineedit_input.setText(rid)
714 | self.thread = ThreadGet(rid_list)
715 | self.thread._signal.connect(self.text)
716 | self.thread.start()
717 | self.pushbutton_logo.setStyleSheet("""QPushButton{
718 | border-image: url('./image/logo_get.png');
719 | }""")
720 | else:
721 | rid_dict = self.config['rid']
722 | if rid_dict:
723 | rid_dict = ast.literal_eval(self.config['rid'])
724 | for i in rid_dict:
725 | rid_list.append(rid_dict[i])
726 | for j in rid_list:
727 | if j not in rid_list_del:
728 | rid_list_del.append(j)
729 | rid_list = rid_list_del
730 | self.thread = ThreadGet(rid_list)
731 | self.thread._signal.connect(self.text)
732 | self.thread.start()
733 | self.pushbutton_logo.setStyleSheet("""QPushButton{
734 | border-image: url('./image/logo_get.png');
735 | }""")
736 |
737 | def text(self, real_dict, text):
738 | if text == 'true':
739 | self.real_dict = real_dict
740 | self.real_window()
741 | elif text == 'false':
742 | self.lineedit_input.setText("直播源获取失败")
743 | self.pushbutton_logo.setStyleSheet("""QPushButton{
744 | border-image: url('./image/logo.png');
745 | }""")
746 | QApplication.processEvents()
747 |
748 | def real_live_(self):
749 | self.config = ConfigObj("config.ini", encoding="UTF8")
750 | self.config['real']['douyu'] = "0"
751 | self.config['real']['huya'] = "0"
752 | self.config['real']['bili'] = "0"
753 | self.config['real']['douyin'] = "0"
754 | self.config['real']['yy'] = "0"
755 | self.config['real']['kuwo'] = "0"
756 | self.config['real']['kugou'] = "0"
757 | if self.real_douyu.isChecked():
758 | self.config['real']['douyu'] = "1"
759 | if self.real_huya.isChecked():
760 | self.config['real']['huya'] = "1"
761 | if self.real_bili.isChecked():
762 | self.config['real']['bili'] = "1"
763 | if self.real_douyin.isChecked():
764 | self.config['real']['douyin'] = "1"
765 | if self.real_yy.isChecked():
766 | self.config['real']['yy'] = "1"
767 | if self.real_kuwo.isChecked():
768 | self.config['real']['kuwo'] = "1"
769 | if self.real_kugou.isChecked():
770 | self.config['real']['kugou'] = "1"
771 | if self.real_yizhibo.isChecked():
772 | self.config['real']['yizhibo'] = "1"
773 |
774 | self.config.write()
775 |
776 | def background_image_(self):
777 | getcwd_file = '{}/image/'.format(os.getcwd())
778 | background_image, filetype = QFileDialog.getOpenFileName(self,
779 | "选取文件",
780 | getcwd_file,
781 | "img (*jpg;*png;*bmp)") # 设置文件扩展名过滤,注意用双分号间隔
782 | if background_image:
783 | self.config['background'] = background_image
784 | self.config.write()
785 | self.label_top_background_image.setStyleSheet("""QLabel{
786 | border:none;
787 | border-bottom-left-radius:0px;
788 | border-bottom-right-radius:0px;
789 | border-image:url('%s');
790 | }""" % background_image)
791 | QApplication.processEvents()
792 | time.sleep(0.1)
793 |
794 | def font_size_(self):
795 | self.config = ConfigObj("config.ini", encoding="UTF8")
796 | font_size, ok = QInputDialog.getInt(self, "字体设置", "字体大小【整数】", int(self.config['font']['size']), 1, 70, 1)
797 | if ok:
798 | self.config['font']['size'] = font_size
799 | self.config.write()
800 | self.lineedit_input.setStyleSheet("""
801 | QLineEdit{
802 | color:%s;
803 | font-family:"Microsoft YaHei";
804 | font-size:%spx;
805 | border-radius:0px;
806 | border-bottom:1px solid #E5E5E5;
807 | }
808 | QLineEdit:hover{
809 | border-bottom:1px solid #C1C1C1;
810 | }
811 | QLineEdit:focus{
812 | border-bottom:1px solid #12B7F5
813 | }""" % (
814 | self.config['font']['color'], font_size))
815 |
816 | def font_color_(self):
817 | self.config = ConfigObj("config.ini", encoding="UTF8")
818 | font_color = QColorDialog.getColor(Qt.blue, self, "信息框颜色")
819 | if font_color.name():
820 | self.config['font']['color'] = font_color.name()
821 | self.config.write()
822 | self.lineedit_input.setStyleSheet("""
823 | QLineEdit{
824 | color:%s;
825 | font-family:"Microsoft YaHei";
826 | font-size:%spx;
827 | border-radius:0px;
828 | border-bottom:1px solid #E5E5E5;
829 | }
830 | QLineEdit:hover{
831 | border-bottom:1px solid #C1C1C1;
832 | }
833 | QLineEdit:focus{
834 | border-bottom:1px solid #12B7F5
835 | }""" % (
836 | font_color.name(), self.config['font']['size']))
837 |
838 | def reset_(self):
839 | # 重置设置
840 | config_ini.config_()
841 | self.config = ConfigObj("config.ini", encoding='UTF8')
842 | self.width_ = int(self.config['size']['width'])
843 | self.height_ = int(self.config['size']['height'])
844 | self.size_window()
845 | self.lineedit_input.setStyleSheet("""
846 | QLineEdit{
847 | color:%s;
848 | font-family:"Microsoft YaHei";
849 | font-size:%spx;
850 | border-radius:0px;
851 | border-bottom:1px solid #E5E5E5;
852 | }
853 | QLineEdit:hover{
854 | border-bottom:1px solid #C1C1C1;
855 | }
856 | QLineEdit:focus{
857 | border-bottom:1px solid #12B7F5
858 | }""" % (
859 | self.config['font']['color'], self.config['font']['size']))
860 | self.label_top_background_image.setStyleSheet("""QLabel{
861 | border:none;
862 | border-bottom-left-radius:0px;
863 | border-bottom-right-radius:0px;
864 | border-image:url('./image/73114636.png');
865 | }""")
866 |
867 | def quality_(self, name):
868 | # 画质设置
869 |
870 | self.config = ConfigObj("config.ini", encoding='UTF8')
871 | if name == "全部画质":
872 | self.quality_all.setChecked(True)
873 | self.quality_high.setChecked(False)
874 | self.quality_low.setChecked(False)
875 | self.config['quality'] = 'all'
876 |
877 | elif name == "最高画质":
878 | self.quality_high.setChecked(True)
879 | self.quality_all.setChecked(False)
880 | self.quality_low.setChecked(False)
881 | self.config['quality'] = 'high'
882 |
883 | elif name == "最低画质":
884 | self.quality_low.setChecked(True)
885 | self.quality_all.setChecked(False)
886 | self.quality_high.setChecked(False)
887 | self.config['quality'] = 'low'
888 | self.config.write()
889 |
890 | def open_path_(self):
891 | # 打开直播源目录
892 | if not os.path.exists(os.getcwd() + "/real_save"): # 判断文件夹是否创建
893 | os.makedirs(os.getcwd() + "/real_save")
894 | os.startfile(os.getcwd() + "/real_save")
895 |
896 | def open_dir_(self):
897 | # 打开软件目录
898 | os.startfile(os.getcwd())
899 |
900 | def mouse_release_event_real_(self, event):
901 | action = self.menu_real.actionAt(event.pos())
902 | if not action:
903 | # 没有找到action就交给QMenu自己处理
904 | return QtWidgets.QMenu.mouseReleaseEvent(self.menu_real, event)
905 | if action.property('canHide'): # 如果有该属性则给菜单自己处理
906 | return QtWidgets.QMenu.mouseReleaseEvent(self.menu_real, event)
907 | # 找到了QAction则只触发Action
908 | action.activate(action.Trigger)
909 |
910 | def mouse_play_event_real_(self, event):
911 | action = self.menu_play.actionAt(event.pos())
912 | if not action:
913 | return QtWidgets.QMenu.mouseReleaseEvent(self.menu_play, event)
914 | if action.property('canHide'):
915 | return QtWidgets.QMenu.mouseReleaseEvent(self.menu_play, event)
916 | action.activate(action.Trigger)
917 |
918 | def mouse_release_event_quality_(self, event):
919 | action = self.menu_quality.actionAt(event.pos())
920 | if not action:
921 | return QtWidgets.QMenu.mouseReleaseEvent(self.menu_quality, event)
922 | if action.property('canHide'):
923 | return QtWidgets.QMenu.mouseReleaseEvent(self.menu_quality, event)
924 | action.activate(action.Trigger)
925 |
926 | def add_shadow(self):
927 | # 添加阴影
928 | effect_shadow = QGraphicsDropShadowEffect(self)
929 | effect_shadow.setOffset(1, 1) # 偏移
930 | effect_shadow.setBlurRadius(10) # 阴影半径
931 | effect_shadow.setColor(QtCore.Qt.gray) # 阴影颜色
932 | self.setGraphicsEffect(effect_shadow) # 将设置套用到widget窗口中
933 |
934 | def config_ini(self):
935 | if not os.access("config.ini", os.F_OK):
936 | # 初始化config配置文件
937 | config_ini.config_()
938 | self.config = ConfigObj("config.ini", encoding='UTF8')
939 | self.width_ = int(int(self.config['size']['width']) * float(self.config['size']['mul']))
940 | self.height_ = int(int(self.config['size']['height']) * float(self.config['size']['mul']))
941 | self.setFixedSize(self.width_, self.height_)
942 |
943 | def size_(self):
944 | if not os.access("config.ini", os.F_OK):
945 | self.config_ini()
946 | config = ConfigObj("{}/config.ini".format(os.getcwd()), encoding='UTF8')
947 | if float(config['size']['mul']) >= 3:
948 | config['size']['mul'] = 1
949 | else:
950 | config['size']['mul'] = float(config['size']['mul']) + 0.5
951 | config['size']['width'] = 438
952 | config['size']['height'] = 338
953 | config.write()
954 |
955 | self.config = ConfigObj("config.ini", encoding='UTF8')
956 | self.width_ = int(int(self.config['size']['width']) * float(self.config['size']['mul']))
957 | self.height_ = int(int(self.config['size']['height']) * float(self.config['size']['mul']))
958 | self.size_window()
959 |
960 | def size_window(self):
961 | self.setFixedSize(self.width_, self.height_)
962 | self.label_background.setGeometry(QtCore.QRect(5, 0, self.width_ - 10, self.height_ - 10))
963 | self.label_top_background.setFixedSize(self.width_ - 10, int(self.height_ / 2.7))
964 | self.label_top_background_image.setFixedSize(self.width_ - 10, int(self.height_ / 2.7))
965 | self.label_bottom_background.setFixedSize(self.label_top_background.width(),
966 | self.height_ - self.label_top_background.height() - 10)
967 | self.label_icon.setFixedSize(self.label_top_background.width(), 32)
968 | self.pushbutton_logo.setGeometry(int(self.width_ / 2 - 47 - 5), int(self.height_ / 2.7 - 47), 94, 94)
969 | self.pushbutton_get.setFixedSize(int(self.width_ - 194), 38)
970 | self.lineedit_input.setFixedSize(int(self.width_ - 194), 80)
971 | self.move((QtGui.QCursor.pos().x() - self.pushbutton_size.x() - 20), QtGui.QCursor.pos().y() - 16)
972 |
973 | def min_(self): # 最小化窗口
974 | self.showMinimized()
975 |
976 | def close_(self): # 关闭窗口动画
977 | subprocess.Popen("cmd.exe /C" + "taskkill -f -im N_m3u8DL-RE.exe", shell=True, stdout=subprocess.PIPE,
978 | stderr=subprocess.PIPE, universal_newlines=True, bufsize=1, creationflags=0x08000000)
979 |
980 | self.mSysTrayIcon = None # 软件关闭后托盘不会有残留
981 | self.close_animation = QPropertyAnimation(self, b"windowOpacity")
982 | self.close_animation.setDuration(400) # 动画时长
983 | self.close_animation.setStartValue(1) # 指定动作的初始状态
984 | self.close_animation.setEndValue(0) # 指定动作的最终状态
985 | self.close_animation.finished.connect(self.close)
986 | self.close_animation.start()
987 |
988 | def mousePressEvent(self, event):
989 | if event.button() == Qt.LeftButton:
990 | self.dragPosition = event.globalPos() - self.frameGeometry().topLeft()
991 | QApplication.postEvent(self, QEvent(174))
992 | event.accept()
993 |
994 | def mouseMoveEvent(self, event):
995 | if event.buttons() == Qt.LeftButton:
996 | try:
997 | self.move(event.globalPos() - self.dragPosition)
998 | event.accept()
999 | except:
1000 | pass
1001 |
1002 | def keyPressEvent(self, event): # 键盘事件
1003 | if str(event.key()) == '16777216':
1004 | self.close_()
1005 | if str(event.key()) == '16777220' or str(event.key()) == '16777221':
1006 | self.thread_get()
1007 |
1008 |
1009 | if __name__ == "__main__":
1010 | app = QApplication(sys.argv)
1011 | translator = QTranslator()
1012 | translator.load('./qtbase_zh_CN.qm')
1013 | app.installTranslator(translator)
1014 | ui = MainUi()
1015 | ui.show()
1016 | sys.exit(app.exec_())
1017 |
--------------------------------------------------------------------------------
/chromedriver.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/chromedriver.exe
--------------------------------------------------------------------------------
/config_ini.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/13 12:00
3 | # @Author: Lanser
4 | # @File:config_ini.py
5 | # Software:PyCharm
6 |
7 | from configobj import ConfigObj
8 | import os
9 |
10 |
11 | def config_():
12 | config = ConfigObj("%s/config.ini" % os.path.dirname(__file__), encoding='UTF8')
13 | config['size'] = {}
14 | config['size']['mul'] = 1
15 | config['size']['width'] = 438
16 | config['size']['height'] = 338
17 | config['background'] = "./image/73114636.png"
18 | config['quality'] = "all"
19 | config['font'] = {}
20 | config['font']['size'] = 26
21 | config['font']['color'] = '#505050'
22 | config['real'] = {'douyu': '1', 'huya': '1', 'bili': '1', 'douyin': '1', 'kuaishou':'1','yy': '1', 'kuwo': '1', 'kugou': '1', 'yizhibo': '1'}
23 | config['rid'] = ""
24 | config['player'] = 'pot'
25 | config['mpv'] = ""
26 | config.write()
27 |
--------------------------------------------------------------------------------
/image/73114636.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/73114636.png
--------------------------------------------------------------------------------
/image/73114636_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/73114636_bg.png
--------------------------------------------------------------------------------
/image/close_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/close_icon.png
--------------------------------------------------------------------------------
/image/close_icon_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/close_icon_2.png
--------------------------------------------------------------------------------
/image/close_icon_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/close_icon_3.png
--------------------------------------------------------------------------------
/image/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/copy.png
--------------------------------------------------------------------------------
/image/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/icon.ico
--------------------------------------------------------------------------------
/image/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/icon.png
--------------------------------------------------------------------------------
/image/image_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/image_1.jpg
--------------------------------------------------------------------------------
/image/image_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/image_2.jpg
--------------------------------------------------------------------------------
/image/image_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/image_3.jpg
--------------------------------------------------------------------------------
/image/image_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/image_4.jpg
--------------------------------------------------------------------------------
/image/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/logo.png
--------------------------------------------------------------------------------
/image/logo_get.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/logo_get.png
--------------------------------------------------------------------------------
/image/lol_s12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/lol_s12.png
--------------------------------------------------------------------------------
/image/min_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/min_icon.png
--------------------------------------------------------------------------------
/image/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/play.png
--------------------------------------------------------------------------------
/image/rec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/rec.png
--------------------------------------------------------------------------------
/image/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/save.png
--------------------------------------------------------------------------------
/image/setting_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/setting_icon.png
--------------------------------------------------------------------------------
/image/size_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/image/size_icon.png
--------------------------------------------------------------------------------
/qtbase_zh_CN.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/s282730788/RealTool/33db6f408b476d1f2ac35bf7ef22b37e7a986dd5/qtbase_zh_CN.qm
--------------------------------------------------------------------------------
/real/bili.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/14 0:35
3 | # @Author: Lanser
4 | # @File:bili.py
5 | # Software:PyCharm
6 |
7 | # 获取哔哩哔哩直播的真实流媒体地址,默认获取直播间提供的最高画质
8 | # qn=1500高清
9 | # qn=2500超清
10 | # qn=4000蓝光
11 | # qn=10000原画
12 | import requests
13 | import re
14 | import sys
15 | # sys.path.insert(0, '..')
16 | # from requests_code import requests_get_code
17 | from .requests_code import requests_get_code
18 | from multiprocessing.pool import ThreadPool
19 |
20 |
21 | class BiliBili:
22 |
23 | def __init__(self, rid):
24 | """
25 | 有些地址无法在PotPlayer播放,建议换个播放器试试
26 | Args:
27 | rid:
28 | """
29 | self.rid = rid
30 | self.header = {
31 | 'User-Agent': 'Mozilla/5.0 (iPod; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, '
32 | 'like Gecko) CriOS/87.0.4280.163 Mobile/15E148 Safari/604.1',
33 | }
34 |
35 | def get_real_url(self):
36 | # 先获取直播状态和真实房间号
37 | r_url = 'https://api.live.bilibili.com/room/v1/Room/room_init'
38 | param = {
39 | 'id': self.rid
40 | }
41 | with requests.Session() as self.s:
42 | res = self.s.get(r_url, headers=self.header, params=param, timeout=2).json()
43 | if '不存在' in res['msg']:
44 | return {}
45 | live_status = res['data']['live_status']
46 | if live_status != 1:
47 | return {}
48 | self.real_room_id = res['data']['room_id']
49 | print(self.real_room_id)
50 | url = 'https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo'
51 | param = {
52 | 'room_id': self.real_room_id,
53 | 'protocol': '0,1',
54 | 'format': '0,1,2',
55 | 'codec': '0,1',
56 | 'qn': 10000,
57 | 'platform': 'web',
58 | 'ptype': 8,
59 | }
60 | res = self.s.get(url, headers=self.header, params=param, timeout=2).json()
61 | uid = res['data']['uid']
62 | stream_info = res['data']['playurl_info']['playurl']['stream']
63 | accept_qn = stream_info[0]['format'][0]['codec'][0]['accept_qn']
64 | real_lists = []
65 | real_list = []
66 | thread_list = []
67 | real_dict = {}
68 |
69 | for data in stream_info:
70 | print(data)
71 | format_name = data['format'][0]['format_name']
72 | if format_name == 'ts':
73 | base_url = data['format'][-1]['codec'][0]['base_url']
74 | url_info = data['format'][-1]['codec'][0]['url_info']
75 | for i, info in enumerate(url_info):
76 | for qn in accept_qn:
77 | url_ = base_url
78 | host = info['host']
79 | extra = info['extra']
80 | if qn < 10000:
81 | qn = qn * 10
82 | url_ = re.sub('bluray/index', f'{qn}/index', base_url)
83 | elif qn > 10000:
84 | continue
85 | extra = re.sub('qn=(\d+)', f'qn={qn}', extra)
86 | real_lists.append({f'线路{i + 1}_{qn}': f'{host}{url_}{extra}'})
87 | break
88 | if real_lists:
89 | print(real_lists)
90 | pool = ThreadPool(processes=int(len(real_lists)))
91 | for real_ in real_lists:
92 | thread_list.append(pool.apply_async(requests_get_code, args=(real_,)))
93 | for thread in thread_list:
94 | return_dict = thread.get()
95 | if return_dict:
96 | real_list.append(return_dict)
97 | if real_list:
98 | try:
99 | real_list.append({'name': self.name_(uid)})
100 | except Exception as err:
101 | print("匹配用户名失败:".format(err))
102 | real_list.append({'name': uid})
103 |
104 | real_list.append({'rid': self.rid})
105 |
106 | real_dict['bili'] = real_list
107 | if real_dict:
108 | return real_dict
109 | return {}
110 |
111 | def name_(self, uid):
112 | url = 'https://api.bilibili.com/x/space/wbi/acc/info?mid={}&token=&platform=web'.format(uid)
113 | headers = {
114 | 'origin': 'https://space.bilibili.com',
115 | 'referer': 'https://space.bilibili.com/{}/'.format(uid),
116 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
117 | }
118 | response = requests.get(url, headers=headers, timeout=1).json()
119 | name = response['data']['name']
120 | if response:
121 | return name
122 | else:
123 | return uid
124 | if __name__ == '__main__':
125 | rid = '6'
126 | bili = BiliBili(rid)
127 | print(bili.get_real_url())
128 |
--------------------------------------------------------------------------------
/real/douyin.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/14 22:56
3 | # @Author: Lanser
4 | # @File:douyin.py
5 | # Software:PyCharm
6 |
7 | import re
8 | import json
9 | import requests
10 | import urllib.parse
11 | import sys
12 | # sys.path.insert(0, '..')
13 | from .requests_code import requests_get_code
14 | # from requests_code import requests_get_code
15 | from multiprocessing.pool import ThreadPool
16 | import requests_html
17 |
18 |
19 | class DouYin:
20 | def __init__(self, rid):
21 | self.rid = rid
22 |
23 | def get_real_url(self):
24 |
25 | if 'v.douyin.com' in self.rid:
26 | self.rid = self.get_room_id(self.rid)
27 | url = 'https://live.douyin.com/{}'.format(self.rid)
28 | headers = {
29 | "cookie": "__ac_nonce=000000000000000000000; ",
30 | # "cookie": "__ac_nonce=063e4d746007f686f2fcf; ",
31 | "referer": "https://live.douyin.com/",
32 | "upgrade-insecure-requests": "1",
33 | "user-agent": "Mozilla/5.0(WindowsNT10.0;WOW64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/86.0.4240.198Safari/537.36",
34 | }
35 | s = requests.session()
36 | response = s.get(url, headers=headers)
37 |
38 | text = urllib.parse.unquote(
39 | re.findall('', response.text)[0])
40 |
41 | json_ = json.loads(text)
42 | try:
43 | name = json_['app']['initialState']['roomStore']['roomInfo']['room']['owner']['nickname']
44 | except:
45 | name = self.rid
46 | douyin_list = []
47 | douyin_dict = {}
48 |
49 | try:
50 | flv_pull_url = json_['app']['initialState']['roomStore']['roomInfo']['room']['stream_url']['flv_pull_url']
51 | douyin_list.append(flv_pull_url)
52 | except:
53 | pass
54 |
55 | try:
56 | hls_pull_url_map = json_['app']['initialState']['roomStore']['roomInfo']['room']['stream_url'][
57 | 'hls_pull_url_map']
58 | lists_ = json_['app']['initialState']['roomStore']['roomInfo']['room']['stream_url']['live_core_sdk_data']['pull_data']['stream_data']
59 | douyin_dict = json.loads(lists_)['data']
60 | douyin_list.append(hls_pull_url_map)
61 | except:
62 | pass
63 |
64 | real_lists = []
65 | real_list = []
66 | thread_list = []
67 | real_dict = {}
68 |
69 | # for real_ in douyin_list:
70 | # for name_ in real_:
71 | # if '.flv' in real_[name_]:
72 | # real_lists.append({f'flv_{name_}': real_[name_]})
73 | # elif '.m3u8' in real_[name_]:
74 | # real_lists.append({f'm3u8_{name_}': real_[name_]})
75 |
76 | for real_ in douyin_dict:
77 | try:
78 | real_lists.append({f'flv_{real_}': douyin_dict[real_]['main']['flv']})
79 | real_lists.append({f'hls_{real_}': douyin_dict[real_]['main']['hls']})
80 | except:
81 | pass
82 |
83 | if real_lists:
84 | pool = ThreadPool(processes=int(len(real_lists)))
85 | for real_ in real_lists:
86 | thread_list.append(pool.apply_async(requests_get_code, args=(real_,)))
87 | for thread in thread_list:
88 | return_dict = thread.get()
89 | if return_dict:
90 | real_list.append(return_dict)
91 | if real_list:
92 | real_list.append({'name': name})
93 | real_list.append({'rid': self.rid})
94 | real_dict['douyin'] = real_list
95 | if real_dict:
96 | return real_dict
97 | return {}
98 |
99 | def get_room_id(self, url):
100 | headers = {
101 | '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',
102 | }
103 | url = re.search(r'(https.*)', url).group(1)
104 | response = requests.head(url, headers=headers)
105 | url = response.headers['location']
106 | room_id = re.search(r'\d{19}', url).group(0)
107 |
108 | headers.update({
109 | 'cookie': '_tea_utm_cache_1128={%22utm_source%22:%22copy%22%2C%22utm_medium%22:%22android%22%2C%22utm_campaign%22:%22client_share%22}',
110 | 'host': 'webcast.amemv.com',
111 | })
112 | params = (
113 | ('type_id', '0'),
114 | ('live_id', '1'),
115 | ('room_id', room_id),
116 | ('app_id', '1128'),
117 | ('X-Bogus', '1'),
118 | )
119 |
120 | response = requests.get('https://webcast.amemv.com/webcast/room/reflow/info/?', headers=headers,
121 | params=params).json()
122 | if response:
123 | return response['data']['room']['owner']['web_rid']
124 |
125 | # if __name__ == '__main__':
126 | # r = '927264600913'
127 | # douyin = DouYin(r)
128 | # print(douyin.get_real_url())
129 |
--------------------------------------------------------------------------------
/real/douyu.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/13 18:34
3 | # @Author: Lanser
4 | # @File:douyu.py
5 | # Software:PyCharm
6 |
7 | # 获取斗鱼直播间的真实流媒体地址,默认最高画质
8 | # 使用 https://github.com/wbt5/real-url/issues/185 中两位大佬@wjxgzz @4bbu6j5885o3gpv6ss8找到的的CDN,在此感谢!
9 | import hashlib
10 | import json
11 | import re
12 | import time
13 | import sys
14 | from multiprocessing.pool import ThreadPool
15 | import execjs
16 | import requests
17 |
18 | # sys.path.insert(0, '..')
19 | from .requests_code import requests_get_code
20 |
21 |
22 | class DouYu:
23 | """
24 | 可用来替换返回链接中的主机部分
25 | 两个阿里的CDN:
26 | dyscdnali1.douyucdn.cn
27 | dyscdnali3.douyucdn.cn
28 | 墙外不用带尾巴的akm cdn:
29 | hls3-akm.douyucdn.cn
30 | hlsa-akm.douyucdn.cn
31 | hls1a-akm.douyucdn.cn
32 | """
33 |
34 | def __init__(self, rid):
35 | self.rate_list = []
36 | """
37 | 房间号通常为1~8位纯数字,浏览器地址栏中看到的房间号不一定是真实rid.
38 | Args:
39 | rid:
40 | """
41 | self.did = '10000000000000000000000000001501'
42 | self.t10 = str(int(time.time()))
43 | self.t13 = str(int((time.time() * 1000)))
44 | self.rid = rid
45 | self.s = requests.Session()
46 |
47 |
48 | @staticmethod
49 | def md5(data):
50 | return hashlib.md5(data.encode('utf-8')).hexdigest()
51 |
52 | def get_pre(self):
53 | url = 'https://playweb.douyucdn.cn/lapi/live/hlsH5Preview/' + self.rid
54 | data = {
55 | 'rid': self.rid,
56 | 'did': self.did
57 | }
58 | auth = DouYu.md5(self.rid + self.t13)
59 | headers = {
60 | 'rid': self.rid,
61 | 'time': self.t13,
62 | 'auth': auth
63 | }
64 | res = self.s.post(url, headers=headers, data=data, timeout=2).json()
65 | error = res['error']
66 | data = res['data']
67 | try:
68 | for i in res['data']['settings']:
69 | self.rate_list.append(i)
70 | except:
71 | pass
72 | key = ''
73 | if data:
74 | rtmp_live = data['rtmp_live']
75 | key = re.search(
76 | r'(\d{1,8}[0-9a-zA-Z]+)_?\d{0,4}(/playlist|.m3u8)', rtmp_live).group(1)
77 | return error, key
78 |
79 | def get_js(self):
80 | result = re.search(
81 | r'(function ub98484234.*)\s(var.*)', self.res).group()
82 | func_ub9 = re.sub(r'eval.*;}', 'strc;}', result)
83 | js = execjs.compile(func_ub9)
84 | res = js.call('ub98484234')
85 |
86 | v = re.search(r'v=(\d+)', res).group(1)
87 | rb = DouYu.md5(self.rid + self.did + self.t10 + v)
88 |
89 | func_sign = re.sub(r'return rt;}\);?', 'return rt;}', res)
90 | func_sign = func_sign.replace('(function (', 'function sign(')
91 | func_sign = func_sign.replace(
92 | 'CryptoJS.MD5(cb).toString()', '"' + rb + '"')
93 |
94 | js = execjs.compile(func_sign)
95 | params = js.call('sign', self.rid, self.did, self.t10)
96 | params += '&ver=219032101&rid={}&rate=-1'.format(self.rid)
97 |
98 | url = 'https://m.douyu.com/api/room/ratestream'
99 | res = self.s.post(url, params=params, timeout=2).text
100 | json_ = json.loads(res)
101 | try:
102 | for i in json_['data']['settings']:
103 | self.rate_list.append(i)
104 | except:
105 | pass
106 | key = re.search(
107 | r'(\d{1,8}[0-9a-zA-Z]+)_?\d{0,4}(.m3u8|/playlist)', res).group(1)
108 | return key
109 |
110 | def get_real_url(self):
111 | self.res = self.s.get('https://m.douyu.com/' + str(self.rid)).text
112 | result = re.search(r'rid":(\d{1,8}),"vipId', self.res)
113 | try:
114 | name = re.findall('"nickname":"(.*?)"', self.res)[0]
115 | except:
116 | name = self.rid
117 | if result:
118 | self.rid = result.group(1)
119 | else:
120 | return {}
121 | error, key = self.get_pre()
122 | if error == 0:
123 | pass
124 | elif error == 102:
125 | return False
126 | elif error == 104:
127 | return False
128 | else:
129 | key = self.get_js()
130 |
131 | real_lists = []
132 | real_list = []
133 | thread_list = []
134 | real_dict = {}
135 | if not self.rate_list:
136 | self.rate_list = [{'name': '蓝光', 'rate': 0, 'high_bit': 1}, {'name': '超清', 'rate': 3, 'high_bit': 0},
137 | {'name': '高清', 'rate': 2, 'high_bit': 0}]
138 | for rate in self.rate_list:
139 | if rate['rate'] != 0:
140 | flv = {"{}_flv".format(rate['name']): "http://hdltctwk.douyucdn2.cn/live/{}_{}.flv?uuid=".format(key, rate[
141 | 'rate'] * 1000)}
142 | m3u8 = {"{}_m3u8".format(rate['name']): "http://hdltctwk.douyucdn2.cn/live/{}_{}.m3u8?uuid=".format(key, rate[
143 | 'rate'] * 1000)}
144 | x_p2p = {"{}_x_p2p".format(rate['name']): "http://hdltctwk.douyucdn2.cn/live/{}_{}.xs?uuid=".format(key,
145 | rate[
146 | 'rate'] * 1000)}
147 | aliyun = {
148 | "{}_aliyun".format(rate['name']): "http://dyscdnali1.douyucdn.cn/live/{}_{}.flv?uuid=".format(key,
149 | rate[
150 | 'rate'] * 1000)}
151 | real_lists.append(flv)
152 | real_lists.append(m3u8)
153 | real_lists.append(x_p2p)
154 | real_lists.append(aliyun)
155 | else:
156 | flv = {"{}_flv".format(rate['name']): "http://hdltctwk.douyucdn2.cn/live/{}.flv?uuid=".format(key)}
157 | m3u8 = {"{}_m3u8".format(rate['name']): "http://hdltctwk.douyucdn2.cn/live/{}.m3u8?uuid=".format(key)}
158 | x_p2p = {"{}_x_p2p".format(rate['name']): "http://hdltctwk.douyucdn2.cn/live/{}.xs?uuid=".format(key)}
159 | aliyun = {
160 | "{}_aliyun".format(rate['name']): "http://dyscdnali1.douyucdn.cn/live/{}.flv?uuid=".format(key)}
161 | real_lists.append(flv)
162 | real_lists.append(m3u8)
163 | real_lists.append(x_p2p)
164 | real_lists.append(aliyun)
165 | if real_lists:
166 | pool = ThreadPool(processes=int(len(real_lists)))
167 | for real_ in real_lists:
168 | thread_list.append(pool.apply_async(requests_get_code, args=(real_,)))
169 | for thread in thread_list:
170 | return_dict = thread.get()
171 | if return_dict:
172 | real_list.append(return_dict)
173 | if real_list:
174 | real_list.append({'name': name})
175 | real_list.append({'rid': self.rid})
176 | real_dict['douyu'] = real_list
177 | return real_dict
178 | return {}
179 |
180 | # if __name__ == '__main__':
181 | # r = '101'
182 | # # r = input('输入斗鱼直播间号:\n')
183 | # s = DouYu(r)
184 | # print(s.get_real_url())
185 |
--------------------------------------------------------------------------------
/real/huya.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/13 18:34
3 | # @Author: Lanser
4 | # @File:huya.py
5 | # Software:PyCharm
6 |
7 | # 获取虎牙直播的真实流媒体地址。
8 |
9 | import requests
10 | import re
11 | import base64
12 | import urllib.parse
13 | import hashlib
14 | import time
15 | import sys
16 | from .requests_code import requests_get_code
17 | from multiprocessing.pool import ThreadPool
18 |
19 | class HuYa:
20 | def __init__(self, rid):
21 | self.rid = rid
22 |
23 | def live(self, e):
24 | i, b = e.split('?')
25 | r = i.split('/')
26 | s = re.sub(r'.(flv|m3u8)', '', r[-1])
27 | c = b.split('&', 3)
28 | c = [i for i in c if i != '']
29 | n = {i.split('=')[0]: i.split('=')[1] for i in c}
30 | fm = urllib.parse.unquote(n['fm'])
31 | u = base64.b64decode(fm).decode('utf-8')
32 | p = u.split('_')[0]
33 | f = str(int(time.time() * 1e7))
34 | l = n['wsTime']
35 | t = '0'
36 | h = '_'.join([p, t, s, f, l])
37 | m = hashlib.md5(h.encode('utf-8')).hexdigest()
38 | y = c[-1]
39 | url = "{}?wsSecret={}&wsTime={}&u={}&seqid={}&{}".format(i, m, l, t, f, y)
40 | return url
41 |
42 | def get_real_url(self):
43 | real_lists = []
44 | real_list = []
45 | thread_list = []
46 | real_dict = {}
47 | name = ''
48 | try:
49 | room_url = 'https://m.huya.com/{}'.format(self.rid)
50 | header = {
51 | 'Content-Type': 'application/x-www-form-urlencoded',
52 | 'User-Agent': 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) '
53 | 'Chrome/75.0.3770.100 Mobile Safari/537.36 '
54 | }
55 | response = requests.get(url=room_url, headers=header, timeout=2).text
56 |
57 | try:
58 | name = re.findall('"sNick":"(.*?)","iSex"', response)[0]
59 | except:
60 | name = self.rid
61 | liveLineUrl = re.findall(r'"liveLineUrl":"([\s\S]*?)",', response)[0]
62 | liveline = base64.b64decode(liveLineUrl).decode('utf-8')
63 |
64 | if liveline:
65 | if 'replay' in liveline:
66 | real_lists.append({'直播录像': f'https://{liveline}'})
67 | else:
68 | liveline = self.live(liveline)
69 | real_url_flv = ("https:" + liveline).replace("hls", "flv").replace("m3u8", "flv").replace(
70 | '&ctype=tars_mobile', '')
71 | print(real_url_flv)
72 | rate = [500, 2000, 4000, 8000, 10000]
73 | for ratio in range(len(rate) - 1, -1, -1):
74 | ratio = rate[ratio]
75 | if ratio != 10000:
76 | real_url_flv = real_url_flv.replace('.flv?', f'.flv?ratio={ratio}&')
77 | real_lists.append({f'flv_{ratio}': real_url_flv})
78 | else:
79 | real_lists.append({f'flv_{ratio}': real_url_flv})
80 | except:
81 | pass
82 | if real_lists:
83 | pool = ThreadPool(processes=int(len(real_lists)))
84 | for real_ in real_lists:
85 | thread_list.append(pool.apply_async(requests_get_code, args=(real_,)))
86 | for thread in thread_list:
87 | return_dict = thread.get()
88 | if return_dict:
89 | real_list.append(return_dict)
90 | if real_list:
91 | real_list.append({'name': name})
92 | real_list.append({'rid': self.rid})
93 | real_dict['huya'] = real_list
94 | if real_dict:
95 | return real_dict
96 | return {}
97 |
98 |
99 | # rid = input('输入虎牙直播房间号:\n')
100 | # real_url = HuYa(rid)
101 | # print('该直播间源地址为:')
102 | # print(real_url.get_real_url())
103 |
--------------------------------------------------------------------------------
/real/kugou.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/23 6:07
3 | # @Author: Lanser
4 | # @File:kugou.py
5 | # Software:PyCharm
6 | # 获取酷狗繁星直播的真实流媒体地址,默认最高码率。
7 | import re
8 |
9 | import requests
10 | import sys
11 | # sys.path.insert(0, '..')
12 | from .requests_code import requests_get_code
13 | from multiprocessing.pool import ThreadPool
14 |
15 |
16 | class KuGou:
17 |
18 | def __init__(self, rid):
19 | """
20 | 酷狗繁星直播
21 | Args:
22 | rid: 房间号
23 | """
24 | self.rid = rid
25 | self.s = requests.Session()
26 | self.url = 'https://fx1.service.kugou.com/video/mo/live/pull/h5/v3/streamaddr'
27 |
28 | def get_real_url(self):
29 | params = {
30 | 'roomId': self.rid,
31 | 'platform': 18,
32 | 'version': 1000,
33 | 'streamType': '3-6',
34 | 'liveType': 1,
35 | 'ch': 'fx',
36 | 'ua': 'fx-mobile-h5',
37 | 'kugouId': 0,
38 | 'layout': 1
39 | }
40 | real_lists = []
41 | real_list = []
42 | thread_list = []
43 | real_dict = {}
44 | try:
45 | name = self.name(self.rid)
46 | except:
47 | name = self.rid
48 | try:
49 | res = self.s.get(self.url, params=params, timeout=2).json()
50 | if res['code'] == 1:
51 | return {}
52 | try:
53 | real_data_horizontal = res['data']['horizontal']
54 | real_data_vertical = res['data']['vertical']
55 | if real_data_horizontal:
56 | real_lists.append({'hls': real_data_horizontal[0]['hls'][0]})
57 | real_lists.append({'hls': real_data_horizontal[0]['httpshls'][0]})
58 | if real_data_vertical:
59 | real_lists.append({'hls': real_data_vertical[0]['hls'][0]})
60 | real_lists.append({'httpshls': real_data_vertical[0]['httpshls'][0]})
61 | except:
62 | return {}
63 |
64 | if real_lists:
65 | pool = ThreadPool(processes=int(len(real_lists)))
66 | for real_ in real_lists:
67 | thread_list.append(pool.apply_async(requests_get_code, args=(real_,)))
68 | for thread in thread_list:
69 | return_dict = thread.get()
70 | if return_dict:
71 | real_list.append(return_dict)
72 | if real_list:
73 | real_list.append({'name': name})
74 | real_list.append({'rid': self.rid})
75 | real_dict['kugou'] = real_list
76 | return real_dict
77 | return {}
78 | except IndexError:
79 | try:
80 | url = f'https://fx1.service.kugou.com/biz/ChannelVServices/' \
81 | f'RoomLiveService.RoomLiveService.getCurrentLiveStarForMob/{self.rid}'
82 | res = self.s.get(url).json()
83 | if res['code'] == 1:
84 | return {}
85 | roomid = res['data']['roomId']
86 | self.url = 'https://fx2.service.kugou.com/video/pc/live/pull/mutiline/streamaddr'
87 | params = {
88 | 'std_rid': roomid,
89 | 'version': '1.0',
90 | 'streamType': '1-2-3-5-6',
91 | 'targetLiveTypes': '1-4-5-6',
92 | 'ua': 'fx-h5'
93 | }
94 | res = self.s.get(self.url, params=params, timeout=2).json()
95 | real_url = res.get('data').get('lines')[-1].get('streamProfiles')[-1]['httpsHls'][-1]
96 | real_dict['kugou'] = [{'hls': real_url}, {'name': name}, {'rid': self.rid}]
97 | return real_dict
98 | except:
99 | return {}
100 |
101 | def name(self, uid):
102 | headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36'}
103 | url = 'https://fanxing.kugou.com/{}'.format(uid)
104 | response = requests.get(url, headers=headers).text
105 | name = re.findall("starName:'(.*?)'", response)[0]
106 | return name
107 |
108 | # if __name__ == '__main__':
109 | # rid = '6738148'
110 | # # rid = '2678075'
111 | # kugou = KuGou(rid)
112 | # print(kugou.get_real_url())
113 |
--------------------------------------------------------------------------------
/real/kuwo.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/23 5:04
3 | # @Author: Lanser
4 | # @File:kuwo.py
5 | # Software:PyCharm
6 |
7 | # 酷我聚星直播:http://jx.kuwo.cn/
8 |
9 | import requests
10 | import re
11 | import sys
12 | # sys.path.insert(0, '..')
13 | from .requests_code import requests_get_code
14 | from multiprocessing.pool import ThreadPool
15 |
16 | class KuWo:
17 |
18 | def __init__(self, rid):
19 | self.rid = rid
20 | self.BASE_URL = 'https://jxm0.kuwo.cn/video/mo/live/pull/h5/v3/streamaddr'
21 | self.s = requests.Session()
22 |
23 | def get_real_url(self):
24 | res = self.s.get(f'https://jx.kuwo.cn/{self.rid}').text
25 | roomid = re.search(r"roomId: '(\d*)'", res)
26 | try:
27 | name = re.findall("starName: '(.*?)'", res)[0]
28 | except:
29 | name = self.rid
30 | if roomid:
31 | self.rid = roomid.group(1)
32 | else:
33 | return {}
34 | params = {
35 | 'std_bid': 1,
36 | 'roomId': self.rid,
37 | 'platform': 405,
38 | 'version': 1000,
39 | 'streamType': '3-6',
40 | 'liveType': 1,
41 | 'ch': 'fx',
42 | 'ua': 'fx-mobile-h5',
43 | 'kugouId': 0,
44 | 'layout': 1,
45 | 'videoAppId': 10011,
46 | }
47 | res = self.s.get(self.BASE_URL, params=params, timeout=2).json()
48 | real_lists = []
49 | real_list = []
50 | thread_list = []
51 | real_dict = {}
52 | if res['data']['sid'] == -1:
53 | return {}
54 | try:
55 | real_url = res['data']['horizontal'][0]['httpshls'][0]
56 | except (KeyError, IndexError):
57 | real_url = res['data']['vertical'][0]['httpshls'][0]
58 | real_lists.append({f'httpshls': real_url})
59 | if real_lists:
60 | pool = ThreadPool(processes=int(len(real_lists)))
61 | for real_ in real_lists:
62 | thread_list.append(pool.apply_async(requests_get_code, args=(real_,)))
63 | for thread in thread_list:
64 | return_dict = thread.get()
65 | if return_dict:
66 | real_list.append(return_dict)
67 | if real_list:
68 | real_list.append({'name': name})
69 | real_list.append({'rid': self.rid})
70 | real_dict['kuwo'] = real_list
71 | return real_dict
72 | return {}
73 |
74 |
75 |
76 | # if __name__ == '__main__':
77 | # r = 'https://x.kuwo.cn/32067302?refer=2193'
78 | # if 'kuwo.cn' in r:
79 | # r = re.findall('/(\d+)', r)[0]
80 | # print(r)
81 | # kuwo = KuWo(r)
82 | # print(kuwo.get_real_url())
83 |
--------------------------------------------------------------------------------
/real/requests_code.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/24 3:21
3 | # @Author: Lanser
4 | # @File:requests_code.py
5 | # Software:PyCharm
6 |
7 | import requests
8 |
9 |
10 | def requests_get_code(real_dict):
11 | for real_ in real_dict:
12 | try:
13 | code = requests.get(real_dict[real_], stream=True, timeout=1).status_code
14 | if code == 200:
15 | return real_dict
16 | except:
17 | pass
--------------------------------------------------------------------------------
/real/rquests_html_1.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time: 2023/1/15 20:08
3 | # @Author: 须尽欢
4 | # @File: rquests_html_1.py
5 | # @Contact: 282730788@qq.com
6 | # Software: PyCharm
7 |
8 | from requests_html import HTMLSession
9 | # import requests
10 | session = HTMLSession()
11 | headers = {
12 | "cookie": "__ac_nonce=0;",
13 | "referer": "https://live.douyin.com/",
14 | "upgrade-insecure-requests": "1",
15 | "user-agent": "Mozilla/5.0(WindowsNT10.0;WOW64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/86.0.4240.198Safari/537.36",
16 | }
17 |
18 | # headers = {"Host": "v.douyin.com",
19 | # "Connection": "keep-alive",
20 | # "sec-ch-ua": "Not_A Brand;v=99, Google Chrome;v=109, Chromium;v=109",
21 | # "sec-ch-ua-mobile": "?0",
22 | # "sec-ch-ua-platform": "Windows",
23 | # "Upgrade-Insecure-Requests": "1",
24 | # "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",
25 | # "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
26 | # "Sec-Fetch-Site": "none",
27 | # "Sec-Fetch-Mode": "navigate",
28 | # "Sec-Fetch-User": "?1",
29 | # "Sec-Fetch-Dest": "document",
30 | # "Accept-Encoding": "gzip, deflate, br",
31 | #
32 | # "Cookie": "ttwid=1%7Cy7G52XRGM50D_HvVEITMI0Zo9VEqZhMOe9j0lcjJpwI%7C1673787944%7C930453440494f53980e0b938e860106d83e598f8bd3442e85d730260b7441ba0; odin_tt=659faadea0dac0c524a727a98ff51a3b75fe39adc297a932258cf5c5a0663ab61b7b63f59ea7cc540a97c382f7ba7076ccaad5cee4bfdee043ad622d0cb7f003cd2313362989c49c0de1ff7fe715bd1c; msToken=rLEW5ugOkgF6LMV-A0lWKEhIpDDZhk-EpSQbMgGb6n4GIAOnDeVmBBmn6fC80Ktd8xNVyAFOhEYjzsSTRqNBm1g6i08iuYGAqDTg_XXiSO3MmTXlY3w6; live_can_add_dy_2_desktop=%221%22"
33 | # }
34 |
35 | url = 'https://v.douyin.com/kQAd2h'
36 |
37 | response = session.head(url=url, headers=headers)
38 |
39 | print(response.headers)
40 |
--------------------------------------------------------------------------------
/real/yizhibo.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/11/2 23:50
3 | # @Author: Lanser
4 | # @File:yizhibo.py
5 | # Software:PyCharm
6 |
7 | # 获取一直播的真实流媒体地址。
8 |
9 | import requests
10 | import re
11 |
12 |
13 | class YiZhiBo:
14 |
15 | def __init__(self, rid):
16 | """
17 | 一直播需要传入直播间的完整地址
18 | Args:
19 | rid:完整地址
20 | """
21 | self.rid = rid
22 | self.s = requests.Session()
23 |
24 | def get_real_url(self):
25 | try:
26 | url = 'https://www.yizhibo.com/l/{}.html'.format(self.rid)
27 | real_list = []
28 | real_dict = {}
29 | res = self.s.get(url).text
30 | play_url, name, status_code, memberid = \
31 | re.findall(r'play_url:"(.*?)"[\s\S]*nickname:"(.*?)"[\s\S]*status:(\d+)[\s\S]*memberid:(.*?),', res)[0]
32 | if status_code == '10':
33 | real_list.append({'m3u8': f'{play_url}'})
34 | real_list.append({'name': f'{name}'})
35 | real_list.append({'rid': f'{memberid}'})
36 | real_dict['yizhibo'] = real_list
37 | return real_dict
38 | else:
39 | return {}
40 | except Exception:
41 | return {}
42 |
43 |
--------------------------------------------------------------------------------
/real/yy.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # @Time:2022/10/23 0:39
3 | # @Author: Lanser
4 | # @File:yy.py
5 | # Software:PyCharm
6 |
7 | # 获取YY直播的真实流媒体地址。https://www.yy.com/1349606469
8 | # 默认获取最高画质
9 | import time
10 |
11 | import requests
12 | import re
13 | import json
14 | import sys
15 | # sys.path.insert(0, '..')
16 | from .requests_code import requests_get_code
17 | from multiprocessing.pool import ThreadPool
18 |
19 |
20 | class YY:
21 |
22 | def __init__(self, rid):
23 | self.rid = rid
24 |
25 | def get_real_url(self):
26 |
27 | headers_web = {
28 | 'referer': f'https://www.yy.com/',
29 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 '
30 | }
31 | headers = {
32 | 'referer': f'https://wap.yy.com/mobileweb/{self.rid}',
33 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
34 | 'Chrome/95.0.4638.69 Safari/537.36 '
35 | }
36 | name = ''
37 | real_lists = []
38 | real_list = []
39 | thread_list = []
40 | real_dict = {}
41 | try:
42 | url = 'https://www.yy.com/{}'.format(self.rid)
43 | response = requests.get(url, headers=headers_web, timeout=2)
44 | name = re.findall('
(.*?)
', response.text)[0]
45 | except:
46 | name = self.rid
47 |
48 | def room_id_():
49 | url = 'https://www.yy.com/{}'.format(self.rid)
50 | with requests.Session() as s:
51 | response = s.get(url, headers=headers_web, timeout=2)
52 | if response.status_code == 200:
53 | room_id = re.findall('ssid : "(\d+)', response.text)[0]
54 | return room_id
55 |
56 | try:
57 | room_url = f'https://interface.yy.com/hls/new/get/{self.rid}/{self.rid}/1200?source=wapyy&callback='
58 | with requests.Session() as s:
59 | res = s.get(room_url, headers=headers, timeout=2)
60 | except:
61 | try:
62 | room_id = room_id_()
63 | room_url = f'https://interface.yy.com/hls/new/get/{room_id}/{room_id}/1200?source=wapyy&callback='
64 | with requests.Session() as s:
65 | res = s.get(room_url, headers=headers, timeout=2)
66 | except:
67 | return {}
68 |
69 | if res.status_code == 200:
70 | data = json.loads(res.text[1:-1])
71 | if data.get('hls', 0):
72 | xa = data['audio']
73 | xv = data['video']
74 | xv = re.sub(r'_0_\d+_0', '_0_0_0', xv)
75 | url = f'https://interface.yy.com/hls/get/stream/15013/{xv}/15013/{xa}?source=h5player&type=m3u8'
76 | res = s.get(url).json()
77 | real_url = res['hls']
78 | real_lists.append({'hls': real_url})
79 | if real_lists:
80 | pool = ThreadPool(processes=int(len(real_lists)))
81 | for real_ in real_lists:
82 | thread_list.append(pool.apply_async(requests_get_code, args=(real_,)))
83 | for thread in thread_list:
84 | return_dict = thread.get()
85 | if return_dict:
86 | real_list.append(return_dict)
87 | if real_list:
88 | real_list.append({'name': name})
89 | real_list.append({'rid': self.rid})
90 | real_dict['yy'] = real_list
91 | return real_dict
92 | return {}
93 |
94 | # if __name__ == '__main__':
95 | # r = '54880976'
96 | # yy = YY(r)
97 | #
98 | # # r = input('输入YY直播房间号:\n')
99 | # print( yy.get_real_url())
100 |
--------------------------------------------------------------------------------