How to stream audio from a Youtube URL in Python (without download)? - python

I have been trying to create a way to stream a youtube url (preferably audio only, although this doesn't matter too much) right from python code. I have tried numerous things but none really seem to work. So far, I am able to search for videos or playlists using youtube data api, grab the first video or playlist and pass it into pafy to get different streaming urls. Does anyone know of a way to play youtube audio/video through python without downloading the video first? I would think it is possible with a cmd line tool such as mplayer or vlc using the sub process to pop open a cmd for the cmd line and pass in the url, but I am stuck. Any help is needed. Please! Here is my following code:
import argparse
import pafy
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
DEVELOPER_KEY = 'DEVELOPER KEY'
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'
def pafy_video(video_id):
url = 'https://www.youtube.com/watch?v={0}'.format(video_id)
vid = pafy.new(url)
def pafy_playlist(playlist_id)
url = "https://www.youtube.com/playlist?list={0}".format(playlist_id)
playlist = pafy.get_playlist(url)
def youtube_search(options):
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY)
search_response = youtube.search().list(
q='Hello world',
part='id,snippet',
maxResults=options.max_results
).execute()
videos = []
playlists = []
channels = []
for search_result in search_response.get('items', []):
if search_result['id']['kind'] == 'youtube#video':
videos.append('%s' % (search_result['id']['videoId']))
elif search_result['id']['kind'] == 'youtube#channel':
channels.append('%s' % (search_result['id']['channelId']))
elif search_result['id']['kind'] == 'youtube#playlist':
playlists.append('%s' % (search_result['id']['playlistId']))
if videos:
print('Videos:{0}'.format(videos))
pafy_video(videos[0])
elif playlists:
print('Playlists:{0}'.format(playlists))
pafy_video(playlists[0])
#https://www.youtube.com/watch?v=rOU4YiuaxAM
#url = 'https://www.youtube.com/watch?v={0}'.format(videos[0])
#print(url)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--q', help='Search term', default='Google')
parser.add_argument('--max-results', help='Max results', default=3)
args = parser.parse_args()
youtube_search(args)
Tldr; I would like to stream a youtube video (using the url or id) straight from python code without downloading the video first
Thank you!

pafy according to its documentation do not list playing media directly (at least I didn't find any).
However we can use it to get correct url, and then use player such as vlc to play directly without downloading.
You can download vlc from here
First we get correct / best URL from youtube using pafy
import pafy
import vlc
url = "https://www.youtube.com/watch?v=bMt47wvK6u0"
video = pafy.new(url)
best = video.getbest()
playurl = best.url
Over here playurl is best URL to play.
Then we use VLC to play it.
Instance = vlc.Instance()
player = Instance.media_player_new()
Media = Instance.media_new(playurl)
Media.get_mrl()
player.set_media(Media)
player.play()
This will open a window with no controls (play/pause/stop etc).
You can run these command on the repr window or at python prompt (depending upon how you are using it)
You will need to build one accordingly using vlc commands such as
>>> player.pause() #-- to pause video
>>> player.play() #-- resume paused video. On older versions,
# this function was called resume
>>> player.stop() #-- to stop/end video

The best way to accomplish this is to use mpv with youtube-dl (a light weight media player that comes with a command line console which allows you to stream any type of video/audio codecs via copy and pasting url to mpv console (works for cmd and terminal). Usage: path\to\mpv https://your/video/audioURL.com/
First import these modules - install via pip install bs4 requests
import re, requests, subprocess, urllib.parse, urllib.request
from bs4 import BeautifulSoup
Then create a variable to store music title of your choice
music_name = "Linkin Park Numb"
query_string = urllib.parse.urlencode({"search_query": music_name})
The goal here to use this search query to extract the output title and link of the first video result. We can then endcode the youtube url "https://www.youtube.com/results?search_query=" with the identifier using urllib (eg., "https://www.youtube.com/results?search_query=linkin+park+numb")
formatUrl = urllib.request.urlopen("https://www.youtube.com/results?" + query_string)
This right here re.findall(r"watch\?v=(\S{11})" views the 11 character identifier of all the video results from our query
search_results = re.findall(r"watch\?v=(\S{11})", formatUrl.read().decode())
After decoding the content, we can extract the full url by concatenating the main youtube url with the 11 character identifier
clip = requests.get("https://www.youtube.com/watch?v=" + "{}".format(search_results[0]))
clip2 = "https://www.youtube.com/watch?v=" + "{}".format(search_results[0])
print(clip2)
Output ==> https://www.youtube.com/watch?v=kXYiU_JCYtU
To further inspect the content, we can use beautifulsoup to scrape the extact title of the video
inspect = BeautifulSoup(clip.content, "html.parser")
yt_title = inspect.find_all("meta", property="og:title")
for concatMusic1 in yt_title:
pass
print(concatMusic1['content'])
Output ==> Numb (Official Video) - Linkin Park
Finally, to play our music we input this to the command line
subprocess.Popen(
"start /b " + "path\\to\\mpv.exe" + clip2 + " --no-video --loop=inf --input-ipc-server=\\\\.\\pipe\\mpv-pipe > output.txt",
shell=True)
Full script
===================================================================
import re, requests, subprocess, urllib.parse, urllib.request
from bs4 import BeautifulSoup
music_name = "Linkin Park Numb"
query_string = urllib.parse.urlencode({"search_query": music_name})
formatUrl = urllib.request.urlopen("https://www.youtube.com/results?" + query_string)
search_results = re.findall(r"watch\?v=(\S{11})", formatUrl.read().decode())
clip = requests.get("https://www.youtube.com/watch?v=" + "{}".format(search_results[0]))
clip2 = "https://www.youtube.com/watch?v=" + "{}".format(search_results[0])
inspect = BeautifulSoup(clip.content, "html.parser")
yt_title = inspect.find_all("meta", property="og:title")
for concatMusic1 in yt_title:
pass
print(concatMusic1['content'])
subprocess.Popen(
"start /b " + "path\\to\\mpv.exe " + clip2 + " --no-video --loop=inf --input-ipc-server=\\\\.\\pipe\\mpv-pipe > output.txt",
shell=True)
# Alternatively, you can do this for simplicity sake:
# subprocess.Popen("start /b " + "path\\to\\mpv.exe " + clip2 + "--no-video", shell=True)

I made exactly this, including options like moving through the video with a scale, play/pause function, making playlist, shuffle function.
One thing to know is that youtube_dl has trouble with some videos, there is a fork called yt_dlp which works better. For my app, I changed pafy so that it uses yt_dlp instead of youtube_dl. I can share the whole code, it's a lot, but it's pretty good I think.
#! /usr/bin/python3
from tkinter import *
from bs4 import BeautifulSoup
from youtube_search import YoutubeSearch
import re,random, vlc, pafy, datetime, time,yt_dlp
#global vars
ran = 0
x = []
win = Tk()
count = 0
count2=-1
playurl = ""
scalevar = ""
scalevar2= 100
url2 = []
dur = 0
#window, player
win.geometry('610x100')
win.title("Youtube Player")
menubar = Menu(win)
instance = vlc.Instance()
player = instance.media_player_new()
#toggling shuffle
def funcRAN():
global ran
ran = not(ran)
#this function keeps track of time and moves the timescale
def every_second():
global count2
global ran
length = player.get_length()
place = player.get_time()
if player.is_playing() == 0 and len(url2) > (count2 + 1) and len(url2) > 0 and abs(place-length) < 10000:
if ran ==0:
count2 += 1
else:
counttemp = count2
while counttemp == count2:
count2 = random.randrange(len(url2))
scale.set(1)
player.set_time(1)
xvar = funcGO3(url2[count2], playurl)
player.set_time(xvar)
place = 1
if player.is_playing() == 1:
timee =str(datetime.timedelta(seconds = round(place/1000))) + " / " + str(datetime.timedelta(seconds = round(length/1000)))
my_label2.config(text=timee)
scale.set(place)
win.after(1000,lambda:every_second())
def settime(scalevar):
place = player.get_time()
scalevar = int(scalevar)
if abs(scalevar - place) > 4000:
player.set_time(scalevar)
def songskip(amount):
global count2
global ran
win.focus()
if ran == 0:
skip = count2 + amount
else:
counttemp = count2
while counttemp == count2:
count2 = random.randrange(len(url2))
skip = count2
if amount == -1 and player.get_time() > 10000:
player.set_time(0)
elif skip >= 0 and skip < len(url2):
count2 = skip
funcGO3(url2[skip], playurl)
#this function is for downloading the song
def funcDL(song_url, song_title):
outtmpl = song_title + '.%(ext)s'
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': outtmpl,
'postprocessors': [
{'key': 'FFmpegExtractAudio','preferredcodec': 'mp3',
'preferredquality': '192',
},
{'key': 'FFmpegMetadata'},
],
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info("youtube.com" + song_url, download=True)
#this function is for moving back and forth in time of the song
def funcSKIP(amount):
win.focus()
timee = player.get_time() + amount
if timee < 0:
timee = 0
timeall = player.get_length()
if timee>timeall:
timee = timeall
scale.set(timee)
#to pause
def funcPPTRANSFER():
if str(win.focus_get()) != str(".!entry"):
funcPP()
#to pause
def funcPP():
win.focus()
pause = player.is_playing()
player.set_pause(pause)
if pause == 1:
btnPP.config(text="|>")
else:
btnPP.config(text="||")
#moving through the songlist
def funcMOVE(title, move, resultcount):
win.focus()
global count
count += move
if count < 0:
count =resultcount
if count > resultcount:
count = 0
my_label.config(text = title[count])
#starting the song
def funcGO(title, URL, playurl):
global count2
global count
win.focus()
instance.vlm_stop_media(playurl)
URL = URL[count]
url = "https://www.youtube.com" + str(URL)
count2 += 1
url2.insert(count2,url)
video = pafy.new(url)
#best = video.getbestaudio()
best = video.audiostreams[0]
print(best.quality, int(best.get_filesize()/10000)/100, "mb")
printer = video.title
playurl = best.url
media=instance.media_new(playurl)
media.get_mrl()
player.set_media(media)
btnPP.place(x=340, y=2)
btnGO2.place(x=170, y=62)
btnBACK.place(x=295, y=2)
btnBACK2.place(x=240, y=2)
btnFWD.place(x=395, y=2)
btnFWD2.place(x=445, y=2)
scale.place(x=370, y=68)
player.play()
btnPP.config(text="||")
scale.set(0)
while player.is_playing() == 0:
time.sleep(1)
scale.config(to = player.get_length())
win.title(title[count])
#this is to select the next song in the list
def funcGO2(URL):
global url2
global count
win.focus()
URL2 = URL[count]
url2.append("https://www.youtube.com" + str(URL2))
#this is to play the next song in the list
def funcGO3(url, playurl):
win.focus()
instance.vlm_stop_media(playurl)
video = pafy.new(url)
#best = video.getbestaudio()
best = video.audiostreams[0]
print(best.quality, int(best.get_filesize()/10000)/100, "mb")
printer = video.title
playurl = best.url
media=instance.media_new(playurl)
media.get_mrl()
btnPP.place(x=340, y=2)
btnGO2.place(x=170, y=62)
btnBACK.place(x=295, y=2)
btnBACK2.place(x=240, y=2)
btnFWD.place(x=395, y=2)
btnFWD2.place(x=445, y=2)
scale.place(x=370, y=68)
player.set_media(media)
player.set_time(1)
player.play()
player.set_time(1)
btnPP.config(text="||")
scale.set(0)
scalevar = scale.get()
while player.is_playing() == 0:
time.sleep(1)
player.set_time(1)
length = player.get_length()
scale.config(to = length)
timee =str(datetime.timedelta(seconds = 0)) + " / " + str(datetime.timedelta(seconds = round(length/1000)))
my_label2.config(text=timee)
win.title(printer)
#win.after(2000, lambda:every_second(player, scale, count2))
return(scalevar)
#controlling the volume
def funcSCALE(scalevar2):
player.audio_set_volume(int(scalevar2))
#extracting info from the html of the search
def titlee(URL, question):
results = YoutubeSearch(URL, max_results=40).to_dict()
title=[]
URL=[]
resultcount = -1
for v in results:
length = v['duration']
if length.count(':') > 1 and dur == 1:
continue
if length.count(':') == 1:
m, s = length.split(':')
length = int(m) * 60 + int(s)
if length > 300 and dur == 1:
continue
URL.append(v['url_suffix'])
resultcount+= 1
print(resultcount)
title.append(re.sub(r"[^a-zA-Z0-9 .,:;+-=!?/()öäßü]", "", v['title']))
if question == 1:
url2.append("https://www.youtube.com" + str(v['url_suffix']))
if resultcount == 20:
break
btnDL.place(x=505, y=2)
return(URL, title, 0, resultcount)
#making the search
def func(event):
global count
global x
btnGO.focus()
btnDL.place(x=500, y=2)
x = str(var.get())
URL, title, count, resultcount = titlee(x, 0)
btnL.config(command = (lambda: funcMOVE(title, -1, resultcount)))
btnR.config(command = (lambda:funcMOVE(title, 1, resultcount)))
btnGO.config(command = (lambda: funcGO(title,URL, playurl)))
btnGO2.config(command = (lambda: funcGO2(URL)))
btnDL.config(command =(lambda: funcDL(URL[count],title[count])))
my_label.config(text = title[count])
#import all songs from querie
def funcImportall():
global x
titlee(x, 1)
#toggle limit duration of song
def funcDur():
global dur
dur = not(dur)
#clear playlist
def funcClear():
global url2
url2 = []
btnPP = Button(win, text = "||", command =(lambda: funcPP()))
btnBACK = Button(win, text = "<", command =(lambda: funcSKIP(-10000)))
btnBACK2 = Button(win, text = "<<", command =(lambda: songskip(-1)))
btnFWD = Button(win, text = ">", command =(lambda: funcSKIP(10000)))
btnFWD2 = Button(win, text = ">>", command =(lambda: songskip(1)))
btnDL = Button(win, text = "↓")
btnL = Button(win, text = "<-")
btnR = Button(win, text = "->")
btnGO = Button(win, text = "OK")
btnGO2 = Button(win, text = "+")
scale = Scale(win, from_=0, to=1000, orient=HORIZONTAL,length=200, variable = scalevar, showvalue=0, command = settime)
scale2 = Scale(win, from_=200, to=0, orient=VERTICAL,length=80, variable = scalevar2, showvalue=0, command = funcSCALE)
scale2.place(x=580, y=2)
scale2.set(100)
my_label = Label(win, text = "")
my_label.place(x=5, y=36)
my_label2 = Label(win, text = "")
my_label2.place(x=220, y=66)
var = Entry(win, width=20)
var.place(x=5, y=5)
var.focus()
var.bind('<Return>', func)
btnL.place(x=5, y=62)
btnR.place(x=60, y=62)
btnGO.place(x=115, y=62)
win.bind_all("<Button-1>", lambda event: event.widget.focus_set())
filemenu = Menu(win, tearoff=0)
filemenu.add_command(label="toggle shuffle", command=funcRAN)
filemenu.add_command(label="toggle limit duration", command=funcDur)
editmenu = Menu(menubar, tearoff=0)
editmenu.add_command(label="all results to playlist", command=funcImportall)
editmenu.add_command(label="clear playlist", command=funcClear)
menubar.add_cascade(label="Options", menu=filemenu)
menubar.add_cascade(label="Playlists", menu=editmenu)
win.config(menu=menubar)
win.bind('<space>',lambda event:funcPPTRANSFER())
win.after(2000, lambda:every_second())
var.focus()
win.mainloop()

Related

Tkinter GUI to be able to change variables of an separate continuous process

I have designed an marine engine simulator - it's a program that sends some serial DATA using a certain protocol called "BlueVision". The data is encoded based on a header, a block number, a block type, actual data (indexed), a checksum and a footer. Due to the fact that if you want to change a value in the actual data - then the checksum changes - I designed an recalculation of the block with the correct checksum when the data is changed. I made a GUI that allows me to change the value of 2 data points in order to be able to test live. The problem is that I don't know how to use threads or subprocesses correctly and the window keeps on freezing. The application works - but works badly and I kept on searching for a similar issue - and did find some suggestions but I haven't been able to implement it. Can someone help? PS: the code is awkward at best - please don't judge - I know I could of made it way better - but this is the best I could do with the time I had. The issue might not be clear to you if you don't have a COM port where the program writes to.
# Blue Vision is a MTU serial communication protocol - based on HEX values grouped in BLOCKS
import tkinter as tk
from tkinter import ttk
import time
import serial
def split_by_n(seq, n):
while seq:
yield seq[:n]
seq= seq[n:]
def after(self, ms, func=None, *args):
"""Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel."""
win = tk.Tk()
win.title("GUI - TEST VERSION")
win.geometry("750x250")
label = tk.Label(win, text = "Main Engine Overspeed")
label.pack()
v = tk.StringVar()
v.set('00')
c = tk.StringVar()
c.set("00")
def setText(word):
v.set(word)
a = ttk.Button(win, text ="01", command =lambda:setText("01"))
a.pack()
b = ttk.Button(win, text="00", command = lambda:setText("00"))
b.pack()
label1 = tk.Label(win, text ="Main Engine Speed")
label1.pack()
name_entry = tk.Entry(win, textvariable=c)
name_entry.pack()
def task():
MyVar = v.get()
priEngSp =c.get()
if len(priEngSp) == 0:
priEngSp = '00'
block_3 = 'FAF500030002000000290100000001000100000001000001000000000000000000000000000222AF5F'
block_4 = 'FAF500040003000001A000004650000047E00000000000000000000000000007EF4000083D6000000000000000000000000000000000000000000000012C000006D600000000000000000000278D00000000000000007FFFFFFF000000000001991500000000000000000016E36000000000000923D8000971F8000001F40000059F000026AC00002774000005800000251C00000580000027740000283C0000056200001D4C00001F400000061800000000000060FB00004650000036B000007D0000008CA0000006180000251C0000000000000000000000000000284800192D500017A6B00000051B0000251CFFFFFFA8000002580000044C000000FA0000000000000000000006770000CB200000D6D8000006770000CB200000D6D80000060600005DC000000000000027100000000000000000000000000000000000000000000003C2000061A8000000000000000000000000000000000000000000000000000000000000000000000000000000000000363300000EA6000249F0FFFFFB1E000F42400000000000000000000000000000000000000000000032D9AF5F'
block_5 = 'FAF5000500020000005600000000000000000000000000000000000000000000007F7F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034AAF5F'
block_6 = 'FAF5000600020000003D00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000234AF5F'
block_7 = 'FAF5000700030000008C000006280000064A0000064A0000068D0000066B0000068D0000068D000006AE0000000000000000000006AE000006070000060700000607000005E5000005A2000006070000064A00000000000000000000062A000006AE000005A20000350EFFFFF07C00003CDEFFFFE8AC00000000000000000000000000000000000012DEAF5F'
block_8 = 'FAF50008000300000070000000000000112900000000000000000000059C000027740000283C000047E000000000000000000000000000000000000000000000000000000000000000007FFFFFFF7FFFFFFF0000055100002CEC0000000000000000000000000000000000000DD1AF5F'
block_9 = 'FAF50009000200000020000000000000000000000000000000000000021AAF5F'
block_10 = 'FAF5000A0002000000260000000000000000000000000000000000000000000000000221AF5F'
block_11 = 'FAF5000B0003000000EC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E9AF5F'
block_12 = 'FAF5000C000200000045000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000242AF5F'
block_2 = 'FAF50002000200000074010001000000000000000000000000000000000000000000007F0000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002E9AF5F'
checksum = hex(0x00)
block_2split = list(split_by_n(block_2,2))
block_2split[10] = MyVar.upper() #Overspeed Alarm Position
for i in range(len(block_2split)-6):
checksum = hex(int(checksum, 16) + int(block_2split[i], 16))
checksum_string = str(checksum)
checksum_actvalue = checksum_string[2:]
checksum_long = checksum_actvalue.rjust(8,'0').upper()
checksum_split = list(split_by_n(checksum_long,2))
block_2split[len(block_2split)-3] = checksum_split[3]
block_2split[len(block_2split)-4] = checksum_split[2]
block_2split[len(block_2split)-5] = checksum_split[1]
block_2split[len(block_2split)-6] = checksum_split[0]
Block_2Output = ''.join(str(item) for item in block_2split)
iEngineSpeed = int(priEngSp,10)
hEngineSpeed = hex(iEngineSpeed * 10)
sEngineSpeed = str(hEngineSpeed)[2:].upper()
while (len(sEngineSpeed)<8):
sEngineSpeed = '0' + sEngineSpeed
block_4split = list(split_by_n(block_4,4))
sEngineSpeed_split = list(split_by_n(sEngineSpeed,4))
block_4split[5] = sEngineSpeed_split[0]
block_4split[6] = sEngineSpeed_split[1]
Block_4joint = ''.join(str(item) for item in block_4split)
Block_4joint_sp = list(split_by_n(Block_4joint,2))
checksumb4 = hex(0x00)
for i in range(len(Block_4joint_sp)-6):
checksumb4 = hex(int(checksumb4, 16) + int(Block_4joint_sp[i], 16))
checksumb4_string = str(checksumb4)
checksumb4_actvalue = checksumb4_string[2:]
checksumb4_long = checksumb4_actvalue.rjust(8,'0').upper()
checksumb4_split = list(split_by_n(checksumb4_long,2))
Block_4joint_sp[len(Block_4joint_sp)-3] = checksumb4_split[3]
Block_4joint_sp[len(Block_4joint_sp)-4] = checksumb4_split[2]
Block_4joint_sp[len(Block_4joint_sp)-5] = checksumb4_split[1]
Block_4joint_sp[len(Block_4joint_sp)-6] = checksumb4_split[0]
Block_4Output = ''.join(str(item) for item in Block_4joint_sp)
blocks = [Block_2Output, block_3, Block_4Output, block_5, block_6, block_7, block_8, block_9, block_10, block_11, block_12]
with serial.Serial('COM5', '9600') as ser: #you might wanna comment this part out
for block in blocks:
print(block)
ser.write(bytes.fromhex(block.strip())) #you might wanna comment this part out
time.sleep(1)
win.after(200, task)
win.after(200, task)
win.mainloop()
To avoid freezing, one of the way is to use thread:
from threading import Thread
...
def task():
while True:
MyVar = v.get()
...
blocks = [Block_2Output, block_3, Block_4Output, block_5, block_6, block_7, block_8, block_9, block_10, block_11, block_12]
with serial.Serial('COM5', '9600') as ser: #you might wanna comment this part out
for block in blocks:
print(block)
ser.write(bytes.fromhex(block.strip())) #you might wanna comment this part out
time.sleep(1)
time.sleep(0.2)
# run task() in a thread
Thread(target=task, daemon=1).start()
win.mainloop()

Python error: urllib.error.HTTPError: HTTP Error 404: Not Found

I am building a python project for the school (YouTube video downloader), but when I run it, it appears this error: urllib.error.HTTPError: HTTP Error 404: Not Found
I have already tried other codes from other people and no one worked, I think is my computer. What can I do?
Here's the code:
from tkinter import *
from tkinter import ttk
from tkinter import filedialog
from pytube import YouTube #pip install pytube3
Folder_Name = ""
#file location
def openLocation():
global Folder_Name
Folder_Name = filedialog.askdirectory()
if(len(Folder_Name) > 1):
locationError.config(text=Folder_Name,fg="green")
else:
locationError.config(text="Please Choose Folder!!",fg="red")
#donwload video
def DownloadVideo():
choice = ytdchoices.get()
url = ytdEntry.get()
if(len(url)>1):
ytdError.config(text="")
yt = YouTube(url)
if(choice == choices[0]):
select = yt.streams.filter(progressive=True).first()
elif(choice == choices[1]):
select = yt.streams.filter(progressive=True,file_extension='mp4').last()
elif(choice == choices[2]):
select = yt.streams.filter(only_audio=True).first()
else:
ytdError.config(text="Paste Link again!!",fg="red")
#download function
select.download(Folder_Name)
ytdError.config(text="Download Completed!!")
root = Tk()
root.title("YTD Downloader")
root.geometry("350x400") #set window
root.columnconfigure(0,weight=1)#set all content in center.
#Ytd Link Label
ytdLabel = Label(root,text="Enter the URL of the Video",font=("jost",15))
ytdLabel.grid()
#Entry Box
ytdEntryVar = StringVar()
ytdEntry = Entry(root,width=50,textvariable=ytdEntryVar)
ytdEntry.grid()
#Error Msg
ytdError = Label(root,text="Error Msg",fg="red",font=("jost",10))
ytdError.grid()
#Asking save file label
saveLabel = Label(root,text="Save the Video File",font=("jost",15,"bold"))
saveLabel.grid()
#btn of save file
saveEntry = Button(root,width=10,bg="red",fg="white",text="Choose Path",command=openLocation)
saveEntry.grid()
#Error Msg location
locationError = Label(root,text="Error Msg of Path",fg="red",font=("jost",10))
locationError.grid()
#Download Quality
ytdQuality = Label(root,text="Select Quality",font=("jost",15))
ytdQuality.grid()
#combobox
choices = ["720p","144p","Only Audio"]
ytdchoices = ttk.Combobox(root,values=choices)
ytdchoices.grid()
#donwload btn
downloadbtn = Button(root,text="Donwload",width=10,bg="red",fg="white",command=DownloadVideo)
downloadbtn.grid()
#developer Label
developerlabel = Label(root,text="Dream Developers",font=("jost",15))
developerlabel.grid()
root.mainloop()
And here is the terminal:
Terminal
Terminal
I tested: code doesn't work with pytube-10.4.1 but works with the newest pytube-10.8.3
It seems YouTube changed something on server and it needed changes in pytube.

How to stop htmlpy ui blocking

I was working on a small downloader for Youtube videos using trollius (for async), htmlpy and pafy (and youtube-dl). Now i have a problem where once i click the download button, the UI freezes up while the backend is downloading the video.
I have tried to make the downloader class its own thread and run that alongside the UI but that doesn't seem to work. I also tried to uses coroutine to have the video download asynchronously while the UI continues. Neither seemed to work.
Here is some code
class Downloader(htmlPy.Object,threading.Thread):
dd = os.path.join(os.getenv('USERPROFILE'), 'Downloads') # dd = download director
dd = dd + "/vindownload/"
def __init__(self, app):
threading.Thread.__init__(self)
super(Downloader, self).__init__()
# Initialize the class here, if required.
self.app = app
return
#htmlPy.Slot(str)
def download_single(self, json_data):
form_data = json.loads(json_data)
print json_data
url = form_data["name"]
dt = form_data["dt"] # Download type is audio or video
if url.__contains__("https://www.youtube.com/watch?v="):
if dt == 'audio':
print "hello1"
loop = trollius.get_event_loop()
loop.run_until_complete(self._downloadVid(url, vid=False))
loop.stop()
else:
print "hello1"
loop = trollius.get_event_loop()
loop.run_until_complete(self._downloadVid(url, vid=True))
loop.stop()
self.app.evaluate_javascript("document.getElementById('form').reset()")
else:
print "Incorrect url"
print form_data
#trollius.coroutine
def _downloadVid(self, url, vid=False, order_reverse=False, vinName=None):
print "hello123"
video = pafy.new(url)
print video
name = u''.join(video.title).encode('utf8')
name = re.sub("[<>:\"/\\|?*]", "", name)
if not vid:
file = video.getbestaudio()
else:
file = video.getbest()
if (order_reverse):
file.download(self.dd + vinName + name + ".mp4", quiet=False,callback=self.mycb)
else:
file.download(self.dd + name + ".mp4", quiet=False,callback=self.mycb)
def mycb(self,total, recvd, ratio, rate, eta):
print(recvd, ratio, eta)
and my initialize.py
BASE_DIR = os.path.abspath(os.path.dirname("initilize.py"))
app = htmlPy.AppGUI(title=u"Vin download", width=700, height=400, resizable=False)
app.static_path = os.path.join(BASE_DIR, "static/")
app.template_path = os.path.join(BASE_DIR, "templates/")
app.web_app.setMaximumWidth(830)
app.web_app.setMaximumHeight(600)
download = Downloader(app)
download.start()
# Register back-end functionalities
app.bind(download)
app.template = ("./index.html", {"template_variable_name": "value"})
# Instructions for running application
if __name__ == "__main__":
# The driver file will have to be imported everywhere in back-end.
# So, always keep app.start() in if __name__ == "__main__" conditional
app.start()
Now my question is. Is there a way where i can free up my UI while downloading so it doesn't look like the application crashed.
I am using: Python 2.7, Trollius, Pafy, Youtube-dl, HTMLPY.
Thank you for your time.
Alright i found the answer to my question, here is what i did. i changed my download_single method to the following:
#htmlPy.Slot(str)
def download_single(self, json_data):
form_data = json.loads(json_data)
print json_data
url = form_data["name"]
dt = form_data["dt"] # Download type is audio or video
videoDown = videoDownload(url, dt, self.app, self.dd)
videoDown.start()
print form_data
And the code i had in it earlier is now transferred to a new class called videoDownload taking all those attributes stated above.
videoDownload.py
class videoDownload(threading.Thread):
def __init__(self, url, dt, app, dd):
threading.Thread.__init__(self)
self.url = url
self.dt = dt
self.app = app
self.dd = dd
def run(self):
threads.append(self)
if self.url.__contains__("https://www.youtube.com/watch?v="):
if self.dt == 'audio':
print "hello1"
self._downloadVid(self.url, vid=False)
else:
print "hello1"
self._downloadVid(self.url, vid=True)
else:
print "Incorrect url"
def _downloadVid(self, url, vid=False, order_reverse=False, vinName=None):
print "hello123"
video = pafy.new(url)
print video
name = u''.join(video.title).encode('utf8')
name = re.sub("[<>:\"/\\|?*]", "", name)
if not vid:
file = video.getbestaudio()
else:
file = video.getbest()
if (order_reverse):
file.download(self.dd + vinName + name + ".mp4", quiet=False, callback=self.mycb)
else:
file.download(self.dd + name + ".mp4", quiet=False, callback=self.mycb)
threads.remove(self)
def mycb(self, total, recvd, ratio, rate, eta):
pass
This solved the issues i had with ui being blocked. The thread will end itself once leaving the run method and it will be removed from a threads array that is defined above the class.
Have a great day all
~Ellisan

Problems with executing a popup tkinter window

In this app I'm trying to execute a popup in which an user can write a date. This popup has to occur after the user clicks a submit button I have already created. The date the user input into this popup has to be saved into a variable which will be used later on on the code. In order to do all this I tried the following:
def CreateOrderPop(self):
def popup():
#contenido = input("Contenido de Orden ")
#diaDeEntregar = input("Dia de Entrega")
self.userentryA = Entry("Dia de Entrega: ")
self.userentryA.pack()
self.userentryAbu = Button(text= "Guardar", command = self.guardarFechaOrden)
self.userentryAbu.pack()
def guardarFechaOrden(self):
global userDate
userDate = self.userentryA.get()
self.destroy()
def submit(self):
result = next(self.counter)
global orderResult
orderResult = str(result)
global contents1
contents1 = ("Nombre: {}".format(self.entry_name.get()))
global contents2
contents2 = ("Email: {}".format(self.entry_email.get()))
global contents3
contents3 = ("Num Cel/Tel: {}".format(self.entry_numtc.get()))
global contents4
contents4 = ("Información Adicional: {}".format(self.entry_addinf.get(1.0, "end")))
def CreateOrder():
fecha = datetime.now()
fechaDeCreacion = fecha.strftime(" %A, %B %d, %Y" )
#diaDeEntregar = userDate
#global fechaDeEntrega
#fechaDeEntrega = fechaDeCreacion + str(diaDeEntregar)
numOrden = orderResult
return fechaDeCreacion, orderResult
completeOrden = [contents1, contents2, contents3, contents4, CreateOrder()]
completeOrdenEnum = "Orden Num:" + orderResult, completeOrden
Database.mainDatabase.append(completeOrdenEnum)
command = self.CreateOrderPop()
After running the code and clicking the submit button, everything runs normal except I don't get the popup I want.
CHANGES
I added this class to help me create what I was looking for:
class PopOrden:
def __init__(self,master):
self.master = master
top=self.top=Toplevel(master)
self.l=Label(top,text="Fecha de Entrega")
self.l.pack()
self.e=Entry(top)
self.e.pack()
self.b=Button(top,text='Ok',command=self.cleanup)
self.b.pack()
def cleanup(self):
self.value=self.e.get()
self.top.destroy()
def entryValue(self):
return self.w.value
print(self.w.value)
The previous code along with this edited code:
def submit(self):
result = next(self.counter)
print (result)
def controLoo():
if result == 1:
self.CreateOrderPop()
command = controLoo()
global orderResult
orderResult = str(result)
global contents1
contents1 = ("Nombre: {}".format(self.entry_name.get()))
global contents2
contents2 = ("Email: {}".format(self.entry_email.get()))
global contents3
contents3 = ("Num Cel/Tel: {}".format(self.entry_numtc.get()))
global contents4
contents4 = ("Información Adicional: {}".format(self.entry_addinf.get(1.0, "end")))
def CreateOrder():
fecha = datetime.now()
fechaDeCreacion = fecha.strftime(" %A, %B %d, %Y" )
#diaDeEntregar = PopOrden
#global fechaDeEntrega
#fechaDeEntrega = fechaDeCreacion + str(diaDeEntregar)
numOrden = orderResult
return fechaDeCreacion, orderResult
completeOrden = [contents1, contents2, contents3, contents4, CreateOrder()]
completeOrdenEnum = "Orden Num:" + orderResult, completeOrden
Database.mainDatabase.append(completeOrdenEnum)
command = self.database_window()
self.clear()
messagebox.showinfo(title = "Orden #"+ orderResult, message = "Orden Guardada")
However, I'm NOW having issues with a blank tk popu that's also generated with the popup I want.
I am not sure what you mean by everything runs normal, because your code seems to have some major formatting issues (indentation to say the least). However, 'pop-ups' are usually achieved with Toplevel() widgets. See this useful resource. It is a great resource for all things tkinter in my opinion.
Also, you might find the answer to this question helpful.
Why dont you use a message box directly
from tkinter import *
import tkMessageBox
root = Tk()
def popUp():
result = tkinter.messageBox.popUp("Quiz","Are you ready? ")
# result wil be yes or no
if result == 'yes':
#do something
else:
# do something
submitButton = Button(root,text= "Submit")
submitButton.bind("<Button-1",popup)
# onleft click on submit popup method gets called
submitButton.pack()

Using global variables in Python

I am using Tkinter to help me build a FTP client, in this client I am trying to get the selected information from a tk listbox. So I have a button that starts the download but what ever the reason is it pops up with the error "
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1410, in __call__
return self.func(*args)
File "/Volumes/LEGO FLASH/ftp.py", line 23, in Download
filename = stuff
NameError: global name 'stuff' is not defined"
Below I have the code for you to look at:
# Import the FTP object from ftplib
from ftplib import FTP
from Tkinter import *
import os
app = Tk()
app.title("FTP")
app.geometry("300x300")
lines = []
#[lines.replace(",", "\n")for lines in lines]
#lines = lines.replace(',','\n')
def handleDownload(block):
file.write(block)
print ".",
def append_line(line):
lines.append(line)
#This is where I am caught------->
def Download():
filename = stuff
file = open(filename, 'wb')
ftp.retrbinary('RETR ' + filename, handleDownload)
ftp.close()
def login():
try:
ftp.login(username.get(),password.get())
except:
error = Label(app, text = "Invalid USERNAME OR PASSWORD")
label2 = Label(app, text = "Welcome to Steam Engine").pack()
username.forget()
password.forget()
button.forget()
app.geometry("800x500")
download = Button(app, text = "Download!!!!!", command = Download)
download.pack(side = "left", pady = "5")
scrollBar.pack(fill = Y, side = "right", padx = "2")
#ftp.cwd('The_Store')
stuff = Listbox(app, height = "700", width = "500")
ftp.retrlines('NLST', append_line)
for i in lines:
stuff.insert(END, i)
stuff.pack(padx = "10", pady = "10")
stuff.config(yscrollcommand = scrollBar.set)
scrollBar.config(command = stuff.yview)
ftp = FTP('sciphigames.com')
label = Label(app, text = "Login").pack(pady = "10")
scrollBar = Scrollbar(app)
username = StringVar(None)
username = Entry(app, text = "Username: ")
username.pack(pady = "2")
password = StringVar(None)
password = Entry(app, text = "Password: ")
password.pack(pady = "2")
button = Button(app, text = "Login!", command = login)
button.pack(pady = "10")
app.mainloop()
Any help would be appreciated!
Thanks!
#This is where I am caught------->
def Download():
filename = stuff
what is stuff here ?? it is not a global variable, it seems to be a parameter of login, but not of Download method.
If you want to do stuff a global variable (probably not the better choice), use the global statement.
# Import the FTP object from ftplib
from ftplib import FTP
from Tkinter import *
import os
# define the global stuff
global stuff
...
def Download():
global stuff
filename = stuff
...
A better way to handle this would be to create an object around all of this ::
class NetworkApp(object):
def login(self):
# here put all the previous code of login
# here we change the callback to self.Download
download = Button(app, text = "Download!!!!!", command = self.Download)
# here we're creating a stuff member
self.stuff = Listbox(app, height = "700", width = "500")
def Download(self):
filename = self.stuff # here we use the stuff member
file = open(filename, 'wb')
ftp.retrbinary('RETR ' + filename, handleDownload)
ftp.close()
#...
net_app = NetworkApp()
button = Button(app, text = "Login!", command = net_app.login)
I am not entirely sure what the purpose is with your stuff variable, but the problems you are experiencing probably stem from the way you are using it.
First, you are using it as argument to login (which, by the way should take no arguments). You assign to this variable from the login function, and refer to another variable with the same name in your Download function.
Again, not being sure what I understand what you want to do with the stuff variable, I would try something like
.....
app.geometry("300x300")
stuff = None # <<<<----
lines = []
.....
#This is where I am caught------->
def Download():
global stuff # <<<<----
filename = stuff
......
ftp.close()
def login():
global stuff # <<<<----
......
stuff = Listbox(app, height = "700", width = "500")
ftp.retrlines('NLST', append_line)
for i in lines:
stuff.insert(END, i)
stuff.pack(padx = "10", pady = "10")
stuff.config(yscrollcommand = scrollBar.set)
scrollBar.config(command = stuff.yview)
......
All you need to do is put "global" in front of the variable
global var1

Categories

Resources