Pyqt制作B站视频下载软件

本文详细介绍了一款基于Pyqt的B站视频下载软件的设计与实现过程,包括视频URL提取、音频与视频分离下载及合成、线程下载管理等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

界面

在这里插入图片描述
在这里插入图片描述

B站下载视频Url提取

需要注意的是B站的音频和视频是分开的,也就是说要下载两个东西,然后自己合并才能变成一个完整的视频,如下:
sText为请求的视频网站的内容

 self.m_oRequest =requests.session()
 oResult = self.m_oRequest.get(sUrl, headers=base_headers, timeout=3)
 sText = oResult.text
    def getVideoLinks(self, sText):
        # get video download links
        from public import tools
        dctDownloadLink = {}
        try:
            data = re.findall(r'<script>window\.__playinfo__=(.*?)</script>', sText)[0]
            dctBiliData = json.loads(data)
            #pprint.pprint(dctBiliData)
            lstLinkData = dctBiliData['data']['dash']['video']
            sAudioUrl = dctBiliData['data']['dash']['audio'][0]["base_url"].replace("http", "https")
            sVideoUrl = dctBiliData['data']['dash']['video'][0]["base_url"].replace("http", "https")
            dctDownloadLink["AudioUrl"] = sAudioUrl
            dctDownloadLink["VideoUrl"] = sVideoUrl
        except:
            print("解析地址失败...")
        print("下载链接 ", dctDownloadLink)
        return dctDownloadLink

视频和音频的合成

这里使用了第三方软件
ffmpeg
网上下载该软件,里面会有exe执行文件,将该执行文件路径加入到系统环境变量中去
合成代码如下

    # 合成音频和视频
    def combineVideo(self, sAudioPath, sVideoPath, sOutputPath):
        import subprocess
        import ffmpeg
        print("开始合成音频和视频...", sAudioPath, sVideoPath, sOutputPath)
        sCmd = 'ffmpeg -i ' + sAudioPath + ' -i ' + sVideoPath + ' -vcodec copy -acodec copy -y ' + sOutputPath
        subprocess.run(sCmd, shell=True)
        print("合成完成...")
        self.combineFinish()

知识点

线程下载

支持暂停,继续,停止

class CDownloader(QThread):
    """download class"""
    oSignalDownload = pyqtSignal(QThread, int) # 下载量信号 list: value flag
    oSignalFinish = pyqtSignal(QThread, int) # 下载结束信号
    def __init__(self, iAvID, dctLinks):
        super(CDownloader, self).__init__()
        self.m_iAvID = iAvID
        self.m_dctLinks = dctLinks
        self.m_oEventPause = threading.Event()     # pause flag
        self.m_oEventPause.set()       # True
        self.m_oEventRunning = threading.Event()      # stop flag
        self.m_oEventRunning.set()      # True
        sScrPath = os.getcwd()
        self.m_sDir = os.path.join(sScrPath, "./videos")
        if not os.path.exists(self.m_sDir):
            os.mkdir(self.m_sDir)

    def run(self):
        """download a video
        may contain muti-slices videos
        """
        headers = {
            'Accept':'*/*',
            'Accept-Encoding':'gzip, deflate, br',
            'Accept-Language':'zh-CN,zh;q=0.8,en;q=0.6',
            'Cache-Control':'no-cache',
            'Connection':'keep-alive',
            'Origin':'https://www.bilibili.com',
            'Pragma':'no-cache',
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36',
        }
        headers['Referer'] = 'https://www.bilibili.com/video/av%s' % self.m_iAvID
        print("下载线程开启...")
        sAudioPath = self.m_sDir + os.sep + str(self.m_iAvID) + ".mp3"
        sVideoPath = self.m_sDir + os.sep + str(self.m_iAvID) + ".mp4"
        sOutputPath = self.m_sDir + os.sep + "av" + str(self.m_iAvID) + ".mp4"
        for sType, sUrl in self.m_dctLinks.items():
            if sType == "AudioUrl":
                sFileName = sAudioPath
            else:
                sFileName = sVideoPath
            while self.m_oEventRunning.isSet(): # 如果被设置为了true就继续,false就终止了
                print("开始下载...", sFileName)
                with contextlib.closing(requests.get(sUrl, headers = headers, timeout = 3, stream=True)) as oRequest:
                    print("状态码: ", oRequest.status_code)
                    iContentSize = int(oRequest.headers['content-length']) # 内容体总大小
                    iChunkSize = int(iContentSize/100) # 单次请求最大值
                    print('文件总大小: %02sM, 单次请求最大值: %s' % (iContentSize / 1024 / 1024, iChunkSize))
                    count = 0
                    try:
                        with open(sFileName, "wb") as file:
                            #当流下载时,用Response.iter_content或许更方便些
                            #requests.get(url)默认是下载在内存中的 下载完成才存到硬盘上
                            # 可以用Response.iter_content 来边下载边存硬盘
                            for data in oRequest.iter_content(chunk_size=iChunkSize):
                                if not self.m_oEventRunning.isSet():
                                    self.finishToEmit(-2)
                                    return
                                self.m_oEventPause.wait()  # 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
                                file.write(data)
                                count += len(data)
                                #print("已下载大小: %s, 进度: %d%%" % (count, count / iContentSize * 100))
                                self.progressToEmit(int(count / iContentSize * 100))
                    except Exception as e:
                        self.finishToEmit(-1)
                        print("错误:", e)
                        return
                print("下载完成! ", sFileName)
                break
        try:
            self.combineVideo(sAudioPath, sVideoPath, sOutputPath)
            self.delSourceFile(sAudioPath, sVideoPath)
        except Exception as e:
            print("错误,请检查是否安装了ffmpeg", e)
        self.finishToEmit(1)

    # 合成音频和视频
    def combineVideo(self, sAudioPath, sVideoPath, sOutputPath):
        import subprocess
        import ffmpeg
        print("开始合成音频和视频...", sAudioPath, sVideoPath, sOutputPath)
        sCmd = 'ffmpeg -i ' + sAudioPath + ' -i ' + sVideoPath + ' -vcodec copy -acodec copy -y ' + sOutputPath
        subprocess.run(sCmd, shell=True)
        print("合成完成...")
        self.combineFinish()

    # 删除源文件
    def delSourceFile(self, sAudioPath, sVideoPath):
        print("删除源文件: ", sAudioPath, sVideoPath)
        os.remove(sAudioPath)
        os.remove(sVideoPath)

    def combineFinish(self):
        print("合成成功!")

    def pause(self):
        self.m_oEventPause.clear()

    def resume(self):
        self.m_oEventPause.set()

    def stop(self):
        self.m_oEventRunning.clear()
        self.exit()

    def progressToEmit(self, iProgress):
        self.oSignalDownload.emit(self, iProgress)

    def finishToEmit(self, iFlag):
        self.oSignalFinish.emit(self, iFlag)

提取了线程下载基类

# -*- coding: utf-8 -*-
from PyQt5.QtCore import QThread, pyqtSignal
import threading
import contextlib
import requests
import os

# 线程下载基类
class CDownloaderBase(QThread):
    """download class"""
    oSignalDownload = pyqtSignal(QThread, int) # 下载量信号 list: value flag
    oSignalFinish = pyqtSignal(QThread, int) # 下载结束信号
    def __init__(self, sSavePath, sUrl):
        super(CDownloaderBase, self).__init__()
        self.m_sSavePath = sSavePath
        self.m_sUrl = sUrl
        self.m_oEventPause = threading.Event()     # pause flag
        self.m_oEventPause.set()       # True
        self.m_oEventRunning = threading.Event()      # stop flag
        self.m_oEventRunning.set()      # True
        self.m_dctHeader = {}

    def setHeader(self, dctHeader):
        self.m_dctHeader = {}

    def getHeader(self):
        dctHeaders = {
            'Accept': '*/*',
            'Accept-Encoding': 'gzip, deflate, br',
            'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            # 'Origin':'https://www.bilibili.com',
            'Pragma': 'no-cache',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36',
            'Referer': "",
        }
        return self.m_dctHeader.update(dctHeaders)

    def run(self):
        dctHeaders = self.getHeader()
        print("下载线程开启...")
        while self.m_oEventRunning.isSet(): # 如果被设置为了true就继续,false就终止了
            print("开始下载...", self.m_sSavePath, self.m_sUrl)
            with contextlib.closing(requests.get(self.m_sUrl, headers = dctHeaders, timeout = 5, stream=True)) as oRequest:
                print("状态码: ", oRequest.status_code)
                if oRequest.status_code != 200:
                    print("状态码有误,下载失败")
                    return
                iContentSize = int(oRequest.headers['content-length']) # 内容体总大小
                iChunkSize = int(iContentSize/100) # 单次请求最大值
                print('文件总大小: %.3f M, 单次请求最大值: %s' % (iContentSize / 1024 / 1024, iChunkSize))
                count = 0
                try:
                    self.createFile(self.m_sSavePath)
                    with open(self.m_sSavePath, "wb") as oFile:
                        #当流下载时,用Response.iter_content或许更方便些
                        #requests.get(url)默认是下载在内存中的 下载完成才存到硬盘上
                        # 可以用Response.iter_content 来边下载边存硬盘
                        for data in oRequest.iter_content(chunk_size=iChunkSize):
                            if not self.m_oEventRunning.isSet():
                                self.finishToEmit(-2)
                                return
                            self.m_oEventPause.wait()  # 为True时立即返回, 为False时阻塞直到内部的标识位为True后返回
                            oFile.write(data)
                            count += len(data)
                            #print("已下载大小: %s, 进度: %d%%" % (count, count / iContentSize * 100))
                            self.progressToEmit(int(count / iContentSize * 100))
                except Exception as e:
                    self.finishToEmit(-1)
                    print("错误:", e)
                    return
            break
        print("下载完成! ", self.m_sSavePath)
        self.finishToEmit(1)

    def pause(self):
        self.m_oEventPause.clear()

    def resume(self):
        self.m_oEventPause.set()

    def stop(self):
        self.m_oEventRunning.clear()
        self.exit()

    def progressToEmit(self, iProgress):
        self.oSignalDownload.emit(self, iProgress)

    def finishToEmit(self, iFlag):
        self.oSignalFinish.emit(self, iFlag)


    def createFile(self, sPath):
        import os
        sPath = sPath.replace("\\", "/")
        sDir = sPath[0:sPath.rfind("/")]
        if not os.path.isdir(sDir):  # 无文件夹时创建
            os.makedirs(sDir)
        if not os.path.isfile(sPath):  # 无文件时创建
            with open(sPath, mode="w", encoding="utf-8"):
                pass
        else:
            pass


# 不使用线程下载
def downloadByUrl(url, sPath, headers = None, dctConfig = None):
    import contextlib
    import requests
    if not dctConfig:
        dctConfig = {}
    if not headers:
        headers = {}
    header_base = {
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Pragma': 'no-cache',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36',
        #'Referer': "",
        #'Origin': 'https://www.bilibili.com',
    }
    header_base.update(headers)
    createFile(sPath)
    with contextlib.closing(requests.get(url, headers=header_base, stream=True)) as oResponse: # stream属性必须带上
        chunk_size = dctConfig.get("ChunkSize", 1024)  # 每次下载的数据大小
        content_size = int(oResponse.headers['content-length'])  # 总大小
        if oResponse.status_code == 200:
            print('[文件大小]:%0.2f MB' % (content_size / 1024 / 1024))  # 换算单位
            with open(sPath, 'wb') as file:
                for data in oResponse.iter_content(chunk_size=chunk_size):
                    file.write(data)
    print("下载成功", url, sPath)

def createFile(sPath):
    import os
    sPath = sPath.replace("\\", "/")
    sDir = sPath[0:sPath.rfind("/")]
    if not os.path.isdir(sDir):  # 无文件夹时创建
        os.makedirs(sDir)
    if not os.path.isfile(sPath):  # 无文件时创建
        with open(sPath, mode="w", encoding="utf-8"):
            pass
    else:
        pass

GitHub地址

GitHub

基于STM32单片机的室内环境检测及报警系统的设计与制作,请为我的毕业设计写一个任务书。综合我的第一句话来写,并将可以优化的部分优化并写进任务书中,任务书要逻辑清晰,目的明确。任务书主要包括几个模块:毕业设计目标、主要任务、实现步骤和方法、预期成果。内容的结构按照下面的来: 毕业设计目标 在这次毕业设计中,完成智慧家禽养殖场辅助系统的设计,让学生学习现代的资源搜集和查找方式,学会编写并完善职业设计说明文档,使学生掌握实际项目的开发方式和开发流程,利用在大学三年所学的技术和所培养的职业素养,锻炼学生解决实际开发所遇到的问题的能力,为今后在以后的工作中打下更扎实的开发能力。培养学生冷静独立、勤奋好学,爱岗敬业、敢于尝试的工作心态与体现大国工匠精神。 主要任务 完成一个基于ESP8266单片机的智能图书馆座位管理系统的设计,实现以下功能: 1、设计一个通过温度传感器实时监控室内温度的功能; 2、设计一个通过水位传感器实时监控家畜饮用水水量的功能; 3、设计一个通过气体传感器实时监控空气二氧化碳浓度的功能; 4、设计一个可定时进行补充家禽饮用水的功能; 5、设计一个监测空气二氧化碳浓度进行实时通风的功能; 6、设计一个可接入网络进行监测数据传输的功能; 7、设计一个基于LCD的本地检测指标显示画面; 8、设计一个矩阵按键的输入系统; 9、设计一个通过网络进行远程控制饮用水补充和通风的功能。 实现步骤和方法 1、根据毕业设计的功能需求,利用视频,技术论坛等渠道搜集毕业设计的相关信息,对农业相关相关材料进行深入研究,最终制定出相应毕业设计方案; 2、根据毕业设计功能、成本需求,选取适当的元器件与芯片; 3、根据方案中所选择的硬件使用国产嘉立创EDA标准版软件绘制软件原理图; 4、进行代码编写,编译完成后进行仿真,根据仿真结果调试好软件代码; 5、仿真测试; 6、检测元器件是否能正常工作; 7、利用锡焊工艺组装产品; 8、通电测试、软硬件联调确保产品正常工作。 预期成果 完成本设计并实现一款基于ESP8266单片机的智慧家禽养殖场辅助系统。该系统能够通过监测家畜的饮用水水位进行自动补水的功能;能够通过按键和远程软件应用设置补水功能跟通风功能;能够通过LCD屏幕对当前的监测指标进行实时实时;同时,系统能够将检测到的水位,温度,二氧化碳浓度在云端实时显示的功能。
03-08
<think>好的,我现在需要帮用户完成毕业设计任务书的撰写,针对基于STM32的室内环境检测及报警系统。首先,用户之前已经询问过单片机型号的选择,现在转向任务书的结构,要求包括目标、任务、步骤、预期成果,并且要优化内容。 首先,我需要理解用户的需求。用户最初提供的示例任务书是关于家禽养殖的系统,但用户现在的课题是室内环境检测,所以需要调整示例中的内容,确保所有功能点都对应到环境监测,比如温度、气体浓度、报警等,而不是家禽相关的功能。同时,用户提到要优化部分内容,可能是指改进示例中的表述,使其更专业或更符合实际应用。 接下来,毕业设计目标部分需要突出学习目标、技术掌握以及职业素养。原示例提到了资源搜集、文档编写、项目开发流程等,这些都可以保留,但要针对环境监测系统调整。例如,强调传感器数据融合、报警机制设计、低功耗优化等。 主要任务部分需要列出系统的具体功能。用户之前的对话中提到过温度、气体、湿度传感器,还有LCD显示、矩阵按键、报警、数据上传等。需要将这些转化为任务条目,比如实时监测多项参数、本地显示、报警逻辑、数据上传、远程控制等。注意优化任务描述,使用更专业的术语,比如“多级报警策略”代替简单的报警功能。 实现步骤和方法需要逻辑清晰,从需求分析到测试部署。原示例中的步骤有些是针对家禽系统的,需要调整为环境监测的步骤,比如选择STM32型号、传感器选型、硬件设计、通信模块集成等。同时,要加入优化点,比如低功耗设计、数据滤波算法、云平台对接等。 预期成果部分要具体明确,包括硬件、软件、文档和扩展功能。例如,完成实物系统,实现各项监测功能,具备数据存储和远程访问,以及优化后的性能指标如响应时间、测量精度等。还要提到用户手册和测试报告,展示全面性。 需要注意用户可能忽略的细节,比如系统的可扩展性、用户界面友好性、数据安全性等,可以在优化部分加入这些内容。此外,确保任务书结构符合要求,模块分明,语言专业但不过于复杂,适合作为毕业设计文档。 最后,检查任务书是否符合用户提供的结构,每个部分是否覆盖所有必要内容,并且优化部分是否合理融入。确保没有使用Markdown格式,语言口语化但保持专业性,同时遵循用户关于LaTeX和系统指令的要求。</think>(优化版任务书) --- ### **毕业设计目标** 通过本课题的设计与实施,达成以下综合培养目标: 1. 掌握基于STM32的嵌入式系统开发全流程,提升传感器数据融合、人机交互设计及物联网通信等关键技术应用能力 2. 培养系统工程思维,学习使用Altium Designer进行硬件电路优化设计,实践低功耗系统设计方法 3. 强化工程文档编写能力,按照GB/T 7713标准完成专业设计文档体系 4. 培养故障诊断与优化意识,通过EMI/EMC测试提升产品可靠性 5. 践行工匠精神,在报警策略优化、数据显示刷新率提升等环节实现技术创新 --- ### **主要任务** **设计并实现基于STM32的智能环境监测报警系统,要求完成:** 1. **环境参数监测** - 高精度温度监测(±0.5℃):支持DS18B20/DHT22多传感器数据融合 - 多气体检测:CO₂(MH-Z19B)、TVOC(SGP30)双模检测 - 湿度监测:支持露点温度计算与结露预警 2. **人机交互系统** - 2.8寸TFT-LCD实时显示:分页显示环境参数曲线(刷新率≥30fps) - 4×4矩阵按键:支持报警阈值设置与模式切换 - 声光报警:多级报警策略(蜂鸣器频率分级+RGB LED颜色预警) 3. **通信与存储** - 本地SD卡存储:CSV格式数据记录(采样间隔可调) - 4G/WiFi双模传输:通过MQTT协议对接阿里云IoT平台 - 微信小程序远程监控:支持实时数据查看与设备控制 4. **系统优化** - 动态功耗管理:待机功耗≤10mA,运行功耗≤150mA - 数据滤波算法:改进型卡尔曼滤波实现传感器噪声抑制 - 故障自诊断:传感器断线检测与系统状态指示 --- ### **实现步骤和方法** #### **阶段一:需求分析与方案设计(2周)** 1. 使用Kano模型进行需求优先级排序,确定核心功能边界 2. 对比STM32F4/H7系列性能参数,选用STM32F407VGT6作为主控 3. 建立传感器选型矩阵表,重点考察精度、响应时间与通信接口匹配度 #### **阶段二:硬件系统开发(4周)** 1. 采用模块化设计: - 核心板:最小系统+电源管理(TPS5430DDAR) - 传感器板:设计I²C总线隔离电路(ADuM1250) - 接口板:ESD保护(SRV05-4)与信号调理电路 2. 使用Altium Designer 23进行四层板设计,重点优化: - ADC采样通道阻抗匹配(50Ω单端阻抗控制) - 数字地/模拟地分割与星型接地拓扑 #### **阶段三:软件系统开发(5周)** 1. 基于STM32CubeMX搭建开发环境: - 使用FreeRTOS创建多任务架构(传感器采集、显示刷新、通信各独立任务) - 配置DMA双缓冲机制提升SPI屏刷新效率 2. 关键算法实现: ```c // 改进型滑动平均滤波示例 #define FILTER_WINDOW 8 float moving_avg_filter(float new_data) { static float buffer[FILTER_WINDOW] = {0}; static uint8_t index = 0; buffer[index] = new_data; index = (index + 1) % FILTER_WINDOW; float sum = 0; for(int i=0; i<FILTER_WINDOW; i++){ sum += buffer[i]; } return sum / FILTER_WINDOW; } ``` 3. 开发微信小程序:使用Uni-app框架实现跨平台控制界面 #### **阶段四:系统集成与测试(3周)** 1. 环境适应性测试: - 温度循环测试(-20℃~+60℃) - 85%RH高湿环境连续运行测试 2. EMI测试: - 使用频谱分析仪检测30MHz-1GHz辐射发射 - 通过GB/T 9254 Class B认证要求 3. 开发HMI测试工具: - 基于PyQt5开发PC端参数配置软件 - 实现Modbus RTU协议通信测试 --- ### **预期成果** #### **硬件成果** 1. 完成具备6项核心功能的实物系统: - 尺寸≤120×80×30mm - 支持DC12V/USB双供电模式 - 平均无故障时间≥2000小时 #### **软件成果** 1. 开发完整的嵌入式软件系统: - 响应时间:按键响应≤50ms,数据显示更新≤100ms - 测量精度:温度±0.3℃,CO₂±50ppm 2. 配套云服务平台: - 数据存储周期≥1年 - 支持同时在线设备数≥50台 #### **文档成果** 1. 编制符合工程规范的设计文档: - 系统设计方案(含DFMEA分析) - 测试验证报告(包含EMC测试数据) - 用户手册(中英双语版) #### **创新点** 1. 提出环境参数耦合分析模型,实现更精准的空气质量评估 2. 开发自适应报警算法,误报率降低至<2% 3. 实现微信小程序与本地设备的双向实时控制(延迟<500ms) --- 该任务书在保留原框架基础上增加了: 1. 硬件可靠性设计要素(EMC、环境测试) 2. 软件算法实现细节(代码示例) 3. 量化验收指标(尺寸、精度、延迟等) 4. 创新性设计要求 可根据实际实验条件调整测试项目深度,建议重点投入传感器数据融合算法开发与用户交互体验优化两个方向。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值