diff --git "a/DD\347\233\221\346\216\247\345\256\244.py" "b/DD\347\233\221\346\216\247\345\256\244.py" index 90f0d69..77706d0 100644 --- "a/DD\347\233\221\346\216\247\345\256\244.py" +++ "b/DD\347\233\221\346\216\247\345\256\244.py" @@ -9,7 +9,14 @@ # https://stackoverflow.com/questions/54110504/dynlib-dll-was-no-found-when-the-application-was-frozen-when-i-make-a-exe-fil import ctypes -import os, sys, json, time, shutil, logging,platform, threading +import os +import sys +import json +import time +import shutil +import logging +import platform +import threading from PyQt5.QtWidgets import * # QAction,QFileDialog from PyQt5.QtGui import * # QIcon,QPixmap from PyQt5.QtCore import * # QSize @@ -89,11 +96,13 @@ def __init__(self, title): self.setWindowTitle(title) self.setObjectName(f'dock-{title}') self.setFloating(False) - self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) + self.setAllowedAreas(Qt.LeftDockWidgetArea | + Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) class StartLiveWindow(QWidget): """开播提醒弹窗""" + def __init__(self): super(StartLiveWindow, self).__init__() self.setWindowTitle('开播提醒') @@ -140,7 +149,8 @@ def __init__(self): layout.addWidget(okButton, 2, 3, 1, 1) def selectCopyPath(self): - savePath = QFileDialog.getExistingDirectory(self, "选择备份缓存路径", None, QFileDialog.ShowDirsOnly) + savePath = QFileDialog.getExistingDirectory( + self, "选择备份缓存路径", None, QFileDialog.ShowDirsOnly) if savePath: self.savePathEdit.setText(savePath) @@ -151,6 +161,7 @@ def sendSetting(self): class Version(QWidget): """版本说明窗口""" + def __init__(self): super(Version, self).__init__() self.resize(350, 150) @@ -171,11 +182,13 @@ def __init__(self): layout.addWidget(checkButton, 0, 2, 1, 1) def checkUpdate(self): - QDesktopServices.openUrl(QUrl(r'https://github.com/zhimingshenjun/DD_Monitor/releases/tag/DD_Monitor')) + QDesktopServices.openUrl( + QUrl(r'https://github.com/zhimingshenjun/DD_Monitor/releases/tag/DD_Monitor')) class HotKey(QWidget): """热键说明窗口""" + def __init__(self): super(HotKey, self).__init__() self.resize(350, 150) @@ -188,6 +201,7 @@ def __init__(self): class DumpConfig(QThread): """导出配置""" + def __init__(self, config): super(DumpConfig, self).__init__() self.config = config @@ -195,14 +209,16 @@ def __init__(self, config): def run(self): try: - configJSONPath = os.path.join(application_path, r'utils/config.json') + configJSONPath = os.path.join( + application_path, r'utils/config.json') with codecs.open(configJSONPath, 'w', encoding='utf-8') as f: f.write(json.dumps(self.config, ensure_ascii=False)) except: logging.exception('config.json 写入失败') try: # 备份 防止存储config时崩溃 - configJSONPath = os.path.join(application_path, r'utils/config_备份%d.json' % self.backupNumber) + configJSONPath = os.path.join( + application_path, r'utils/config_备份%d.json' % self.backupNumber) self.backupNumber += 1 if self.backupNumber == 4: self.backupNumber = 1 @@ -216,12 +232,14 @@ def run(self): class CheckDanmmuProvider(QThread): """检查弹幕服务器域名解析状态""" + def __init__(self): - super(CheckDanmmuProvider,self).__init__() - + super(CheckDanmmuProvider, self).__init__() + def run(self): try: - anwsers = dns.resolver.resolve('broadcastlv.chat.bilibili.com', 'A') + anwsers = dns.resolver.resolve( + 'broadcastlv.chat.bilibili.com', 'A') danmu_ip = anwsers[0].to_text() logging.info("弹幕IP: %s" % danmu_ip) except Exception as e: @@ -231,6 +249,7 @@ def run(self): class MainWindow(QMainWindow): """主窗口""" + def __init__(self, cacheFolder, progressBar, progressText): super(MainWindow, self).__init__() self.setWindowTitle('DD监控室') @@ -240,7 +259,8 @@ def __init__(self, cacheFolder, progressBar, progressText): self.cacheFolder = cacheFolder # ---- json 配置文件加载 ---- - self.configJSONPath = os.path.join(application_path, r'utils/config.json') + self.configJSONPath = os.path.join( + application_path, r'utils/config.json') self.config = {} # 读取默认的 config if os.path.exists(self.configJSONPath): @@ -253,19 +273,21 @@ def __init__(self, cacheFolder, progressBar, progressText): logging.exception('json 配置读取失败') self.config = {} # 读取config失败 尝试读取备份 - if not self.config: + if not self.config: for backupNumber in [1, 2, 3]: # 备份预设123 - self.configJSONPath = os.path.join(application_path, r'utils/config_备份%d.json' % backupNumber) + self.configJSONPath = os.path.join( + application_path, r'utils/config_备份%d.json' % backupNumber) if os.path.exists(self.configJSONPath): # 如果备份文件存在 if os.path.getsize(self.configJSONPath): # 如过备份文件有效 try: - self.config = json.loads(open(self.configJSONPath).read()) + self.config = json.loads( + open(self.configJSONPath).read()) break except: logging.exception('json 备份配置读取失败') self.config = {} # 如果能成功读取到config文件 - if self.config: + if self.config: while len(self.config['player']) < 16: self.config['player'].append('0') while len(self.config['volume']) < 16: @@ -294,7 +316,8 @@ def __init__(self, cacheFolder, progressBar, progressText): self.config['translator'] = [True] * 16 for index, textSetting in enumerate(self.config['danmu']): if type(textSetting) == bool: - self.config['danmu'][index] = [textSetting, 20, 1, 7, 0, '【 [ {'] + self.config['danmu'][index] = [ + textSetting, 20, 1, 7, 0, '【 [ {'] if 'hardwareDecode' not in self.config: self.config['hardwareDecode'] = True if 'maxCacheSize' not in self.config: @@ -313,14 +336,16 @@ def __init__(self, cacheFolder, progressBar, progressText): danmuConfig.append(10) else: # 默认和备份 json 配置均读取失败 self.config = { - 'roomid': {'21396545': False, '21402309': False, '22384516': False, '8792912': False}, # 置顶显示 + # 置顶显示 + 'roomid': {'21396545': False, '21402309': False, '22384516': False, '8792912': False}, 'layout': [(0, 0, 1, 1), (0, 1, 1, 1), (1, 0, 1, 1), (1, 1, 1, 1)], 'player': ['0'] * 16, 'quality': [80] * 16, 'audioChannel': [0] * 16, 'muted': [1] * 16, 'volume': [50] * 16, - 'danmu': [[True, 50, 1, 7, 0, '【 [ {', 10]] * 16, # 显示,透明,横向,纵向,类型,同传字符,字体大小 + # 显示,透明,横向,纵向,类型,同传字符,字体大小 + 'danmu': [[True, 50, 1, 7, 0, '【 [ {', 10]] * 16, 'globalVolume': 30, 'control': True, 'hardwareDecode': True, @@ -342,24 +367,26 @@ def __init__(self, cacheFolder, progressBar, progressText): self.layoutSettingPanel.layoutConfig.connect(self.changeLayout) self.version = Version() self.cacheSetting = CacheSetting() - self.cacheSetting.maxCacheEdit.setText(str(self.config['maxCacheSize'] // 1024000)) + self.cacheSetting.maxCacheEdit.setText( + str(self.config['maxCacheSize'] // 1024000)) self.cacheSetting.savePathEdit.setText(self.config['saveCachePath']) self.cacheSetting.setting.connect(self.setCache) self.hotKey = HotKey() self.pay = pay() self.startLiveWindow = StartLiveWindow() - # ---- 内嵌/弹出播放器初始化 ---- self.videoWidgetList = [] self.popVideoWidgetList = [] vlcProgressCounter = 1 for i in range(16): + if len(self.config['danmu'][i]) < 8: + self.config['danmu'][i].append(0) volume = self.config['volume'][i] progressText.setText('设置第%s个主层播放器...' % str(i + 1)) self.videoWidgetList.append(VideoWidget(i, volume, cacheFolder, textSetting=self.config['danmu'][i], - maxCacheSize = self.config['maxCacheSize'], - saveCachePath = self.config['saveCachePath'], + maxCacheSize=self.config['maxCacheSize'], + saveCachePath=self.config['saveCachePath'], startWithDanmu=self.config['startWithDanmu'], hardwareDecode=self.config['hardwareDecode'])) vlcProgressCounter += 1 @@ -377,16 +404,18 @@ def __init__(self, cacheFolder, progressBar, progressText): self.videoWidgetList[i].hideBarKey.connect(self.openControlPanel) self.videoWidgetList[i].fullScreenKey.connect(self.fullScreen) self.videoWidgetList[i].muteExceptKey.connect(self.muteExcept) - self.videoWidgetList[i].mediaMute(self.config['muted'][i], emit=False) + self.videoWidgetList[i].mediaMute( + self.config['muted'][i], emit=False) self.videoWidgetList[i].slider.setValue(self.config['volume'][i]) self.videoWidgetList[i].quality = self.config['quality'][i] self.videoWidgetList[i].audioChannel = self.config['audioChannel'][i] self.popVideoWidgetList.append(VideoWidget(i + 16, volume, cacheFolder, True, '悬浮窗', [1280, 720], maxCacheSize=self.config['maxCacheSize'], - saveCachePath = self.config['saveCachePath'], + saveCachePath=self.config['saveCachePath'], startWithDanmu=self.config['startWithDanmu'], hardwareDecode=self.config['hardwareDecode'])) - self.popVideoWidgetList[i].closePopWindow.connect(self.closePopWindow) + self.popVideoWidgetList[i].closePopWindow.connect( + self.closePopWindow) vlcProgressCounter += 1 progressBar.setValue(vlcProgressCounter) progressText.setText('设置第%s个悬浮窗播放器...' % str(i + 1)) @@ -406,22 +435,32 @@ def __init__(self, cacheFolder, progressBar, progressText): self.play = PushButton(self.style().standardIcon(QStyle.SP_MediaPause)) self.play.clicked.connect(self.globalMediaPlay) self.controlBarLayout.addWidget(self.play, 0, 0, 1, 1) - self.reload = PushButton(self.style().standardIcon(QStyle.SP_BrowserReload)) + self.reload = PushButton( + self.style().standardIcon(QStyle.SP_BrowserReload)) self.reload.clicked.connect(self.globalMediaReload) self.controlBarLayout.addWidget(self.reload, 0, 1, 1, 1) - self.stop = PushButton(self.style().standardIcon(QStyle.SP_DialogCancelButton)) + self.stop = PushButton(self.style().standardIcon( + QStyle.SP_DialogCancelButton)) self.stop.clicked.connect(self.globalMediaStop) self.controlBarLayout.addWidget(self.stop, 0, 2, 1, 1) # 全局弹幕设置 self.danmuOption = TextOpation() self.danmuOption.setWindowTitle('全局弹幕窗设置') - self.danmuOption.opacitySlider.value.connect(self.setGlobalDanmuOpacity) - self.danmuOption.horizontalCombobox.currentIndexChanged.connect(self.setGlobalHorizontalPercent) - self.danmuOption.verticalCombobox.currentIndexChanged.connect(self.setGlobalVerticalPercent) - self.danmuOption.translateCombobox.currentIndexChanged.connect(self.setGlobalTranslateBrowser) - self.danmuOption.translateFitler.textChanged.connect(self.setGlobalTranslateFilter) - self.danmuOption.fontSizeCombox.currentIndexChanged.connect(self.setGlobalFontSize) + self.danmuOption.opacitySlider.value.connect( + self.setGlobalDanmuOpacity) + self.danmuOption.horizontalCombobox.currentIndexChanged.connect( + self.setGlobalHorizontalPercent) + self.danmuOption.verticalCombobox.currentIndexChanged.connect( + self.setGlobalVerticalPercent) + self.danmuOption.translateCombobox.currentIndexChanged.connect( + self.setGlobalTranslateBrowser) + self.danmuOption.showEnterRoom.currentIndexChanged.connect( + self.setGlobalShowEnterRoom) + self.danmuOption.translateFitler.textChanged.connect( + self.setGlobalTranslateFilter) + self.danmuOption.fontSizeCombox.currentIndexChanged.connect( + self.setGlobalFontSize) # self.danmuButton = ToolButton(self.style().standardIcon(QStyle.SP_FileDialogDetailedView)) icon = QIcon() icon.addFile(os.path.join(application_path, 'utils/danmu.png')) @@ -434,7 +473,8 @@ def __init__(self, cacheFolder, progressBar, progressText): # 全局静音 self.globalMuteToken = False - self.volumeButton = PushButton(self.style().standardIcon(QStyle.SP_MediaVolume)) + self.volumeButton = PushButton( + self.style().standardIcon(QStyle.SP_MediaVolume)) self.volumeButton.clicked.connect(self.globalMediaMute) self.controlBarLayout.addWidget(self.volumeButton, 1, 0, 1, 1) # 全局音量滑条 @@ -467,7 +507,8 @@ def __init__(self, cacheFolder, progressBar, progressText): self.liverPanel.addLiverRoomWidget.getHotLiver.start() self.liverPanel.addToWindow.connect(self.addCoverToPlayer) self.liverPanel.dumpConfig.connect(self.dumpConfig.start) # 保存config - self.liverPanel.refreshIDList.connect(self.refreshPlayerStatus) # 刷新播放器 + self.liverPanel.refreshIDList.connect( + self.refreshPlayerStatus) # 刷新播放器 self.liverPanel.startLiveList.connect(self.startLiveTip) # 开播提醒 self.scrollArea.setWidget(self.liverPanel) self.scrollArea.multipleTimes.connect(self.changeLiverPanelLayout) @@ -480,39 +521,57 @@ def __init__(self, cacheFolder, progressBar, progressText): # ---- 菜单设置 ---- self.optionMenu = self.menuBar().addMenu('设置') self.controlBarLayoutToken = self.config['control'] - layoutConfigAction = QAction('布局方式', self, triggered=self.openLayoutSetting) + layoutConfigAction = QAction( + '布局方式', self, triggered=self.openLayoutSetting) self.optionMenu.addAction(layoutConfigAction) globalQualityMenu = self.optionMenu.addMenu('全局画质 ►') - originQualityAction = QAction('原画', self, triggered=lambda: self.globalQuality(10000)) + originQualityAction = QAction( + '原画', self, triggered=lambda: self.globalQuality(10000)) globalQualityMenu.addAction(originQualityAction) - bluerayQualityAction = QAction('蓝光', self, triggered=lambda: self.globalQuality(400)) + bluerayQualityAction = QAction( + '蓝光', self, triggered=lambda: self.globalQuality(400)) globalQualityMenu.addAction(bluerayQualityAction) - highQualityAction = QAction('超清', self, triggered=lambda: self.globalQuality(250)) + highQualityAction = QAction( + '超清', self, triggered=lambda: self.globalQuality(250)) globalQualityMenu.addAction(highQualityAction) - lowQualityAction = QAction('流畅', self, triggered=lambda: self.globalQuality(80)) + lowQualityAction = QAction( + '流畅', self, triggered=lambda: self.globalQuality(80)) globalQualityMenu.addAction(lowQualityAction) + onlyAudio = QAction( + '仅播声音', self, triggered=lambda: self.globalQuality(-1)) + globalQualityMenu.addAction(onlyAudio) globalAudioMenu = self.optionMenu.addMenu('全局音效 ►') - audioOriginAction = QAction('原始音效', self, triggered=lambda: self.globalAudioChannel(0)) + audioOriginAction = QAction( + '原始音效', self, triggered=lambda: self.globalAudioChannel(0)) globalAudioMenu.addAction(audioOriginAction) - audioDolbysAction = QAction('杜比音效', self, triggered=lambda: self.globalAudioChannel(5)) + audioDolbysAction = QAction( + '杜比音效', self, triggered=lambda: self.globalAudioChannel(5)) globalAudioMenu.addAction(audioDolbysAction) hardDecodeMenu = self.optionMenu.addMenu('解码方案 ►') - hardDecodeAction = QAction('硬解', self, triggered=lambda: self.setDecode(True)) + hardDecodeAction = QAction( + '硬解', self, triggered=lambda: self.setDecode(True)) hardDecodeMenu.addAction(hardDecodeAction) - softDecodeAction = QAction('软解', self, triggered=lambda: self.setDecode(False)) + softDecodeAction = QAction( + '软解', self, triggered=lambda: self.setDecode(False)) hardDecodeMenu.addAction(softDecodeAction) startLiveSetting = self.optionMenu.addMenu('开播提醒 ►') - enableStartLive = QAction('打开', self, triggered=lambda: self.setStartLive(True)) + enableStartLive = QAction( + '打开', self, triggered=lambda: self.setStartLive(True)) startLiveSetting.addAction(enableStartLive) - disableStartLive = QAction('关闭', self, triggered=lambda: self.setStartLive(False)) + disableStartLive = QAction( + '关闭', self, triggered=lambda: self.setStartLive(False)) startLiveSetting.addAction(disableStartLive) - cacheSizeSetting = QAction('缓存设置', self, triggered=self.openCacheSetting) + cacheSizeSetting = QAction( + '缓存设置', self, triggered=self.openCacheSetting) self.optionMenu.addAction(cacheSizeSetting) - startWithDanmuSetting = QAction('自动加载弹幕设置', self, triggered=self.openStartWithDanmuSetting) + startWithDanmuSetting = QAction( + '自动加载弹幕设置', self, triggered=self.openStartWithDanmuSetting) self.optionMenu.addAction(startWithDanmuSetting) - controlPanelAction = QAction('显示 / 隐藏控制条(H)', self, triggered=self.openControlPanel) + controlPanelAction = QAction( + '显示 / 隐藏控制条(H)', self, triggered=self.openControlPanel) self.optionMenu.addAction(controlPanelAction) - self.fullScreenAction = QAction('全屏(F) / 退出(Esc)', self, triggered=self.fullScreen) + self.fullScreenAction = QAction( + '全屏(F) / 退出(Esc)', self, triggered=self.fullScreen) self.optionMenu.addAction(self.fullScreenAction) exportConfig = QAction('导出预设', self, triggered=self.exportConfig) self.optionMenu.addAction(exportConfig) @@ -528,7 +587,8 @@ def __init__(self, cacheFolder, progressBar, progressText): versionAction = QAction('检查版本', self, triggered=self.openVersion) self.versionMenu.addAction(versionAction) otherDDMenu = self.versionMenu.addMenu('其他DD系列工具 ►') - DDSubtitleAction = QAction('DD烤肉机', self, triggered=self.openDDSubtitle) + DDSubtitleAction = QAction( + 'DD烤肉机', self, triggered=self.openDDSubtitle) otherDDMenu.addAction(DDSubtitleAction) DDThanksAction = QAction('DD答谢机', self, triggered=self.openDDThanks) otherDDMenu.addAction(DDThanksAction) @@ -592,17 +652,21 @@ def deleteMedia(self, id): def exchangeMedia(self, info): # 交换播放窗口的函数 fromID, fromRoomID, toID, toRoomID = info # 交换数据 - fromVideo, toVideo = self.videoWidgetList[fromID], self.videoWidgetList[toID] # 待交换的两个控件 + # 待交换的两个控件 + fromVideo, toVideo = self.videoWidgetList[fromID], self.videoWidgetList[toID] fromVideo.id, toVideo.id = toID, fromID # 交换id - fromVideo.topLabel.setText(fromVideo.topLabel.text().replace('窗口%s' % (fromID + 1), '窗口%s' % (toID + 1))) - toVideo.topLabel.setText(toVideo.topLabel.text().replace('窗口%s' % (toID + 1), '窗口%s' % (fromID + 1))) + fromVideo.topLabel.setText(fromVideo.topLabel.text().replace( + '窗口%s' % (fromID + 1), '窗口%s' % (toID + 1))) + toVideo.topLabel.setText(toVideo.topLabel.text().replace( + '窗口%s' % (toID + 1), '窗口%s' % (fromID + 1))) fromWidth, fromHeight = fromVideo.width(), fromVideo.height() toWidth, toHeight = toVideo.width(), toVideo.height() if 3 < abs(fromWidth - toWidth) or 3 < abs(fromHeight - toHeight): # 有主次关系的播放窗交换同时交换音量和弹幕设置 fromMuted = 2 if fromVideo.player.audio_get_mute() else 1 toMuted = 2 if toVideo.player.audio_get_mute() else 1 - fromVolume, toVolume = fromVideo.player.audio_get_volume(), toVideo.player.audio_get_volume() # 音量值 + fromVolume, toVolume = fromVideo.player.audio_get_volume( + ), toVideo.player.audio_get_volume() # 音量值 fromVideo.mediaMute(toMuted) # 交换静音设置 fromVideo.setVolume(toVolume) # 交换音量 toVideo.mediaMute(fromMuted) @@ -610,11 +674,18 @@ def exchangeMedia(self, info): # 交换播放窗口的函数 fromVideo.textSetting, toVideo.textSetting = toVideo.textSetting, fromVideo.textSetting # 交换弹幕设置 for videoWidget in [fromVideo, toVideo]: - color = str(hex(int(videoWidget.textSetting[1] / 101 * 256)))[2:] + '000000' - videoWidget.textBrowser.textBrowser.setStyleSheet('background-color:#%s' % color) # 设置透明度 - videoWidget.textBrowser.transBrowser.setStyleSheet('background-color:#%s' % color) - videoWidget.horiPercent = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][videoWidget.textSetting[2]] - videoWidget.vertPercent = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][videoWidget.textSetting[3]] + color = str( + hex(int(videoWidget.textSetting[1] / 101 * 256)))[2:] + '000000' + videoWidget.textBrowser.textBrowser.setStyleSheet( + 'background-color:#%s' % color) # 设置透明度 + videoWidget.textBrowser.transBrowser.setStyleSheet( + 'background-color:#%s' % color) + videoWidget.textBrowser.msgsBrowser.setStyleSheet( + 'background-color:#%s' % color) + videoWidget.horiPercent = [ + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][videoWidget.textSetting[2]] + videoWidget.vertPercent = [ + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][videoWidget.textSetting[3]] if videoWidget.textSetting[4] == 0: # 显示弹幕和同传 videoWidget.textBrowser.textBrowser.show() videoWidget.textBrowser.transBrowser.show() @@ -625,16 +696,26 @@ def exchangeMedia(self, info): # 交换播放窗口的函数 videoWidget.textBrowser.textBrowser.hide() videoWidget.textBrowser.transBrowser.show() videoWidget.filters = videoWidget.textSetting[5].split(' ') + if videoWidget.textSetting[7] < 3: + videoWidget.textBrowser.msgsBrowser.show() + elif videoWidget.textSetting[7] == 3: + videoWidget.textBrowser.msgsBrowser.hide() size = videoWidget.textSetting[6] - videoWidget.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', size + 5, QFont.Bold)) - videoWidget.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', size + 5, QFont.Bold)) - - self.videoWidgetList[fromID], self.videoWidgetList[toID] = toVideo, fromVideo # 交换控件列表 + videoWidget.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', size + 5, QFont.Bold)) + videoWidget.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', size + 5, QFont.Bold)) + videoWidget.textBrowser.msgsBrowser.setFont( + QFont('Microsoft JhengHei', size + 5, QFont.Bold)) + + # 交换控件列表 + self.videoWidgetList[fromID], self.videoWidgetList[toID] = toVideo, fromVideo self.config['player'][toID] = fromRoomID # 记录config self.config['player'][fromID] = toRoomID self.dumpConfig.start() # self.changeLayout(self.config['layout']) # 刷新layout - fromLayout, toLayout = self.config['layout'][fromID], self.config['layout'][toID] # 用新的方法直接交换两个窗口 + # 用新的方法直接交换两个窗口 + fromLayout, toLayout = self.config['layout'][fromID], self.config['layout'][toID] y, x, h, w = fromLayout self.mainLayout.addWidget(toVideo, y, x, h, w) y, x, h, w = toLayout @@ -647,7 +728,8 @@ def exchangeMedia(self, info): # 交换播放窗口的函数 # toVideo.textBrowser.move(fromVideoPos + QPoint(fromWidth * toVideo.deltaX, fromHeight * toVideo.deltaY)) def clearLiverPanel(self): # 清空卡片槽 - reply = QMessageBox.information(self, '清空卡片槽', '注意:是否要清空卡片槽?', QMessageBox.Yes | QMessageBox.No) + reply = QMessageBox.information( + self, '清空卡片槽', '注意:是否要清空卡片槽?', QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: # 确认用户操作 self.liverPanel.deleteAll() @@ -677,7 +759,7 @@ def setAudioChannel(self, info): def popWindow(self, info): # 悬浮窗播放 id, roomID, quality, showMax, startWithDanmu = info - logging.info("%s 进入悬浮窗模式, 弹幕?: %s" % (roomID, startWithDanmu)) + logging.info("%s 进入悬浮窗模式, 弹幕?: %s" % (roomID, startWithDanmu)) self.popVideoWidgetList[id].roomID = roomID self.popVideoWidgetList[id].quality = quality self.popVideoWidgetList[id].resize(1280, 720) @@ -719,10 +801,12 @@ def globalMediaReload(self): def globalMediaMute(self): if self.globalMuteToken: force = 1 - self.volumeButton.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume)) + self.volumeButton.setIcon( + self.style().standardIcon(QStyle.SP_MediaVolume)) else: force = 2 - self.volumeButton.setIcon(self.style().standardIcon(QStyle.SP_MediaVolumeMuted)) + self.volumeButton.setIcon( + self.style().standardIcon(QStyle.SP_MediaVolumeMuted)) self.globalMuteToken = not self.globalMuteToken for videoWidget in self.videoWidgetList: videoWidget.mediaMute(force) @@ -731,7 +815,8 @@ def globalMediaMute(self): def globalSetVolume(self, value): for videoWidget in self.videoWidgetList: - videoWidget.player.audio_set_volume(int(value * videoWidget.volumeAmplify)) + videoWidget.player.audio_set_volume( + int(value * videoWidget.volumeAmplify)) videoWidget.volume = value videoWidget.slider.setValue(value) self.config['volume'] = [value] * 16 @@ -751,31 +836,42 @@ def globalMediaStop(self): # danmuConfig[0] = self.globalDanmuToken def setGlobalDanmuOpacity(self, value): - if value < 7: value = 7 # 最小透明度 + if value < 7: + value = 7 # 最小透明度 opacity = int(value / 101 * 256) color = str(hex(opacity))[2:] + '000000' for videoWidget in self.videoWidgetList + self.popVideoWidgetList: videoWidget.textSetting[1] = value # 记录设置 - videoWidget.textBrowser.textBrowser.setStyleSheet('background-color:#%s' % color) - videoWidget.textBrowser.transBrowser.setStyleSheet('background-color:#%s' % color) + videoWidget.textBrowser.textBrowser.setStyleSheet( + 'background-color:#%s' % color) + videoWidget.textBrowser.transBrowser.setStyleSheet( + 'background-color:#%s' % color) + videoWidget.textBrowser.msgsBrowser.setStyleSheet( + 'background-color:#%s' % color) def setGlobalHorizontalPercent(self, index): # 设置弹幕框水平宽度 for videoWidget in self.videoWidgetList + self.popVideoWidgetList: videoWidget.textSetting[2] = index - videoWidget.horiPercent = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][index] # 记录横向占比 + videoWidget.horiPercent = [ + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][index] # 记录横向占比 width = videoWidget.width() * videoWidget.horiPercent - videoWidget.textBrowser.resize(width, videoWidget.textBrowser.height()) + videoWidget.textBrowser.resize( + width, videoWidget.textBrowser.height()) videoWidget.textBrowser.textBrowser.verticalScrollBar().setValue(100000000) videoWidget.textBrowser.transBrowser.verticalScrollBar().setValue(100000000) + videoWidget.textBrowser.msgsBrowser.verticalScrollBar().setValue(100000000) def setGlobalVerticalPercent(self, index): # 设置弹幕框垂直高度 for videoWidget in self.videoWidgetList + self.popVideoWidgetList: videoWidget.textSetting[3] = index - videoWidget.vertPercent = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][index] # 记录纵向占比 + videoWidget.vertPercent = [ + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][index] # 记录纵向占比 height = videoWidget.height() * videoWidget.vertPercent - videoWidget.textBrowser.resize(videoWidget.textBrowser.width(), height) + videoWidget.textBrowser.resize( + videoWidget.textBrowser.width(), height) videoWidget.textBrowser.textBrowser.verticalScrollBar().setValue(100000000) videoWidget.textBrowser.transBrowser.verticalScrollBar().setValue(100000000) + videoWidget.textBrowser.msgsBrowser.verticalScrollBar().setValue(100000000) def setGlobalTranslateBrowser(self, index): for videoWidget in self.videoWidgetList + self.popVideoWidgetList: @@ -793,6 +889,17 @@ def setGlobalTranslateBrowser(self, index): height = videoWidget.height() * videoWidget.vertPercent videoWidget.textBrowser.resize(width, height) + def setGlobalShowEnterRoom(self, index): + for videoWidget in self.videoWidgetList + self.popVideoWidgetList: + videoWidget.textSetting[7] = index + if index < 3: # 显示礼物和进入信息] + videoWidget.textBrowser.msgsBrowser.show() + elif index == 3: # 隐藏窗口 + videoWidget.textBrowser.msgsBrowser.hide() + width = videoWidget.width() * videoWidget.horiPercent + height = videoWidget.height() * videoWidget.vertPercent + videoWidget.textBrowser.resize(width, height) + def setGlobalTranslateFilter(self, filterWords): for videoWidget in self.videoWidgetList + self.popVideoWidgetList: videoWidget.textSetting[5] = filterWords @@ -801,8 +908,12 @@ def setGlobalTranslateFilter(self, filterWords): def setGlobalFontSize(self, index): for videoWidget in self.videoWidgetList + self.popVideoWidgetList: videoWidget.textSetting[6] = index - videoWidget.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', index + 5, QFont.Bold)) - videoWidget.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', index + 5, QFont.Bold)) + videoWidget.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', index + 5, QFont.Bold)) + videoWidget.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', index + 5, QFont.Bold)) + videoWidget.textBrowser.msgsBrowser.setFont( + QFont('Microsoft JhengHei', index + 5, QFont.Bold)) def globalQuality(self, quality): for videoWidget in self.videoWidgetList + self.popVideoWidgetList: @@ -848,16 +959,20 @@ def openVersion(self): self.version.show() def openGithub(self): - QDesktopServices.openUrl(QUrl(r'https://github.com/zhimingshenjun/DD_Monitor')) + QDesktopServices.openUrl( + QUrl(r'https://github.com/zhimingshenjun/DD_Monitor')) def openBilibili(self): - QDesktopServices.openUrl(QUrl(r'https://www.bilibili.com/video/BV14v411s7WE')) + QDesktopServices.openUrl( + QUrl(r'https://www.bilibili.com/video/BV14v411s7WE')) def openDDSubtitle(self): - QDesktopServices.openUrl(QUrl(r'https://www.bilibili.com/video/BV1p5411b7o7')) + QDesktopServices.openUrl( + QUrl(r'https://www.bilibili.com/video/BV1p5411b7o7')) def openDDThanks(self): - QDesktopServices.openUrl(QUrl(r'https://www.bilibili.com/video/BV1Di4y1L7T2')) + QDesktopServices.openUrl( + QUrl(r'https://www.bilibili.com/video/BV1Di4y1L7T2')) def openCacheSetting(self): self.cacheSetting.hide() @@ -867,19 +982,21 @@ def setCache(self, setting): maxCache, savePath = setting intergerMaxCache = int(maxCache) if intergerMaxCache <= 0: - QMessageBox.warning(self,'大小错误' ,'缓存大小不能小于为0GB!', QMessageBox.Ok ) + QMessageBox.warning(self, '大小错误', '缓存大小不能小于为0GB!', QMessageBox.Ok) return - self.config['maxCacheSize'] = intergerMaxCache * 1024000 + self.config['maxCacheSize'] = intergerMaxCache * 1024000 self.config['saveCachePath'] = savePath self.dumpConfig.start() - QMessageBox.information(self, '缓存设置更改', '设置成功 重启监控室后生效', QMessageBox.Ok) + QMessageBox.information( + self, '缓存设置更改', '设置成功 重启监控室后生效', QMessageBox.Ok) def openStartWithDanmuSetting(self): items = ('加载(推荐,默认。但可能增加网络压力,可能会被限流。)', '不加载') defulatSelection = 0 if not self.config['startWithDanmu']: defulatSelection = 1 - selection, okPressed = QInputDialog.getItem(self,"设置启动时是否加载弹幕", "加载选项", items, defulatSelection, False) + selection, okPressed = QInputDialog.getItem( + self, "设置启动时是否加载弹幕", "加载选项", items, defulatSelection, False) if okPressed: trueDanmu = (selection == items[0]) self.config['startWithDanmu'] = trueDanmu @@ -915,7 +1032,8 @@ def checkMousePos(self): def moveEvent(self, QMoveEvent): # 捕获主窗口moveEvent来实时同步弹幕机位置 for videoWidget in self.videoWidgetList: - videoPos = videoWidget.mapToGlobal(videoWidget.videoFrame.pos()) # videoFrame的坐标要转成globalPos + videoPos = videoWidget.mapToGlobal( + videoWidget.videoFrame.pos()) # videoFrame的坐标要转成globalPos videoWidget.textBrowser.move(videoPos + videoWidget.textPosDelta) videoWidget.textPosDelta = videoWidget.textBrowser.pos() - videoPos @@ -1013,15 +1131,18 @@ def saveDockLayout(self): def loadDockLayout(self): if 'geometry' in self.config: - geometry = QByteArray().fromBase64(self.config['geometry'].encode('ASCII')) + geometry = QByteArray().fromBase64( + self.config['geometry'].encode('ASCII')) self.restoreGeometry(geometry) if 'windowState' in self.config: - windowState = QByteArray().fromBase64(self.config['windowState'].encode('ASCII')) + windowState = QByteArray().fromBase64( + self.config['windowState'].encode('ASCII')) self.restoreState(windowState) logging.info(f'restore Window layout.') def exportConfig(self): - self.savePath = QFileDialog.getSaveFileName(self, "选择保存路径", 'DD监控室预设', "*.json")[0] + self.savePath = QFileDialog.getSaveFileName( + self, "选择保存路径", 'DD监控室预设', "*.json")[0] if self.savePath: # 保存路径有效 try: with codecs.open(self.savePath, 'w', encoding='utf-8') as f: @@ -1053,7 +1174,8 @@ def importConfig(self): self.config = config while len(self.config['player']) < 16: self.config['player'].append('0') - self.config['player'] = list(map(str, self.config['player'])) + self.config['player'] = list( + map(str, self.config['player'])) if type(self.config['roomid']) == list: roomIDList = self.config['roomid'] self.config['roomid'] = {} @@ -1069,7 +1191,8 @@ def importConfig(self): self.config['translator'] = [True] * 16 for index, textSetting in enumerate(self.config['danmu']): if type(textSetting) == bool: - self.config['danmu'][index] = [textSetting, 20, 1, 7, 0, '【 [ {'] + self.config['danmu'][index] = [ + textSetting, 20, 1, 7, 0, '【 [ {'] if 'hardwareDecode' not in self.config: self.config['hardwareDecode'] = True if 'maxCacheSize' not in self.config: @@ -1087,7 +1210,8 @@ def importConfig(self): if len(danmuConfig) == 6: danmuConfig.append(10) self.liverPanel.addLiverRoomList(self.config['roomid']) - QMessageBox.information(self, '导入预设', '导入完成', QMessageBox.Ok) + QMessageBox.information( + self, '导入预设', '导入完成', QMessageBox.Ok) def muteExcept(self): if not self.soloToken: @@ -1104,7 +1228,8 @@ def muteExcept(self): def closePopWindow(self, info): id, roomID = info - if not self.videoWidgetList[id - 16].isHidden() and roomID != '0' and roomID: # 房间号有效 + # 房间号有效 + if not self.videoWidgetList[id - 16].isHidden() and roomID != '0' and roomID: self.videoWidgetList[id - 16].roomID = roomID self.videoWidgetList[id - 16].mediaReload() self.config['player'][id - 16] = roomID @@ -1162,10 +1287,12 @@ def startLiveTip(self, startLiveList): # 开播提醒 os.mkdir(logsPath) try: # 尝试清除上次缓存 如果失败则跳过 for cacheFolder in os.listdir(cachePath): - shutil.rmtree(os.path.join(application_path, 'cache/%s' % cacheFolder)) + shutil.rmtree(os.path.join( + application_path, 'cache/%s' % cacheFolder)) except: logging.exception('清除缓存失败') - cacheFolder = os.path.join(application_path, 'cache/%d' % time.time()) # 初始化缓存文件夹 + cacheFolder = os.path.join( + application_path, 'cache/%d' % time.time()) # 初始化缓存文件夹 os.mkdir(cacheFolder) # 应用qss @@ -1192,9 +1319,10 @@ def startLiveTip(self, startLiveList): # 开播提醒 logging.info(f"vlc version: {vlc.libvlc_get_version()}") # 欢迎页面 - splash = QSplashScreen(QPixmap(os.path.join(application_path, 'utils/splash.jpg')), Qt.WindowStaysOnTopHint) + splash = QSplashScreen(QPixmap(os.path.join( + application_path, 'utils/splash.jpg')), Qt.WindowStaysOnTopHint) progressBar = QProgressBar(splash) - progressBar.setMaximum(32) # 16 * 2个播放器, 0 - 17 index + progressBar.setMaximum(32) # 16 * 2个播放器, 0 - 17 index progressBar.setGeometry(0, splash.height() - 20, splash.width(), 20) progressText = QLabel(splash) progressText.setText("加载中...") diff --git a/VideoWidget.py b/VideoWidget.py index ad3a7fa..3f55194 100644 --- a/VideoWidget.py +++ b/VideoWidget.py @@ -1,4 +1,5 @@ -import requests, json +import requests +import json from PyQt5.QtWidgets import * # QAction,QFileDialog from PyQt5.QtGui import * # QIcon,QPixmap from PyQt5.QtCore import * # QSize @@ -43,12 +44,14 @@ def __init__(self, setting=[20, 2, 6, 0, '【 [ {']): layout.addWidget(self.opacitySlider, 0, 1, 1, 1) layout.addWidget(QLabel('窗体横向占比'), 1, 0, 1, 1) self.horizontalCombobox = QComboBox() - self.horizontalCombobox.addItems(['10%', '15%', '20%', '25%', '30%', '35%', '40%', '45%', '50%']) + self.horizontalCombobox.addItems( + ['10%', '15%', '20%', '25%', '30%', '35%', '40%', '45%', '50%']) self.horizontalCombobox.setCurrentIndex(setting[1]) layout.addWidget(self.horizontalCombobox, 1, 1, 1, 1) layout.addWidget(QLabel('窗体纵向占比'), 2, 0, 1, 1) self.verticalCombobox = QComboBox() - self.verticalCombobox.addItems(['50%', '55%', '60%', '65%', '70%', '75%', '80%', '85%', '90%', '95%', '100%']) + self.verticalCombobox.addItems( + ['50%', '55%', '60%', '65%', '70%', '75%', '80%', '85%', '90%', '95%', '100%']) self.verticalCombobox.setCurrentIndex(setting[2]) layout.addWidget(self.verticalCombobox, 2, 1, 1, 1) layout.addWidget(QLabel('单独同传窗口'), 3, 0, 1, 1) @@ -78,11 +81,13 @@ def __init__(self, parent, id): self.bar.moveSignal.connect(self.moveWindow) layout.addWidget(self.bar, 0, 0, 1, 10) - self.optionButton = ToolButton(self.style().standardIcon(QStyle.SP_FileDialogDetailedView)) + self.optionButton = ToolButton( + self.style().standardIcon(QStyle.SP_FileDialogDetailedView)) self.optionButton.clicked.connect(self.optionWidget.show) # 弹出设置菜单 layout.addWidget(self.optionButton, 0, 8, 1, 1) - self.closeButton = ToolButton(self.style().standardIcon(QStyle.SP_TitleBarCloseButton)) + self.closeButton = ToolButton( + self.style().standardIcon(QStyle.SP_TitleBarCloseButton)) self.closeButton.clicked.connect(self.userClose) layout.addWidget(self.closeButton, 0, 9, 1, 1) @@ -148,8 +153,10 @@ def wheelEvent(self, event): # 把进度条的滚轮事件去了 用啥子滚 def updateValue(self, QPoint): value = QPoint.x() - if value > 100: value = 100 - elif value < 0: value = 0 + if value > 100: + value = 100 + elif value < 0: + value = 0 self.setValue(value) self.value.emit(value) @@ -186,12 +193,58 @@ def setConfig(self, roomID, quality): self.roomID = roomID self.quality = quality + def getStreamUrl(self): + url = "https://api.live.bilibili.com/xlive/app-room/v2/index/getRoomPlayInfo" + onlyAudio = self.quality < 0 + params = { + "appkey": "iVGUTjsxvpLeuDCf", + "build": 6215200, + "c_locale": "zh_CN", + "channel": "bili", + "codec": 0, + "device": "android", + "device_name": "VTR-AL00", + "dolby": 1, + "format": "0,2", + "free_type": 0, + "http": 1, + "mask": 0, + "mobi_app": "android", + "network": "wifi", + "no_playurl": 0, + "only_audio": bool(onlyAudio), + "only_video": 0, + "platform": "android", + "play_type": 0, + "protocol": "0,1", + "qn": (onlyAudio and 10000) or (not onlyAudio and self.quality), + "room_id": self.roomID, + "s_locale": "zh_CN", + "statistics": "{\"appId\":1,\"platform\":3,\"version\":\"6.21.5\",\"abtest\":\"\"}", + "ts": int(time.time()) + } + r = requests.get(url, params=params) + j = r.json() + baseUrl = j['data']['playurl_info']['playurl']['stream'][0]['format'][0]['codec'][0]['base_url'] + extra = j['data']['playurl_info']['playurl']['stream'][0]['format'][0]['codec'][0]['url_info'][0]['extra'] + host = j['data']['playurl_info']['playurl']['stream'][0]['format'][0]['codec'][0]['url_info'][0]['host'] + # let base_url = jqXHR.responseJSON.data.playurl_info.playurl.stream[0].format[0].codec[0].base_url + # let extra = jqXHR.responseJSON.data.playurl_info.playurl.stream[0].format[0].codec[0].url_info[0].extra + # let host = jqXHR.responseJSON.data.playurl_info.playurl.stream[0].format[0].codec[0].url_info[0].host + # streamURL = host + base_url + extra + streamUrl = host + baseUrl + extra + return streamUrl + def run(self): - api = r'https://api.live.bilibili.com/room/v1/Room/playUrl?cid=%s&platform=web&qn=%s' % (self.roomID, self.quality) - r = requests.get(api) + # api = r'https://api.live.bilibili.com/room/v1/Room/playUrl?cid=%s&platform=web&qn=%s' % ( + # self.roomID, self.quality) + # r = requests.get(api) try: # print(json.loads(r.text)['data']['durl'][0]['url']) - self.url.emit(QMediaContent(QUrl(json.loads(r.text)['data']['durl'][0]['url']))) + # self.url.emit(QMediaContent( + # QUrl(json.loads(r.text)['data']['durl'][0]['url']))) + self.url.emit(QMediaContent( + QUrl(self.getStreamUrl()))) except Exception as e: print(str(e)) @@ -218,8 +271,10 @@ def __init__(self, id, top=False, title='', resize=[], textSetting=[True, 20, 2, self.fullScreen = False self.top = top self.textSetting = textSetting - self.horiPercent = [0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5][self.textSetting[2]] - self.vertPercent = [0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1][self.textSetting[3]] + self.horiPercent = [0.1, 0.15, 0.2, 0.25, 0.3, + 0.35, 0.4, 0.45, 0.5][self.textSetting[2]] + self.vertPercent = [0.5, 0.55, 0.6, 0.65, 0.7, 0.75, + 0.8, 0.85, 0.9, 0.95, 1][self.textSetting[3]] self.filters = textSetting[5].split(' ') self.opacity = 100 if top: @@ -275,10 +330,12 @@ def __init__(self, id, top=False, title='', resize=[], textSetting=[True, 20, 2, self.play = PushButton(self.style().standardIcon(QStyle.SP_MediaPause)) self.play.clicked.connect(self.mediaPlay) frameLayout.addWidget(self.play) - self.reload = PushButton(self.style().standardIcon(QStyle.SP_BrowserReload)) + self.reload = PushButton( + self.style().standardIcon(QStyle.SP_BrowserReload)) self.reload.clicked.connect(self.mediaReload) frameLayout.addWidget(self.reload) - self.volume = PushButton(self.style().standardIcon(QStyle.SP_MediaVolume)) + self.volume = PushButton( + self.style().standardIcon(QStyle.SP_MediaVolume)) self.volume.clicked.connect(self.mediaMute) frameLayout.addWidget(self.volume) self.slider = Slider() @@ -288,7 +345,8 @@ def __init__(self, id, top=False, title='', resize=[], textSetting=[True, 20, 2, self.danmuButton = PushButton(text='弹') self.danmuButton.clicked.connect(self.showDanmu) frameLayout.addWidget(self.danmuButton) - self.stop = PushButton(self.style().standardIcon(QStyle.SP_DialogCancelButton)) + self.stop = PushButton(self.style().standardIcon( + QStyle.SP_DialogCancelButton)) self.stop.clicked.connect(self.mediaStop) frameLayout.addWidget(self.stop) @@ -297,20 +355,30 @@ def __init__(self, id, top=False, title='', resize=[], textSetting=[True, 20, 2, self.textBrowser = TextBrowser(self, self.id) self.setDanmuOpacity(self.textSetting[1]) # 设置弹幕透明度 - self.textBrowser.optionWidget.opacitySlider.setValue(self.textSetting[1]) # 设置选项页透明条 - self.textBrowser.optionWidget.opacitySlider.value.connect(self.setDanmuOpacity) + self.textBrowser.optionWidget.opacitySlider.setValue( + self.textSetting[1]) # 设置选项页透明条 + self.textBrowser.optionWidget.opacitySlider.value.connect( + self.setDanmuOpacity) self.setHorizontalPercent(self.textSetting[2]) # 设置横向占比 - self.textBrowser.optionWidget.horizontalCombobox.setCurrentIndex(self.textSetting[2]) # 设置选项页占比框 - self.textBrowser.optionWidget.horizontalCombobox.currentIndexChanged.connect(self.setHorizontalPercent) + self.textBrowser.optionWidget.horizontalCombobox.setCurrentIndex( + self.textSetting[2]) # 设置选项页占比框 + self.textBrowser.optionWidget.horizontalCombobox.currentIndexChanged.connect( + self.setHorizontalPercent) self.setVerticalPercent(self.textSetting[3]) # 设置横向占比 - self.textBrowser.optionWidget.verticalCombobox.setCurrentIndex(self.textSetting[3]) # 设置选项页占比框 - self.textBrowser.optionWidget.verticalCombobox.currentIndexChanged.connect(self.setVerticalPercent) + self.textBrowser.optionWidget.verticalCombobox.setCurrentIndex( + self.textSetting[3]) # 设置选项页占比框 + self.textBrowser.optionWidget.verticalCombobox.currentIndexChanged.connect( + self.setVerticalPercent) self.setTranslateBrowser(self.textSetting[4]) - self.textBrowser.optionWidget.translateCombobox.setCurrentIndex(self.textSetting[4]) # 设置同传窗口 - self.textBrowser.optionWidget.translateCombobox.currentIndexChanged.connect(self.setTranslateBrowser) + self.textBrowser.optionWidget.translateCombobox.setCurrentIndex( + self.textSetting[4]) # 设置同传窗口 + self.textBrowser.optionWidget.translateCombobox.currentIndexChanged.connect( + self.setTranslateBrowser) self.setTranslateFilter(self.textSetting[5]) # 同传过滤字符 - self.textBrowser.optionWidget.translateFitler.setText(self.textSetting[5]) - self.textBrowser.optionWidget.translateFitler.textChanged.connect(self.setTranslateFilter) + self.textBrowser.optionWidget.translateFitler.setText( + self.textSetting[5]) + self.textBrowser.optionWidget.translateFitler.textChanged.connect( + self.setTranslateFilter) self.textBrowser.closeSignal.connect(self.closeDanmu) # self.translator = TextBrowser(self, self.id, '同传') @@ -325,34 +393,46 @@ def __init__(self, id, top=False, title='', resize=[], textSetting=[True, 20, 2, self.fullScreenTimer.timeout.connect(self.hideFrame) def setDanmuOpacity(self, value): - if value < 7: value = 7 # 最小透明度 + if value < 7: + value = 7 # 最小透明度 self.textSetting[1] = value # 记录设置 value = int(value / 101 * 256) color = str(hex(value))[2:] + '000000' - self.textBrowser.textBrowser.setStyleSheet('background-color:#%s' % color) - self.textBrowser.transBrowser.setStyleSheet('background-color:#%s' % color) + self.textBrowser.textBrowser.setStyleSheet( + 'background-color:#%s' % color) + self.textBrowser.transBrowser.setStyleSheet( + 'background-color:#%s' % color) def setHorizontalPercent(self, index): # 设置弹幕框水平宽度 self.textSetting[2] = index - self.horiPercent = [0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5][index] # 记录横向占比 + self.horiPercent = [0.1, 0.15, 0.2, 0.25, 0.3, + 0.35, 0.4, 0.45, 0.5][index] # 记录横向占比 width = self.width() * self.horiPercent self.textBrowser.resize(width, self.textBrowser.height()) if width > 300: - self.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', 20, QFont.Bold)) - self.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', 20, QFont.Bold)) + self.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', 20, QFont.Bold)) + self.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', 20, QFont.Bold)) elif 100 < width <= 300: - self.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', width // 20 + 5, QFont.Bold)) - self.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', width // 20 + 5, QFont.Bold)) + self.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', width // 20 + 5, QFont.Bold)) + self.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', width // 20 + 5, QFont.Bold)) else: - self.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', 10, QFont.Bold)) - self.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', 10, QFont.Bold)) + self.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', 10, QFont.Bold)) + self.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', 10, QFont.Bold)) self.textBrowser.textBrowser.verticalScrollBar().setValue(100000000) self.textBrowser.transBrowser.verticalScrollBar().setValue(100000000) def setVerticalPercent(self, index): # 设置弹幕框垂直高度 self.textSetting[3] = index - self.vertPercent = [0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1][index] # 记录纵向占比 - self.textBrowser.resize(self.textBrowser.width(), self.height() * self.vertPercent) + self.vertPercent = [0.5, 0.55, 0.6, 0.65, 0.7, + 0.75, 0.8, 0.85, 0.9, 0.95, 1][index] # 记录纵向占比 + self.textBrowser.resize(self.textBrowser.width(), + self.height() * self.vertPercent) self.textBrowser.textBrowser.verticalScrollBar().setValue(100000000) self.textBrowser.transBrowser.verticalScrollBar().setValue(100000000) @@ -363,9 +443,11 @@ def setTranslateBrowser(self, index): # else: # print(1) # self.textBrowser.transBrowser.setFixedHeight(0) - self.textBrowser.transBrowser.show() if not index else self.textBrowser.transBrowser.hide() # 显示/隐藏同传 + self.textBrowser.transBrowser.show( + ) if not index else self.textBrowser.transBrowser.hide() # 显示/隐藏同传 self.textBrowser.adjustSize() - self.resize(self.width() * self.horiPercent, self.height() * self.vertPercent) + self.resize(self.width() * self.horiPercent, + self.height() * self.vertPercent) def setTranslateFilter(self, filterWords): self.filters = filterWords.split(' ') @@ -410,10 +492,11 @@ def dropFile(self, text): self.mediaReload() self.textBrowser.textBrowser.clear() self.textBrowser.transBrowser.clear() - elif 'exchange' in text: # 交换窗口 + elif 'exchange' in text: # 交换窗口 fromID, fromRoomID = map(int, text.split(':')[1:]) if fromID != self.id: - self.exchangeMedia.emit([self.id, fromRoomID, fromID, self.roomID]) + self.exchangeMedia.emit( + [self.id, fromRoomID, fromID, self.roomID]) self.roomID = fromRoomID self.mediaReload() # self.textBrowser.textBrowser.clear() @@ -425,39 +508,49 @@ def rightMouseClicked(self, event): chooseQuality = menu.addMenu('选择画质') originQuality = chooseQuality.addAction('原画') if self.quality == 10000: - originQuality.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + originQuality.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) bluerayQuality = chooseQuality.addAction('蓝光') if self.quality == 400: - bluerayQuality.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + bluerayQuality.setIcon( + self.style().standardIcon(QStyle.SP_DialogApplyButton)) highQuality = chooseQuality.addAction('超清') if self.quality == 250: - highQuality.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + highQuality.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) lowQuality = chooseQuality.addAction('流畅') if self.quality == 80: - lowQuality.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + lowQuality.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) if not self.top: # 非弹出类悬浮窗 popWindow = menu.addAction('悬浮窗播放') else: opacityMenu = menu.addMenu('调节透明度') percent100 = opacityMenu.addAction('100%') if self.opacity == 100: - percent100.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent100.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) percent80 = opacityMenu.addAction('80%') if self.opacity == 80: - percent80.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent80.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) percent60 = opacityMenu.addAction('60%') if self.opacity == 60: - percent60.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent60.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) percent40 = opacityMenu.addAction('40%') if self.opacity == 40: - percent40.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent40.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) percent20 = opacityMenu.addAction('20%') if self.opacity == 20: - percent20.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent20.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) action = menu.exec_(self.mapToGlobal(event.pos())) if action == openBrowser: if self.roomID: - QDesktopServices.openUrl(QUrl(r'https://live.bilibili.com/%s' % self.roomID)) + QDesktopServices.openUrl( + QUrl(r'https://live.bilibili.com/%s' % self.roomID)) elif action == originQuality: self.changeQuality.emit([self.id, 10000]) self.quality = 10000 @@ -476,7 +569,8 @@ def rightMouseClicked(self, event): self.mediaReload() if not self.top: if action == popWindow: - self.popWindow.emit([self.id, self.roomID, self.quality, False]) + self.popWindow.emit( + [self.id, self.roomID, self.quality, False]) self.mediaPlay(1) # 暂停播放 elif self.top: if action == percent100: @@ -500,14 +594,20 @@ def resizeEvent(self, QEvent): width = self.width() * self.horiPercent self.textBrowser.resize(width, self.height() * self.vertPercent) if width > 300: - self.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', 20, QFont.Bold)) - self.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', 20, QFont.Bold)) + self.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', 20, QFont.Bold)) + self.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', 20, QFont.Bold)) elif 100 < width <= 300: - self.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', width // 20 + 5, QFont.Bold)) - self.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', width // 20 + 5, QFont.Bold)) + self.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', width // 20 + 5, QFont.Bold)) + self.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', width // 20 + 5, QFont.Bold)) else: - self.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', 10, QFont.Bold)) - self.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', 10, QFont.Bold)) + self.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', 10, QFont.Bold)) + self.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', 10, QFont.Bold)) # if not self.textBrowser.transBrowser.isHidden(): # self.textBrowser.transBrowser.setFixedHeight(self.textBrowser.height() / 3) self.textBrowser.move(0, 0) @@ -558,16 +658,20 @@ def mediaPlay(self, force=0): def mediaMute(self, force=0, emit=True): if force == 1: self.player.setMuted(False) - self.volume.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume)) + self.volume.setIcon( + self.style().standardIcon(QStyle.SP_MediaVolume)) elif force == 2: self.player.setMuted(True) - self.volume.setIcon(self.style().standardIcon(QStyle.SP_MediaVolumeMuted)) + self.volume.setIcon(self.style().standardIcon( + QStyle.SP_MediaVolumeMuted)) elif self.player.isMuted(): self.player.setMuted(False) - self.volume.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume)) + self.volume.setIcon( + self.style().standardIcon(QStyle.SP_MediaVolume)) else: self.player.setMuted(True) - self.volume.setIcon(self.style().standardIcon(QStyle.SP_MediaVolumeMuted)) + self.volume.setIcon(self.style().standardIcon( + QStyle.SP_MediaVolumeMuted)) if emit: self.mutedChanged.emit([self.id, self.player.isMuted()]) @@ -615,7 +719,8 @@ def setTitle(self): title = '未定义的直播间' uname = '未定义' else: - r = requests.get(r'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=%s' % self.roomID) + r = requests.get( + r'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=%s' % self.roomID) data = json.loads(r.text) if data['message'] == '房间已加密': title = '房间已加密' diff --git a/VideoWidget_vlc.py b/VideoWidget_vlc.py index 91dbd10..94ce753 100644 --- a/VideoWidget_vlc.py +++ b/VideoWidget_vlc.py @@ -3,7 +3,12 @@ 包含视频缓存播放、音量管理、弹幕窗 遇到不确定的播放状态就调用MediaReload()函数 我已经在里面写好了全部的处理 会自动获取直播间状态并进行对应的刷新操作 """ -import requests, json, os, time, shutil, random +import requests +import json +import os +import time +import shutil +import random from PyQt5.QtWidgets import * # QAction,QFileDialog from PyQt5.QtGui import * # QIcon,QPixmap from PyQt5.QtCore import * # QSize @@ -23,6 +28,7 @@ class PushButton(QPushButton): """文字/图标按钮""" + def __init__(self, icon='', text=''): super(PushButton, self).__init__() self.setFixedSize(30, 30) @@ -64,11 +70,55 @@ def setConfig(self, roomID, quality): self.roomID = roomID self.quality = quality + def getStreamUrl(self): + url = "https://api.live.bilibili.com/xlive/app-room/v2/index/getRoomPlayInfo" + onlyAudio = self.quality < 0 + params = { + "appkey": "iVGUTjsxvpLeuDCf", + "build": 6215200, + "c_locale": "zh_CN", + "channel": "bili", + "codec": 0, + "device": "android", + "device_name": "VTR-AL00", + "dolby": 1, + "format": "0,2", + "free_type": 0, + "http": 1, + "mask": 0, + "mobi_app": "android", + "network": "wifi", + "no_playurl": 0, + "only_audio": int(onlyAudio), + "only_video": 0, + "platform": "android", + "play_type": 0, + "protocol": "0,1", + "qn": (onlyAudio and 10000) or (not onlyAudio and self.quality), + "room_id": self.roomID, + "s_locale": "zh_CN", + "statistics": "{\"appId\":1,\"platform\":3,\"version\":\"6.21.5\",\"abtest\":\"\"}", + "ts": int(time.time()) + } + r = requests.get(url, params=params) + j = r.json() + baseUrl = j['data']['playurl_info']['playurl']['stream'][0]['format'][0]['codec'][0]['base_url'] + extra = j['data']['playurl_info']['playurl']['stream'][0]['format'][0]['codec'][0]['url_info'][0]['extra'] + host = j['data']['playurl_info']['playurl']['stream'][0]['format'][0]['codec'][0]['url_info'][0]['host'] + # let base_url = jqXHR.responseJSON.data.playurl_info.playurl.stream[0].format[0].codec[0].base_url + # let extra = jqXHR.responseJSON.data.playurl_info.playurl.stream[0].format[0].codec[0].url_info[0].extra + # let host = jqXHR.responseJSON.data.playurl_info.playurl.stream[0].format[0].codec[0].url_info[0].host + # streamURL = host + base_url + extra + streamUrl = host + baseUrl + extra + return streamUrl + def run(self): - api = r'https://api.live.bilibili.com/room/v1/Room/playUrl?cid=%s&platform=web&qn=%s' % (self.roomID, self.quality) - r = requests.get(api) + # api = r'https://api.live.bilibili.com/room/v1/Room/playUrl?cid=%s&platform=web&qn=%s' % ( + # self.roomID, self.quality) + # r = requests.get(api) try: - url = json.loads(r.text)['data']['durl'][0]['url'] + # url = json.loads(r.text)['data']['durl'][0]['url'] + url = self.getStreamUrl() fileName = '%s/%s.flv' % (self.cacheFolder, self.id) download = requests.get(url, stream=True, headers=header) logging.debug(download.headers) @@ -94,8 +144,11 @@ def run(self): self.cacheVideo.close() time.sleep(0.1) # 等待0.1秒确保关闭 try: - if self.saveCachePath and os.path.exists(self.saveCachePath) and os.path.getsize(fileName): # 如果备份路径有效 - renameFile = '%s/%s.flv' % (self.cacheFolder, random.randint(50, 10000000)) # 随机命名防止重名 + # 如果备份路径有效 + if self.saveCachePath and os.path.exists(self.saveCachePath) and os.path.getsize(fileName): + # 随机命名防止重名 + renameFile = '%s/%s.flv' % (self.cacheFolder, + random.randint(50, 10000000)) os.rename(fileName, renameFile) self.copyFile.emit(renameFile) # 发射信号备份缓存 else: @@ -155,6 +208,7 @@ def run(self): class ExportTip(QWidget): """导出提示""" + def __init__(self): super(ExportTip, self).__init__() self.resize(600, 100) @@ -181,7 +235,7 @@ class VideoWidget(QFrame): closePopWindow = pyqtSignal(list) # 关闭悬浮窗 def __init__(self, id, volume, cacheFolder, top=False, title='', resize=[], - textSetting=[True, 20, 2, 6, 0, '【 [ {', 10], maxCacheSize=2048000, + textSetting=[True, 20, 2, 6, 0, '【 [ {', 10, 0], maxCacheSize=2048000, saveCachePath='', startWithDanmu=True, hardwareDecode=True): super(VideoWidget, self).__init__() self.setAcceptDrops(True) @@ -219,10 +273,13 @@ def __init__(self, id, volume, cacheFolder, top=False, title='', resize=[], if top: self.setWindowFlags(Qt.Window) else: - self.setStyleSheet('#video{border-width:1px;border-style:solid;border-color:gray}') + self.setStyleSheet( + '#video{border-width:1px;border-style:solid;border-color:gray}') self.textSetting = textSetting - self.horiPercent = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][self.textSetting[2]] - self.vertPercent = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][self.textSetting[3]] + self.horiPercent = [0.1, 0.2, 0.3, 0.4, 0.5, + 0.6, 0.7, 0.8, 0.9, 1.0][self.textSetting[2]] + self.vertPercent = [0.1, 0.2, 0.3, 0.4, 0.5, + 0.6, 0.7, 0.8, 0.9, 1.0][self.textSetting[3]] self.filters = textSetting[5].split(' ') self.opacity = 100 if top: @@ -237,29 +294,47 @@ def __init__(self, id, volume, cacheFolder, top=False, title='', resize=[], layout.setContentsMargins(0, 0, 0, 0) # ---- 弹幕机 ---- - self.textBrowser = TextBrowser(self) # 必须赶在resizeEvent和moveEvent之前初始化textbrowser + # 必须赶在resizeEvent和moveEvent之前初始化textbrowser + self.textBrowser = TextBrowser(self) self.setDanmuOpacity(self.textSetting[1]) # 设置弹幕透明度 - self.textBrowser.optionWidget.opacitySlider.setValue(self.textSetting[1]) # 设置选项页透明条 - self.textBrowser.optionWidget.opacitySlider.value.connect(self.setDanmuOpacity) + self.textBrowser.optionWidget.opacitySlider.setValue( + self.textSetting[1]) # 设置选项页透明条 + self.textBrowser.optionWidget.opacitySlider.value.connect( + self.setDanmuOpacity) self.setHorizontalPercent(self.textSetting[2]) # 设置横向占比 - self.textBrowser.optionWidget.horizontalCombobox.setCurrentIndex(self.textSetting[2]) # 设置选项页占比框 - self.textBrowser.optionWidget.horizontalCombobox.currentIndexChanged.connect(self.setHorizontalPercent) + self.textBrowser.optionWidget.horizontalCombobox.setCurrentIndex( + self.textSetting[2]) # 设置选项页占比框 + self.textBrowser.optionWidget.horizontalCombobox.currentIndexChanged.connect( + self.setHorizontalPercent) self.setVerticalPercent(self.textSetting[3]) # 设置横向占比 - self.textBrowser.optionWidget.verticalCombobox.setCurrentIndex(self.textSetting[3]) # 设置选项页占比框 - self.textBrowser.optionWidget.verticalCombobox.currentIndexChanged.connect(self.setVerticalPercent) + self.textBrowser.optionWidget.verticalCombobox.setCurrentIndex( + self.textSetting[3]) # 设置选项页占比框 + self.textBrowser.optionWidget.verticalCombobox.currentIndexChanged.connect( + self.setVerticalPercent) self.setTranslateBrowser(self.textSetting[4]) - self.textBrowser.optionWidget.translateCombobox.setCurrentIndex(self.textSetting[4]) # 设置同传窗口 - self.textBrowser.optionWidget.translateCombobox.currentIndexChanged.connect(self.setTranslateBrowser) + self.textBrowser.optionWidget.translateCombobox.setCurrentIndex( + self.textSetting[4]) # 设置同传窗口 + self.textBrowser.optionWidget.translateCombobox.currentIndexChanged.connect( + self.setTranslateBrowser) self.setTranslateFilter(self.textSetting[5]) # 同传过滤字符 - self.textBrowser.optionWidget.translateFitler.setText(self.textSetting[5]) - self.textBrowser.optionWidget.translateFitler.textChanged.connect(self.setTranslateFilter) + self.textBrowser.optionWidget.translateFitler.setText( + self.textSetting[5]) + self.textBrowser.optionWidget.translateFitler.textChanged.connect( + self.setTranslateFilter) self.setFontSize(self.textSetting[6]) # 设置弹幕字体大小 - self.textBrowser.optionWidget.fontSizeCombox.setCurrentIndex(self.textSetting[6]) - self.textBrowser.optionWidget.fontSizeCombox.currentIndexChanged.connect(self.setFontSize) + self.textBrowser.optionWidget.fontSizeCombox.setCurrentIndex( + self.textSetting[6]) + self.textBrowser.optionWidget.fontSizeCombox.currentIndexChanged.connect( + self.setFontSize) + self.setMsgsBrowser(self.textSetting[7]) + self.textBrowser.optionWidget.showEnterRoom.setCurrentIndex( + self.textSetting[7]) # 设置礼物和进入提示窗口 + self.textBrowser.optionWidget.showEnterRoom.currentIndexChanged.connect( + self.setMsgsBrowser) self.textBrowser.closeSignal.connect(self.closeDanmu) self.textBrowser.moveSignal.connect(self.moveTextBrowser) - if not self.startWithDanmu: # 如果启动隐藏被设置,隐藏弹幕机 + if not self.startWithDanmu: # 如果启动隐藏被设置,隐藏弹幕机 self.textSetting[0] = False self.textBrowser.hide() @@ -311,11 +386,13 @@ def __init__(self, id, volume, cacheFolder, top=False, title='', resize=[], self.play.clicked.connect(self.mediaPlay) frameLayout.addWidget(self.play) # 刷新 - self.reload = PushButton(self.style().standardIcon(QStyle.SP_BrowserReload)) + self.reload = PushButton( + self.style().standardIcon(QStyle.SP_BrowserReload)) self.reload.clicked.connect(self.mediaReload) frameLayout.addWidget(self.reload) # 音量开关 - self.volumeButton = PushButton(self.style().standardIcon(QStyle.SP_MediaVolume)) + self.volumeButton = PushButton( + self.style().standardIcon(QStyle.SP_MediaVolume)) self.volumeButton.clicked.connect(self.mediaMute) frameLayout.addWidget(self.volumeButton) # 音量滑条 @@ -328,13 +405,15 @@ def __init__(self, id, volume, cacheFolder, top=False, title='', resize=[], self.danmuButton.clicked.connect(self.showDanmu) frameLayout.addWidget(self.danmuButton) # 关闭窗口 - self.stop = PushButton(self.style().standardIcon(QStyle.SP_DialogCancelButton)) + self.stop = PushButton(self.style().standardIcon( + QStyle.SP_DialogCancelButton)) self.stop.clicked.connect(self.mediaStop) frameLayout.addWidget(self.stop) # ---- IO 交互设置 ---- # 单开线程获取视频流 - self.getMediaURL = GetMediaURL(self.id, cacheFolder, maxCacheSize, saveCachePath) + self.getMediaURL = GetMediaURL( + self.id, cacheFolder, maxCacheSize, saveCachePath) self.getMediaURL.cacheName.connect(self.setMedia) self.getMediaURL.copyFile.connect(self.copyCache) self.getMediaURL.downloadError.connect(self.mediaReload) @@ -385,7 +464,8 @@ def checkPlayStatus(self): # 播放卡住了 else: self.player.set_xwindow(self.videoFrame.winId()) if self.hardwareDecode: - self.media = self.instance.media_new(self.cacheName, 'avcodec-hw=dxva2') # 设置vlc并硬解播放 + self.media = self.instance.media_new( + self.cacheName, 'avcodec-hw=dxva2') # 设置vlc并硬解播放 else: self.media = self.instance.media_new(self.cacheName) # 软解 self.player.set_media(self.media) # 设置视频 @@ -411,17 +491,23 @@ def initTextPos(self): # 初始化弹幕机位置 self.moveTimer.stop() def setDanmuOpacity(self, value): - if value < 7: value = 7 # 最小透明度 + if value < 7: + value = 7 # 最小透明度 self.textSetting[1] = value # 记录设置 value = int(value / 101 * 256) color = str(hex(value))[2:] + '000000' - self.textBrowser.textBrowser.setStyleSheet('background-color:#%s' % color) - self.textBrowser.transBrowser.setStyleSheet('background-color:#%s' % color) + self.textBrowser.textBrowser.setStyleSheet( + 'background-color:#%s' % color) + self.textBrowser.transBrowser.setStyleSheet( + 'background-color:#%s' % color) + self.textBrowser.msgsBrowser.setStyleSheet( + 'background-color:#%s' % color) self.setDanmu.emit() def setHorizontalPercent(self, index): # 设置弹幕框水平宽度 self.textSetting[2] = index - self.horiPercent = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][index] # 记录横向占比 + self.horiPercent = [0.1, 0.2, 0.3, 0.4, 0.5, + 0.6, 0.7, 0.8, 0.9, 1.0][index] # 记录横向占比 width = self.width() * self.horiPercent self.textBrowser.resize(width, self.textBrowser.height()) # if width > 240: @@ -435,14 +521,18 @@ def setHorizontalPercent(self, index): # 设置弹幕框水平宽度 # self.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', 10, QFont.Bold)) self.textBrowser.textBrowser.verticalScrollBar().setValue(100000000) self.textBrowser.transBrowser.verticalScrollBar().setValue(100000000) + self.textBrowser.msgsBrowser.verticalScrollBar().setValue(100000000) self.setDanmu.emit() def setVerticalPercent(self, index): # 设置弹幕框垂直高度 self.textSetting[3] = index - self.vertPercent = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0][index] # 记录纵向占比 - self.textBrowser.resize(self.textBrowser.width(), self.height() * self.vertPercent) + self.vertPercent = [0.1, 0.2, 0.3, 0.4, 0.5, + 0.6, 0.7, 0.8, 0.9, 1.0][index] # 记录纵向占比 + self.textBrowser.resize(self.textBrowser.width(), + self.height() * self.vertPercent) self.textBrowser.textBrowser.verticalScrollBar().setValue(100000000) self.textBrowser.transBrowser.verticalScrollBar().setValue(100000000) + self.textBrowser.msgsBrowser.verticalScrollBar().setValue(100000000) self.setDanmu.emit() def setTranslateBrowser(self, index): @@ -456,7 +546,18 @@ def setTranslateBrowser(self, index): elif index == 2: # 只显示同传 self.textBrowser.textBrowser.hide() self.textBrowser.transBrowser.show() - self.textBrowser.resize(self.width() * self.horiPercent, self.height() * self.vertPercent) + self.textBrowser.resize( + self.width() * self.horiPercent, self.height() * self.vertPercent) + self.setDanmu.emit() + + def setMsgsBrowser(self, index): + self.textSetting[7] = index + if index < 3: # 显示弹幕和同传 + self.textBrowser.msgsBrowser.show() + elif index == 3: # 只显示弹幕 + self.textBrowser.msgsBrowser.hide() + self.textBrowser.resize( + self.width() * self.horiPercent, self.height() * self.vertPercent) self.setDanmu.emit() def setTranslateFilter(self, filterWords): @@ -466,8 +567,12 @@ def setTranslateFilter(self, filterWords): def setFontSize(self, index): self.textSetting[6] = index - self.textBrowser.textBrowser.setFont(QFont('Microsoft JhengHei', index + 5, QFont.Bold)) - self.textBrowser.transBrowser.setFont(QFont('Microsoft JhengHei', index + 5, QFont.Bold)) + self.textBrowser.textBrowser.setFont( + QFont('Microsoft JhengHei', index + 5, QFont.Bold)) + self.textBrowser.transBrowser.setFont( + QFont('Microsoft JhengHei', index + 5, QFont.Bold)) + self.textBrowser.msgsBrowser.setFont( + QFont('Microsoft JhengHei', index + 5, QFont.Bold)) self.setDanmu.emit() def resizeEvent(self, QEvent): @@ -479,18 +584,22 @@ def resizeEvent(self, QEvent): self.textBrowser.resize(width, self.height() * self.vertPercent) self.textBrowser.textBrowser.verticalScrollBar().setValue(100000000) self.textBrowser.transBrowser.verticalScrollBar().setValue(100000000) + self.textBrowser.msgsBrowser.verticalScrollBar().setValue(100000000) self.moveTextBrowser() def moveEvent(self, QMoveEvent): # 理论上给悬浮窗同步弹幕机用的moveEvent 但不生效 但是又不能删掉 不然交换窗口弹幕机有bug - videoPos = self.mapToGlobal(self.videoFrame.pos()) # videoFrame的坐标要转成globalPos + # videoFrame的坐标要转成globalPos + videoPos = self.mapToGlobal(self.videoFrame.pos()) self.textBrowser.move(videoPos + self.textPosDelta) def moveTextBrowser(self, point=None): - videoPos = self.mapToGlobal(self.videoFrame.pos()) # videoFrame的坐标要转成globalPos + # videoFrame的坐标要转成globalPos + videoPos = self.mapToGlobal(self.videoFrame.pos()) if point: danmuX, danmuY = point.x(), point.y() else: - danmuX, danmuY = self.textBrowser.x(), self.textBrowser.y() # textBrowser坐标本身就是globalPos + # textBrowser坐标本身就是globalPos + danmuX, danmuY = self.textBrowser.x(), self.textBrowser.y() videoX, videoY = videoPos.x(), videoPos.y() videoW, videoH = self.videoFrame.width(), self.videoFrame.height() danmuW, danmuH = self.textBrowser.width(), self.textBrowser.height() @@ -509,7 +618,8 @@ def moveTextBrowser(self, point=None): danmuY = videoY + videoH - danmuH self.textBrowser.move(danmuX, danmuY) self.textPosDelta = self.textBrowser.pos() - videoPos - self.deltaX, self.deltaY = self.textPosDelta.x() / self.width(), self.textPosDelta.y() / self.height() + self.deltaX, self.deltaY = self.textPosDelta.x( + ) / self.width(), self.textPosDelta.y() / self.height() def enterEvent(self, QEvent): self.hoverToken = True @@ -523,7 +633,8 @@ def leaveEvent(self, QEvent): def doubleClick(self): if not self.top: # 非弹出类悬浮窗 - self.popWindow.emit([self.id, self.roomID, self.quality, True, self.startWithDanmu]) + self.popWindow.emit( + [self.id, self.roomID, self.quality, True, self.startWithDanmu]) self.mediaStop() # 直接停止播放原窗口 def leftMouseClicked(self): # 设置drag事件 发送拖动封面的房间号 @@ -536,7 +647,8 @@ def leftMouseClicked(self): # 设置drag事件 发送拖动封面的房间号 mimeData.setText('exchange:%s:%s' % (self.id, self.roomID)) drag.setMimeData(mimeData) drag.exec_() - logging.debug(f'{self.name_str} drag exchange:%s:%s' % (self.id, self.roomID)) + logging.debug(f'{self.name_str} drag exchange:%s:%s' % + (self.id, self.roomID)) def dragEnterEvent(self, QDragEnterEvent): QDragEnterEvent.accept() @@ -552,12 +664,14 @@ def dropEvent(self, QDropEvent): self.mediaReload() self.textBrowser.textBrowser.clear() self.textBrowser.transBrowser.clear() + self.textBrowser.msgsBrowser.clear() elif 'exchange' in text: # 交换窗口 fromID, fromRoomID = text.split(':')[1:] # exchange:id:roomID fromID = int(fromID) print(fromID, self.id) if fromID != self.id: - self.exchangeMedia.emit([fromID, fromRoomID, self.id, self.roomID]) + self.exchangeMedia.emit( + [fromID, fromRoomID, self.id, self.roomID]) def rightMouseClicked(self, event): menu = QMenu() @@ -566,42 +680,58 @@ def rightMouseClicked(self, event): chooseQuality = menu.addMenu('选择画质 ►') originQuality = chooseQuality.addAction('原画') if self.quality == 10000: - originQuality.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + originQuality.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) bluerayQuality = chooseQuality.addAction('蓝光') if self.quality == 400: - bluerayQuality.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + bluerayQuality.setIcon( + self.style().standardIcon(QStyle.SP_DialogApplyButton)) highQuality = chooseQuality.addAction('超清') if self.quality == 250: - highQuality.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + highQuality.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) lowQuality = chooseQuality.addAction('流畅') if self.quality == 80: - lowQuality.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + lowQuality.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) + onlyAudio = chooseQuality.addAction('仅播声音') + if self.quality == -1: + onlyAudio.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) chooseAudioChannel = menu.addMenu('选择音效 ►') chooseAudioOrigin = chooseAudioChannel.addAction('原始音效') if self.audioChannel == 0: - chooseAudioOrigin.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + chooseAudioOrigin.setIcon( + self.style().standardIcon(QStyle.SP_DialogApplyButton)) chooseAudioDolbys = chooseAudioChannel.addAction('杜比音效') if self.audioChannel == 5: - chooseAudioDolbys.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + chooseAudioDolbys.setIcon( + self.style().standardIcon(QStyle.SP_DialogApplyButton)) chooseAmplify = menu.addMenu('音量增大 ►') chooseAmp_0_5 = chooseAmplify.addAction('x 0.5') if self.volumeAmplify == 0.5: - chooseAmp_0_5.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + chooseAmp_0_5.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) chooseAmp_1 = chooseAmplify.addAction('x 1.0') if self.volumeAmplify == 1.0: - chooseAmp_1.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + chooseAmp_1.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) chooseAmp_1_5 = chooseAmplify.addAction('x 1.5') if self.volumeAmplify == 1.5: - chooseAmp_1_5.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + chooseAmp_1_5.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) chooseAmp_2 = chooseAmplify.addAction('x 2.0') if self.volumeAmplify == 2.0: - chooseAmp_2.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + chooseAmp_2.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) chooseAmp_3 = chooseAmplify.addAction('x 3.0') if self.volumeAmplify == 3.0: - chooseAmp_3.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + chooseAmp_3.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) chooseAmp_4 = chooseAmplify.addAction('x 4.0') if self.volumeAmplify == 4.0: - chooseAmp_4.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + chooseAmp_4.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) if not self.top: # 非弹出类悬浮窗 popWindow = menu.addAction('悬浮窗播放') @@ -609,37 +739,46 @@ def rightMouseClicked(self, event): opacityMenu = menu.addMenu('调节透明度 ►') percent100 = opacityMenu.addAction('100%') if self.opacity == 100: - percent100.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent100.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) percent80 = opacityMenu.addAction('80%') if self.opacity == 80: - percent80.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent80.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) percent60 = opacityMenu.addAction('60%') if self.opacity == 60: - percent60.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent60.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) percent40 = opacityMenu.addAction('40%') if self.opacity == 40: - percent40.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) + percent40.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) percent20 = opacityMenu.addAction('20%') if self.opacity == 20: - percent20.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton)) - fullScreen = menu.addAction('退出全屏') if self.isFullScreen() else menu.addAction('全屏') + percent20.setIcon(self.style().standardIcon( + QStyle.SP_DialogApplyButton)) + fullScreen = menu.addAction( + '退出全屏') if self.isFullScreen() else menu.addAction('全屏') exit = menu.addAction('退出') action = menu.exec_(self.mapToGlobal(event.pos())) if action == exportCache: if self.cacheName and os.path.exists(self.cacheName): saveName = '%s_%s' % (self.uname, self.title) - savePath = QFileDialog.getSaveFileName(self, "选择保存路径", saveName, "*.flv")[0] + savePath = QFileDialog.getSaveFileName( + self, "选择保存路径", saveName, "*.flv")[0] if savePath: # 保存路径有效 self.exportCache.setArgs(self.cacheName, savePath) self.exportCache.start() self.exportTip.setWindowTitle('导出缓存至%s' % savePath) self.exportTip.show() else: - QMessageBox.information(self, '导出失败', '未检测到有效缓存\n%s' % self.cacheName, QMessageBox.Ok) + QMessageBox.information( + self, '导出失败', '未检测到有效缓存\n%s' % self.cacheName, QMessageBox.Ok) elif action == openBrowser: if self.roomID != '0': - QDesktopServices.openUrl(QUrl(r'https://live.bilibili.com/%s' % self.roomID)) + QDesktopServices.openUrl( + QUrl(r'https://live.bilibili.com/%s' % self.roomID)) elif action == originQuality: self.changeQuality.emit([self.id, 10000]) self.quality = 10000 @@ -656,6 +795,10 @@ def rightMouseClicked(self, event): self.changeQuality.emit([self.id, 80]) self.quality = 80 self.mediaReload() + elif action == onlyAudio: + self.changeQuality.emit([self.id, -1]) + self.quality = -1 + self.mediaReload() elif action == chooseAudioOrigin: self.changeAudioChannel.emit([self.id, 0]) self.player.audio_set_channel(0) @@ -684,7 +827,8 @@ def rightMouseClicked(self, event): self.audioTimer.start() if not self.top: if action == popWindow: - self.popWindow.emit([self.id, self.roomID, self.quality, False, self.startWithDanmu]) + self.popWindow.emit( + [self.id, self.roomID, self.quality, False, self.startWithDanmu]) self.mediaStop() # 停止播放 # self.mediaPlay(1, True) # 暂停播放 elif self.top: @@ -803,19 +947,23 @@ def mediaMute(self, force=0, emit=True): if force == 1: self.muted = False # self.player.audio_set_mute(False) - self.volumeButton.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume)) + self.volumeButton.setIcon( + self.style().standardIcon(QStyle.SP_MediaVolume)) elif force == 2: self.muted = True # self.player.audio_set_mute(True) - self.volumeButton.setIcon(self.style().standardIcon(QStyle.SP_MediaVolumeMuted)) + self.volumeButton.setIcon( + self.style().standardIcon(QStyle.SP_MediaVolumeMuted)) elif self.player.audio_get_mute(): self.muted = False # self.player.audio_set_mute(False) - self.volumeButton.setIcon(self.style().standardIcon(QStyle.SP_MediaVolume)) + self.volumeButton.setIcon( + self.style().standardIcon(QStyle.SP_MediaVolume)) else: self.muted = True # self.player.audio_set_mute(True) - self.volumeButton.setIcon(self.style().standardIcon(QStyle.SP_MediaVolumeMuted)) + self.volumeButton.setIcon( + self.style().standardIcon(QStyle.SP_MediaVolumeMuted)) self.audioTimer.start() if emit: self.mutedChanged.emit([self.id, self.muted]) @@ -831,7 +979,8 @@ def mediaReload(self): self.playerRestart() self.setTitle() # 同时获取最新直播状态 if self.liveStatus == 1: # 直播中 - self.getMediaURL.setConfig(self.roomID, self.quality) # 设置房号和画质 + self.getMediaURL.setConfig( + self.roomID, self.quality) # 设置房号和画质 self.getMediaURL.start() # 开始缓存视频 self.getMediaURL.checkTimer.start(3000) # 启动监测定时器 else: @@ -841,7 +990,8 @@ def mediaStop(self, deleteMedia=True): # self.userPause = True self.oldTitle, self.oldUname = '', '' self.roomID = '0' - self.topLabel.setText((' 窗口%s 未定义的直播间' % (self.id + 1))[:20]) # 限制下直播间标题字数 + self.topLabel.setText((' 窗口%s 未定义的直播间' % + (self.id + 1))[:20]) # 限制下直播间标题字数 self.titleLabel.setText('未定义') self.playerRestart() self.play.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) @@ -873,9 +1023,11 @@ def setMedia(self, cacheName): self.danmu.start() self.textBrowser.show() if self.hardwareDecode: - self.media = self.instance.media_new(cacheName, 'avcodec-hw=any') # 设置vlc并硬解播放 + self.media = self.instance.media_new( + cacheName, 'avcodec-hw=any') # 设置vlc并硬解播放 else: - self.media = self.instance.media_new(cacheName, 'avcodec-hw=none') # 软解 vlc3.0似乎不起作用? + self.media = self.instance.media_new( + cacheName, 'avcodec-hw=none') # 软解 vlc3.0似乎不起作用? self.player.set_media(self.media) # 设置视频 self.player.audio_set_channel(self.audioChannel) self.player.play() @@ -888,8 +1040,10 @@ def copyCache(self, copyFile): for s in ['/', '\\', ':', '*', '"', '<', '>', '|', '?']: title = title.replace(s, '') uname = self.oldUname if self.oldUname else self.uname - formatTime = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time())) - self.exportCache.setArgs(copyFile, '%s/%s_%s_%s.flv' % (self.saveCachePath, uname, title, formatTime)) + formatTime = time.strftime( + '%Y-%m-%d-%H-%M-%S', time.localtime(time.time())) + self.exportCache.setArgs(copyFile, '%s/%s_%s_%s.flv' % + (self.saveCachePath, uname, title, formatTime)) self.exportCache.cut = True # 设置为剪切 self.exportCache.start() @@ -898,6 +1052,7 @@ def copyCache(self, copyFile): + playerRestart() 重置 self.player + playerFree() 释放并销毁 playerFree 实例 """ + def newPlayer(self): """实例化 player 依赖实例化的 vlc (self.instance) @@ -938,7 +1093,8 @@ def setTitle(self): self.title = '未定义的直播间' self.uname = '未定义' else: - r = requests.get(r'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=%s' % self.roomID) + r = requests.get( + r'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=%s' % self.roomID) data = json.loads(r.text) if data['message'] == '房间已加密': self.title = '房间已加密' @@ -953,14 +1109,25 @@ def setTitle(self): self.uname = data['anchor_info']['base_info']['uname'] if self.liveStatus != 1: self.uname = '(未开播)' + self.uname - self.topLabel.setText((' 窗口%s %s' % (self.id + 1, self.title))[:20]) + self.topLabel.setText( + (' 窗口%s %s' % (self.id + 1, self.title))[:20]) self.titleLabel.setText(self.uname) def playDanmu(self, message): token = False + if message.startswith("## ") or message.startswith("** "): + if self.textSetting[7] == 0: + self.textBrowser.msgsBrowser.append(message) + elif self.textSetting[7] == 1: + if message.startswith("** "): + self.textBrowser.msgsBrowser.append(message) + elif self.textSetting[7] == 2: + if message.startswith("## "): + self.textBrowser.msgsBrowser.append(message) + return for symbol in self.filters: - if symbol in message: - self.textBrowser.transBrowser.append(message) # 同传不换行 + if symbol in message[message.find(': ')+2:]: + self.textBrowser.transBrowser.append(message) token = True break if not token: diff --git a/danmu.py b/danmu.py index e6fc33d..f82e7dd 100644 --- a/danmu.py +++ b/danmu.py @@ -24,6 +24,7 @@ def mouseMoveEvent(self, event): class ToolButton(QToolButton): """标题栏按钮""" + def __init__(self, icon): super(ToolButton, self).__init__() self.setStyleSheet('border-color:#CCCCCC') @@ -33,7 +34,8 @@ def __init__(self, icon): class TextOpation(QWidget): """弹幕机选项 - 弹出式窗口""" - def __init__(self, setting=[50, 1, 7, 0, '【 [ {', 10]): + + def __init__(self, setting=[50, 1, 7, 0, '【 [ {', 10, 0]): super(TextOpation, self).__init__() self.resize(300, 300) self.setWindowTitle('弹幕窗设置') @@ -54,13 +56,15 @@ def __init__(self, setting=[50, 1, 7, 0, '【 [ {', 10]): layout.addWidget(QLabel('窗体横向占比'), 2, 0, 1, 1) self.horizontalCombobox = QComboBox() - self.horizontalCombobox.addItems(['%d' % x + '%' for x in range(10, 110, 10)]) + self.horizontalCombobox.addItems( + ['%d' % x + '%' for x in range(10, 110, 10)]) self.horizontalCombobox.setCurrentIndex(setting[1]) layout.addWidget(self.horizontalCombobox, 2, 1, 1, 1) layout.addWidget(QLabel('窗体纵向占比'), 3, 0, 1, 1) self.verticalCombobox = QComboBox() - self.verticalCombobox.addItems(['%d' % x + '%' for x in range(10, 110, 10)]) + self.verticalCombobox.addItems( + ['%d' % x + '%' for x in range(10, 110, 10)]) self.verticalCombobox.setCurrentIndex(setting[2]) layout.addWidget(self.verticalCombobox, 3, 1, 1, 1) @@ -76,6 +80,12 @@ def __init__(self, setting=[50, 1, 7, 0, '【 [ {', 10]): self.translateFitler.setFixedWidth(100) layout.addWidget(self.translateFitler, 5, 1, 1, 1) + layout.addWidget(QLabel('礼物和进入信息'), 6, 0, 1, 1) + self.showEnterRoom = QComboBox() + self.showEnterRoom.addItems(['显示礼物和进入信息', '只显示礼物', '只显示进入信息', '隐藏窗口']) + self.showEnterRoom.setCurrentIndex(setting[6]) + layout.addWidget(self.showEnterRoom, 6, 1, 1, 1) + class TextBrowser(QWidget): """弹幕机 - 弹出式窗口 @@ -102,17 +112,19 @@ def __init__(self, parent): self.bar.moveSignal.connect(self.moveWindow) layout.addWidget(self.bar, 0, 0, 1, 10) # 弹幕选项菜单 - self.optionButton = ToolButton(self.style().standardIcon(QStyle.SP_FileDialogDetailedView)) + self.optionButton = ToolButton( + self.style().standardIcon(QStyle.SP_FileDialogDetailedView)) self.optionButton.clicked.connect(self.optionWidget.show) # 弹出设置菜单 layout.addWidget(self.optionButton, 0, 8, 1, 1) # 关闭按钮 - self.closeButton = ToolButton(self.style().standardIcon(QStyle.SP_TitleBarCloseButton)) + self.closeButton = ToolButton( + self.style().standardIcon(QStyle.SP_TitleBarCloseButton)) self.closeButton.clicked.connect(self.userClose) layout.addWidget(self.closeButton, 0, 9, 1, 1) # 弹幕区域 self.textBrowser = QTextBrowser() - self.textBrowser.setFont(QFont('Microsoft JhengHei', 16, QFont.Bold)) + self.textBrowser.setFont(QFont('Microsoft JhengHei', 14, QFont.Bold)) self.textBrowser.setStyleSheet('border-width:1') # textCursor = self.textBrowser.textCursor() # textBlockFormat = QTextBlockFormat() @@ -123,10 +135,17 @@ def __init__(self, parent): # 同传区域 self.transBrowser = QTextBrowser() - self.transBrowser.setFont(QFont('Microsoft JhengHei', 16, QFont.Bold)) + self.transBrowser.setFont(QFont('Microsoft JhengHei', 14, QFont.Bold)) self.transBrowser.setStyleSheet('border-width:1') layout.addWidget(self.transBrowser, 2, 0, 1, 10) + # 信息区域 + self.msgsBrowser = QTextBrowser() + self.msgsBrowser.setFont(QFont('Microsoft JhengHei', 14, QFont.Bold)) + self.msgsBrowser.setStyleSheet('border-width:1') + # self.msgsBrowser.setMaximumHeight(100) + layout.addWidget(self.msgsBrowser, 3, 0, 1, 10) + def userClose(self): self.hide() self.closeSignal.emit() diff --git a/remote.py b/remote.py index 773d671..ff2731d 100644 --- a/remote.py +++ b/remote.py @@ -25,7 +25,8 @@ def __init__(self, roomID): return headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 \ (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE'} - html = requests.get('https://live.bilibili.com/' + self.roomID, headers=headers).text + html = requests.get('https://live.bilibili.com/' + + self.roomID, headers=headers).text for line in html.split('\n'): if '"roomid":' in line: self.roomID = line.split('"roomid":')[1].split(',')[0] @@ -77,22 +78,77 @@ def printDM(self, data): pass return + captainName = { + 0: "", + 1: "总督", + 2: "提督", + 3: "舰长" + } + + userType = { + "#FF7C28": "+++", + "#E17AFF": "++", + "#00D1F1": "+", + "": "" + } + + adminType = ["", "*"] + + def getMetal(jd): + try: + medal = [] + if jd['cmd'] == 'DANMU_MSG': + jz = captainName[jd['info'][3][10]] + if jz: + medal.append(jz) + medal.append(jd['info'][3][1]) + medal.append(str(jd['info'][3][0])) + else: + jz = captainName[jd['data']['medal_info']['guard_level']] + if jz: + medal.append(jz) + medal.append(jd['data']['medal_info']['medal_name']) + medal.append(jd['data']['medal_info']['medal_level']) + return "|" + "|".join(medal) + "|" + except: + return "" + if op == 5: try: jd = json.loads(data[16:].decode('utf-8', errors='ignore')) if jd['cmd'] == 'DANMU_MSG': - self.message.emit(jd['info'][1]) + self.message.emit( + f"{userType[jd['info'][2][7]]}{adminType[jd['info'][2][2]]}{getMetal(jd)} {jd['info'][2][1]}: {jd['info'][1]}" + ) elif jd['cmd'] == 'SUPER_CHAT_MESSAGE': - d = jd['data'] - self.message.emit('\nSC: ¥%s\n【%s】\n' % (d['price'], d['message'])) - # elif jd['cmd'] == 'SEND_GIFT': - # d = jd['data'] - # self.message.emit('%s投喂了%s个%s' % (d['uname'], d['num'], d['giftName'])) - # elif jd['cmd'] == 'COMBO_SEND': - # d = jd['data'] - # self.message.emit('%s投喂了%s个%s' % (d['uname'], d['batch_combo_num'], d['gift_name'])) - # elif jd['cmd'] == 'GUARD_BUY': - # self.message.emit('%s上了舰长' % jd['data']['username']) + self.message.emit( + f"SC(¥{jd['data']['price']}) {getMetal(jd)} {jd['data']['user_info']['uname']}: {jd['data']['message']}" + ) + elif jd['cmd'] == 'SEND_GIFT': + if jd['data']['coin_type'] == "gold": + self.message.emit( + f"** {jd['data']['uname']} {jd['data']['action']}了 {jd['data']['num']} 个 {jd['data']['giftName']}" + ) + elif jd['cmd'] == 'USER_TOAST_MSG': + self.message.emit( + f"** {jd['data']['username']} 上了 {jd['data']['num']} 个 {captainName[jd['data']['guard_level']]}" + ) + elif jd['cmd'] == 'ROOM_BLOCK_MSG': + self.message.emit( + f"** 用户 {jd['data']['uname']} 已被管理员禁言" + ) + elif jd['cmd'] == 'INTERACT_WORD': + self.message.emit( + f"## 用户 {jd['data']['uname']} 进入直播间" + ) + elif jd['cmd'] == 'ENTRY_EFFECT': + self.message.emit( + f"## {jd['data']['copy_writing_v2']}" + ) + elif jd['cmd'] == 'COMBO_SEND': + self.message.emit( + f"** {jd['data']['uname']} 共{jd['data']['action']}了 {jd['data']['combo_num']} 个 {jd['data']['gift_name']}" + ) except: logging.exception('弹幕输出失败')