基于Tkinter与Pytube的YouTube下载器TypeError错误求助
嘿,我来帮你搞定这个问题!你遇到的TypeError: unsupported operand type(s) for -: 'int' and 'NoneType'错误,主要是两个小细节没处理好,咱们一步步捋清楚:
错误原因分析
进度回调函数参数不匹配
Pytube的on_progress_callback回调函数现在的标准参数格式是(stream, chunk, bytes_remaining),但你写的progress函数参数是stream= None, chunk=None, file_handle = None, remaining=None,参数顺序和名称不对,导致remaining拿到的是None。当你执行fileSizeInBytes - remaining时,就触发了“整数减None”的类型错误。480p视频选择的小疏忽
在选择480p视频的代码里,你写了selectVideo = yt.streams.filter(progressive=True, file_extension='mp4').last,这里的last是一个方法,必须加括号调用last(),不然selectVideo会变成一个方法对象而不是实际的视频流对象,后续也会引发问题。
修复方案
1. 修改进度回调函数
把progress函数改成下面这样,参数完全对应Pytube的要求:
def progress(stream, chunk, bytes_remaining): # 计算已下载百分比 percent = (100 * (fileSizeInBytes - bytes_remaining)) / fileSizeInBytes print('{:.2f}% downloaded'.format(percent))
2. 修复480p视频选择代码
把480p选项里的last改成last():
elif (choice == downloadChoices[2]): print('480p video is downloading...') loadingLabel.config(text='480p video file downloading...') selectVideo = yt.streams.filter(progressive=True, file_extension='mp4').last() # 这里加括号
完整修复后的代码
from tkinter import * from tkinter import ttk from tkinter import filedialog from pytube import YouTube FolderName = '' fileSizeInBytes = 0 MaxFileSize = 0 def openDirectory(): global FolderName FolderName = filedialog.askdirectory() if (len(FolderName)> 1): fileLocationLabelError.config(text=FolderName, fg='green') else: fileLocationLabelError.config(text='Please Choose Folder', fg='red') def DownloadFile(): global MaxFileSize,fileSizeInBytes choice = youtubeChoices.get() video = youtubeEntry.get() if (len(video)>1): youtubeEntryError.config(text='') print(video,'at',FolderName) yt = YouTube(video,on_progress_callback=progress) print('video name is:\n\n',yt.title) if (choice == downloadChoices[0]): print('1080p video is downloading...') loadingLabel.config(text='1080p video file downloading...') selectVideo =yt.streams.filter(progressive=True).first() elif (choice == downloadChoices[1]): print('720p video is downloading...') loadingLabel.config(text='720p video file downloading...') selectVideo =yt.streams.filter(progressive=True).first() elif (choice == downloadChoices[2]): print('480p video is downloading...') loadingLabel.config(text='480p video file downloading...') selectVideo = yt.streams.filter(progressive=True, file_extension='mp4').last() # 修复:添加括号 elif (choice == downloadChoices[3]): print('3gp file is downloading...') selectVideo = yt.streams.filter(file_extension='3gp').first() elif (choice == downloadChoices[4]): print('Audio file is downloading...') selectVideo = yt.streams.filter(only_audio=True).first() fileSizeInBytes = selectVideo.filesize MaxFileSize = fileSizeInBytes / 1024000 MB = str(MaxFileSize) + 'MB' print('File size = : {:.2f}MB'.format(MaxFileSize)) # 优化显示格式 # 开始下载 selectVideo.download(FolderName) print('Downloaded on : {}'.format(FolderName)) complete() else: youtubeEntryError.config(text='Please paste youtube link', fg='red') # 修复:调整回调函数参数匹配Pytube要求 def progress(stream, chunk, bytes_remaining): percent = (100 * (fileSizeInBytes - bytes_remaining)) / fileSizeInBytes print('{:.2f}% downloaded'.format(percent)) def complete(): loadingLabel.config(text='Download completed') root = Tk() root.title('YouTube video Downloader') root.grid_columnconfigure(0,weight=1) youtubeLinkLabel = Label(root,text='Please paste youtube link here..', fg='blue',font=('Agency FB',30)) youtubeLinkLabel.grid() youtubeEntryvar = StringVar() youtubeEntry = Entry(root,width=50, textvariable=youtubeEntryvar) youtubeEntry.grid(pady=(0,20)) youtubeEntryError = Label(root,fg='red', text='',font=('Agency FB',20)) youtubeEntryError.grid(pady=(0,10)) SaveLabel = Label(root,text='Where to download file?',fg='blue',font=('Agency FB',20,'bold')) SaveLabel.grid() SaveEntry = Button(root,width=20,bg='green',fg='white', text='Choose folder',font=('Arial',15), command=openDirectory) SaveEntry.grid() fileLocationLabelError = Label(root,text='',font=('Agency FB',20)) fileLocationLabelError.grid(pady=(0,0)) youtubeChooseLabel = Label(root,text='Please choose what to download:', font=('Agency FB',20)) youtubeChooseLabel.grid() downloadChoices = ['Mp4 1080p', 'Mp4 720p', 'Mp4 480p', 'Video 3gp', 'song mp3'] youtubeChoices = ttk.Combobox(root,values=downloadChoices) youtubeChoices.grid() downloadButton = Button(root,text='Download',width=15,bg='green', command=DownloadFile) downloadButton.grid() loadingLabel = ttk.Label(root,text='App developed by |> CID', font=('Agency FB',20)) loadingLabel.grid() root.mainloop()
额外小优化
我还帮你调整了文件大小的打印格式,从{:00.00f}改成了{:.2f},这样能显示更合理的小数位数,比如97.5MB而不是只显示整数97。
内容的提问来源于stack exchange,提问作者Aynur Jumageldiyeva

