回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【Mind+Python】全网视频下载播放器 简单

头像 周志华 2021.09.21 417 0

1.设计背景、功能需求

作为一名教师,我们时常需求将网页上的视频进行下载并将其呈现在课件上,为课堂的教学呈现丰富的多媒体素材,同时也作为一名学习者,我们也时常需要在各个视频发布平台上观看视频进行学习,通过平时的操作过程中发现绝大部分平台提供的视频一是无下载权限和相关下载操作,二是例如爱奇艺、腾讯视频等平台即便可以下载,但是下载的格式为平台专属的格式如qsv、qlv等,不具有适用性,并且平台的资源都是其他用户上传的,如果用户将删除了他的作品,我们也就不能再次观看或学习,因此我们将多媒体素材按通用MP4格式保存到本地即可解决此上两种问题。

本次的作品以这个需求基于python的you-get模块制作了一个主流视频网站的下载软件。因为考虑到以青少年教育的背景下所以本次下载的功能使用的是功能完善的python的三方资源下载模块you-get,该模块的使用是十分简单的,只需要提供视频的网址就可以下载,能提起青少年对爬虫的兴趣,逐步了解该模块的下载原理。

考虑到用户的使用体验所以我们是需要一个UI界面的。该界面主要由视频选择板块、视频播放板块、视频保存路径选择板块、视频下载。

2.功能原理

本项目的界面使用的是tkinter模块实现的,视频下载通过you-get模块加多线程的方法实现,单线程下载视频会出线界面未响应的情况,因为当执行下载视频的程序后程序会一直等在该处则造成界面卡死,所以视频下载单独开一个线程。视频播放使用到pyaudio和moviepy两个模块,同样使用多线程的方式播放视频,声音画面采用两个线程完成。

步骤1 界面介绍

界面设计图如图3.1.1所示,图中的红色的1是选择本地的视频进行播放使用的是tkinter中的菜单组件,图中红色区域二是下载视频的选择区域,该区域使用的tkinter中的控件包括按钮控件(Button)、输入控件(Entry)。图中红色区域3用于视频播放,由一个标签控件(Label)组成。

project-image

步骤2 界面功能介绍

环境的配置

you-get安装

下载命令:pip install you-get,按win+r后输入cmd打开命令控制面板然后执行该命令等待下载即可。

project-image

pyaudio下载

下载命令:pip install pyaudio通过mind+中库管理安装

project-image

moviepy下载

下载命令:pip install moviepy通过mind+库管理安装

project-image

pillow下载

下载命令:pip install pillow通过mind+库管理安装

project-image

步骤3 视频播放功能

用户点击了图3.1.1中的文件菜单并选择了需要播放的视频,在区域三就会播放我们选中的视频。效果如图3.2.1所示。

project-image

步骤4 视频下载功能

用户想要下载视频通过图3.1.1中的红色区域2进行下载,在该区域中主要有三个功能1、获取用户将下载好的视频保存的位置。2、获取用户想要下载视频的url。3、通过用户提供的url下载视频。用户将视频保存路径,和视频的填写好后,通过正则表达式判断用户提供的url是否合法,合法正常下载在图3.1.1区域三的Label标签会显示开始下载,下载完成自动播放该视频,反之提示url错误。下载过程图如图3.2.2、3.2.3、3.2.4所示

project-image

图3.2.3(下载完成并自动播放)

project-image

图3.2.4(url错误提示)

project-image

步骤5 基本文件操作

在运行程序之前需要在桌面上新建一个download文件夹,在该文件夹中创建一个文件名为:视频的文件夹。和保存两个图片,图片名字为:下载.png、logo.ico。这样程序方可正常运行。

代码
import os
import tkinter as tk
from tkinter import filedialog
import re
import threading
from tkinter import messagebox
from PIL import Image, ImageTk
from time import sleep
from tkinter.filedialog import askopenfilename
import pyaudio
from moviepy.editor import VideoFileClip
class Download:
    # 编写界面
    def __init__(self):
        self.desktop_path = self.GetDesktopPath()
        os.system('chcp 65001')  # 解决乱码
        self.window = tk.Tk()
        self.window.geometry('450x500')
        self.window.title('视频播放&下载')
        self.window.iconbitmap(self.desktop_path + r'\download\logo.ico')  # 设置图标

        # 在桌面创建download文件夹
        if 'download' not in os.listdir(self.desktop_path):
            os.mkdir(r'{}\download'.format(self.desktop_path))
        self.e_path = tk.StringVar()
        self.e_path.set('')
        self.e_url = tk.StringVar()
        self.e_url.set('')
        self.ui()

    def ui(self):
        img_path = self.desktop_path + '\\download\\下载.png'
        self.img = Image.open(img_path)
        self.img = self.img.resize((30, 30))
        self.img = ImageTk.PhotoImage(image=self.img)

        l = tk.Label(self.window, text='视频下载&播放', font=('', 20))
        l.place(x=105, y=5, width=250)
        b = tk.Button(self.window, text='保存路径', font=('', 20))
        b.place(x=10, y=60, height=30, width=120)
        e = tk.Entry(self.window, textvariable=self.e_path, font=('', 15))
        e.place(x=130, y=60, width=280, height=30)
        b1 = tk.Button(self.window, text='浏览', font=('楷体', 13), bg="green", command=self.choose_file)
        b1.place(x=410, y=60)

        b1 = tk.Button(self.window, text='资源URL', font=('', 20))
        b1.place(x=10, y=110, height=30, width=120)
        e1 = tk.Entry(self.window, textvariable=self.e_url, font=('', 20))
        e1.place(x=130, y=110, width=280, height=30)
        d = tk.Button(self.window, image=self.img, command=self.download)
        d.place(x=410, y=110, height=30)
        self.t = tk.Text(self.window, bg='white', font=('', 15))
        self.t.place(x=10, y=150, width=430, height=320)
        # 视频播放
        self.isPlaying = False
        # 用来显示视频画面的Label组件,自带双缓冲,不闪烁
        self.lbVideo = tk.Label(self.window, bg='white')
        self.lbVideo.place(x=10, y=150)
        # 创建主菜单
        self.mainMenu = tk.Menu(self.window)
        # tearoff=1时菜单顶部会有个虚线
        # 单击虚线之后可以使得菜单从窗口中分离处理单独显示
        subMenu = tk.Menu(tearoff=0)
        # 把子菜单挂到主菜单上
        self.mainMenu.add_cascade(label='文件', menu=subMenu)
        # 添加菜单项,设置命令
        subMenu.add_command(label='打开视频文件',
                            command= lambda:self.open_video(False))

        # 把主菜单放置到窗口上
        self.window['menu'] = self.mainMenu
        self.window.protocol('WM_DELETE_WINDOW', self.exiting)
        self.exit_ = False

    def choose_file(self) -> None:
          #浏览选择本地文件夹
        save_address = filedialog.askdirectory()
        #把获得路径,插入保存地址输入框(即插入input_save_address输入框)
        self.e_path.set(save_address)
    def GetDesktopPath(self) -> str:
        '''
            获取桌面路径
        '''
        return os.path.join(os.path.expanduser("~"), 'Desktop')

    def you_get(self, command):
        os.system(command)
        self.t.insert('end', '下载完成\n')
        self.e_url.set('')
        self.open_video(True)

    def download(self):
        '''
            下载用户需要下载的资源
        '''
        # 判断是否输入url
        user_url = self.e_url.get()
        if not user_url:
            messagebox.showinfo('消息框', '请输入url')
            return
        # 使用正则判断url是否合法
        r = r'^https?:/{2}\w.+$'
        if not re.match(r, user_url, re.S):
            messagebox.showinfo('消息框', '请输入正确的url')
            self.e_url.set('')
            return
        # 创建保存视频的文件夹
        user_path = self.e_path.get()
        self.t.insert('end', '开始下载...\n')
        # 使用you-get下载资源
        command = r'you-get -o {} {}'.format(user_path, user_url)
        threading.Thread(target=self.you_get, args=(command,)).start()  # 创建一个线程避免程序卡死

    def destroy(self):
        '''
            注销自己
        '''
        self.window.update()  # 解决注销报错
        self.window.destroy()

    # 视频播放
    def play_video(self, video):
        # 逐帧播放画面
        for frame in video.iter_frames(fps=video.fps / 0.98):
            if self.exit_:
                self.exit_ = False
                break
            elif not self.isPlaying:
                self.exit_ = True
                break
            frame = Image.fromarray(frame).resize((430, 320))
            frame = ImageTk.PhotoImage(frame)
            self.lbVideo['image'] = frame
            self.lbVideo.image = frame
            self.lbVideo.update()
        self.isPlaying = False

    def play_audio(self, audio):
        p = pyaudio.PyAudio()
        # 创建输出流
        stream = p.open(format=pyaudio.paFloat32,
                        channels=2,
                        rate=44100,
                        output=True)
        # 逐帧播放音频
        for chunk in audio.iter_frames():
            if self.exit_:
                self.exit_ = False
                break
            elif not self.isPlaying:
                self.exit_ = True
                break
            stream.write(chunk.astype('float32').tostring())
        p.terminate()

    def open_video(self, choose):
        self.isPlaying = False
        if choose:
            fn = self.get_video_path()
        else:
            fn = askopenfilename(title='打开视频文件',
                                filetypes=[('视频', '*.mp4 *.avi')])
        if fn:
            self.window.title(f'视频播放器-正在播放"{fn}"')
            video = VideoFileClip(fn)
            self.time_v = video.duration/60
            audio = video.audio
            print(video.iter_frames(), video.fps, audio.fps)
            self.isPlaying = True
            # 播放视频的线程
            t1 = threading.Thread(target=self.play_video, args=(video,))
            t1.daemon = True
            t1.start()
            # # 播放音频的线程
            t2 = threading.Thread(target=self.play_audio, args=(audio,))
            t2.daemon = True
            t2.start()
        # 确保子线程关闭,
    def exiting(self):
        self.isPlaying = False
        sleep(0.05)
        self.destroy()
    def get_video_path(self):
        path = self.desktop_path + r'\download\视频'
        path_list = os.listdir(path)
        if path_list:
            return path + '\\' + path_list[-1]
Download()
tk.mainloop()

评论

user-avatar