I'm new to Python, and have only worked with PHP 5 in the past, (many years ago now). As a beginner project I thought I'd make a YouTube downloader using pytube that lets you choose whether to download a video in highest quality or only download the audio from it as a .mp3.
Well I'm stuck on the last part: changing the extension to .mp3.
I'd like a simple and elegant solution that I can understand, but any help will be appreciated.
I tried using os.rename() but am unsure how to make it work.
from pytube import YouTube
import os
yt = YouTube(str(input("Enter the URL of the video you want to download: \n>> ")))
print("Select download format:")
print("1: Video file with audio (.mp4)")
print("2: Audio only (.mp3)")
media_type = input()
if media_type == "1":
video = yt.streams.first()
elif media_type == "2":
video = yt.streams.filter(only_audio = True).first()
else:
print("Invalid selection.")
print("Enter the destination (leave blank for current directory)")
destination = str(input(">> "))
video.download(output_path = destination)
if media_type == "2":
os.rename(yt.title + ".mp4", yt.title + ".mp3")
print(yt.title + "\nHas been successfully downloaded.")
EDIT:
It just hung on the last part yesterday when I tried it, but I just tried again and after a while I got this error message:
Traceback (most recent call last):
File "Tubey.py", line 42, in <module>
os.rename(yt.title + ".mp4", yt.title + ".mp3")
FileNotFoundError: [WinError 2] The system cannot find the file specified: "Cristobal Tapia de veer - DIRK GENTLY's original score sampler - cut 3.mp4" -> "Cristobal Tapia de veer - DIRK GENTLY's original score sampler - cut 3.mp3"
The file got downloaded but did not get renamed.
FINAL EDIT: (probably)
I finally got it working, mostly thanks to J_H. Thank you for putting up with my incompetence, you're a saint. Here's the full code that finally did the trick (in case anyone else who stumbles upon this in the future has a similar problem):
from pytube import YouTube
import os
yt = YouTube(str(input("Enter the URL of the video you want to download: \n>> ")))
print("Select download format:")
print("1: Video file with audio (.mp4)")
print("2: Audio only (.mp3)")
media_type = input(">> ")
if media_type == "1":
video = yt.streams.first()
elif media_type == "2":
video = yt.streams.filter(only_audio = True).first()
else:
print("Invalid selection.")
print("Enter the destination (leave blank for current directory)")
destination = str(input(">> ")) or '.'
out_file = video.download(output_path = destination)
if media_type == "2":
base, ext = os.path.splitext(out_file)
new_file = base + '.mp3'
os.rename(out_file, new_file)
print(yt.title + " has been successfully downloaded.")
I intend to turn this into a long-term project and extend the script with more features the more I learn, but for now I'm satisfied. Thanks again.
It looks like your code only works correctly when the user input ends with trailing slash.
Use os.path.join()
to combine destination directory with filename.
Use this expression to default empty to .
, the current directory:
destination = str(input(">> ")) or '.'
EDIT:
I was hoping that your assumption, that we could predict the output filespec from the title, was correct.
But no.
For example, yt = YouTube('https://www.youtube.com/watch?v=9bZkp7q19f0')
fetches a PSY music video with a title ending in 'M/V'
.
Yet .download()
will (quite reasonably) construct a filename containing just 'MV'
, with no slash.
You should not be trying to predict the output filespec.
Rather, you should store the return value from .download()
,
so you will know for certain what the correct filespec is.
Here is an example of renaming the output to a constant filename:
>>> out_file = yt.streams.first().download()
>>> os.rename(out_file, 'new.mp3')
>>>
Or if you prefer, you could rename it to os.path.join('/tmp', 'new.mp3')
.
You might also wish to parse out the extension using splitext
:
base, ext = os.path.splitext(out_file)
new_file = base + '.mp3'
os.rename(out_file, new_file)
You may find that the only_audio
flag is a convenient way to reduce bandwidth consumed by video,
if you prefer to just get the audio tracks:
yt.streams.filter(only_audio=True).all()
EDIT 2021:
You don't have to use os
,
you can now use:
video.download(output_path=destination, filename=input("Enter file name (without extension): "))