Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- # Version 12
- # Prevent creation of the __pycache__ directory.
- # SOURCE: https://github.com/pytest-dev/pytest/issues/200
- import sys
- sys.dont_write_bytecode = True
- # -----
- import ctypes
- import uuid
- def known_folder(idstr):
- # Convert the GUID string (e.g. '{374DE...}') into a UUID object,
- # then get its little-endian 16-byte representation as required by Windows.
- fid = uuid.UUID(idstr).bytes_le
- # Create a real C array of 16 unsigned bytes initialized with the GUID bytes.
- # This produces a stable block of memory that SHGetKnownFolderPath() can read.
- guid = (ctypes.c_byte * 16)(*fid)
- # Prepare a wchar* (wide char pointer) to receive the resulting folder path.
- # SHGetKnownFolderPath will allocate and fill this pointer.
- out = ctypes.c_wchar_p()
- # Call the Windows API:
- # HRESULT SHGetKnownFolderPath(
- # REFKNOWNFOLDERID rfid, // pointer to our 16-byte GUID
- # DWORD dwFlags, // we pass 0 (default behavior)
- # HANDLE hToken, // None = current user
- # PWSTR *ppszPath // receives allocated wide-string path
- # )
- ctypes.windll.shell32.SHGetKnownFolderPath(
- guid, # pointer to 16-byte GUID array
- 0, # flags = 0
- None, # user token = current user
- ctypes.byref(out) # address of out pointer to receive the path
- )
- # Return the actual string value (Python automatically converts wchar*).
- return out.value
- # Common Known Folders GUIDs (user profile related only).
- KnownFolders = {
- 'Desktop': '{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}',
- 'Documents': '{FDD39AD0-238F-46AF-ADB4-6C85480369C7}',
- 'Downloads': '{374DE290-123F-4565-9164-39C4925E467B}',
- 'Music': '{4BD8D571-6D19-48D3-BE97-422220080E43}',
- 'Pictures': '{33E28130-4E1E-4676-835A-98395C3BC3BB}',
- 'Videos': '{18989B1D-99B5-455B-841C-AB7C74E4DDFC}',
- 'Favorites': '{1777F761-68AD-4D8A-87BD-30B759FA33DD}',
- 'Links': '{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}',
- 'SavedGames': '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}',
- 'SavedSearches': '{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}',
- 'Contacts': '{56784854-C6CB-462B-8169-88E350ACB882}',
- 'AppDataLocal': '{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}',
- 'AppDataLocalLow': '{A520A1A4-1780-4FF6-BD18-167343C5AF16}',
- 'AppDataRoaming': '{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}',
- 'DesktopDirectory': '{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}', # same as Desktop
- 'Profile': '{5E6C858F-0E22-4760-9AFE-EA3317B67173}', # C:\Users\<User>
- 'Public': '{DFDF76A2-C82A-4D63-906A-5644AC457385}',
- 'OneDrive': '{A52BBA46-E9E1-435f-B3D9-28DAA648C0F6}',
- 'OneDriveDocuments': '{24D89E24-2F19-4534-9DDE-6A6671FBB8FE}',
- 'OneDrivePictures': '{339719B5-8C47-4894-94C2-D8F77ADD44A6}',
- 'OneDriveMusic': '{0077D3A4-6F62-46A1-8F79-6BCB9B93D15B}',
- }
- # -----
- # Winget adds yt-dlp.exe to PATH so don't need to hard code it here.
- YT_DLP_PATH = r'yt-dlp'
- DOWNLOAD_DIRECTORY = known_folder(KnownFolders['Downloads'])
- # -----
- import ctypes
- import os
- import subprocess
- import sys
- def is_admin():
- try:
- if sys.platform == 'win32':
- return ctypes.windll.shell32.IsUserAnAdmin()
- else:
- return os.geteuid() == 0
- except:
- return False
- def check_permissions():
- if is_admin():
- return
- print('Requesting administrative privileges . . .')
- try:
- if sys.platform == 'win32':
- params = ' '.join([f'"{arg}"' for arg in sys.argv])
- result = ctypes.windll.shell32.ShellExecuteW(None, 'runas', sys.executable, params, None, 1)
- if result <= 32: # SOURCE: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew If the function succeeds, it returns a value greater than 32.
- sys.exit(1)
- else:
- try:
- script = os.path.abspath(sys.argv[0])
- args = [script] + sys.argv[1:]
- subprocess.run(['sudo', sys.executable] + args)
- except KeyboardInterrupt:
- pass
- except Exception as e:
- print(f'An error occurred: {e}')
- sys.exit()
- def update_yt_dlp():
- # Update yt-dlp and wait for it to finish.
- print('Updating yt-dlp . . .')
- try:
- subprocess.run([YT_DLP_PATH, '--update'], check=True)
- except subprocess.CalledProcessError as e:
- print('ERROR: Failed to update yt-dlp.', e)
- check_permissions() # Restart script and try again with administrator privileges.
- # If script is already running as administrator.
- wait_for_any_keypress()
- sys.exit(1)
- def get_video_url():
- url = None
- while True:
- try:
- url = input('Youtube URL: ')
- if not str_is_none_or_whitespace(url):
- break
- clear()
- except ValueError as e:
- print(e)
- return url
- def str_is_none_or_whitespace(text):
- return not text or text.isspace()
- import os
- def clear():
- os.system('cls' if os.name in ('nt', 'dos') else 'clear')
- import subprocess
- def main():
- update_yt_dlp()
- print()
- print('Download Directory:', DOWNLOAD_DIRECTORY)
- print()
- url = get_video_url()
- print()
- subprocess.run([YT_DLP_PATH, '--list-formats', url])
- cmd = [
- YT_DLP_PATH,
- '-f', 'bestvideo+bestaudio', # yt-dlp chooses what it thinks is the "best" combination (sometimes not the absolute largest resolution).
- #'-f', 'bv*+ba*', # explicitly forces maximum resolution video and best audio, no matter what yt-dlp's ranking says.
- #'-f', 'bestvideo[vcodec=vp9]+bestaudio', # yt-dlp assumes AV1 is "better" because it's newer and more efficient, but if your goal is maximum resolution + maximum bitrate, you need to override its preference. (VP9) → larger file, higher bitrate, potentially better quality if you want maximum fidelity.
- #'--merge-output-format', 'mp4', # re-encoding may cause quality loss.
- '--output', '%(title)s-%(id)s.%(ext)s',
- '--verbose',
- ]
- # cookies_path = os.path.join(os.getcwd(), 'cookies.txt')
- # if os.path.exists(cookies_path):
- # cmd += ['--cookies', cookies_path]
- # print(f'Loading cookies: {cookies_path}')
- # else:
- # print('WARNING: cookies.txt not found in the current directory. Proceeding without cookies.')
- # SOURCE: https://github.com/yt-dlp/yt-dlp/wiki/FAQ
- # Firefox/Librewolf: User agent from about:support
- # Brave: User agent from brave://version
- # Chrome: User agent from chrome://version
- cmd += ['--user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:153.0) Gecko/20100101 Firefox/153.0']
- # SOURCE: https://github.com/yt-dlp/yt-dlp/issues/14050
- if os.name in ('nt', 'dos'):
- cmd += ['--cookies-from-browser', 'firefox:%APPDATA%/librewolf']
- else:
- # TODO: Untested
- cmd += ['--cookies-from-browser', 'firefox:${HOME}/.librewolf/']
- #cmd += ['--cookies-from-browser', 'firefox:./.librewolf/']
- cmd.append(url)
- try:
- subprocess.run(cmd, cwd=DOWNLOAD_DIRECTORY, check=True)
- except subprocess.CalledProcessError as e:
- print('ERROR: Failed to download video from url:', url)
- wait_for_any_keypress()
- sys.exit(1)
- print('Download complete . . .')
- print()
- wait_for_any_keypress()
- # ----- Press any key to continue -----
- def wait_for_any_keypress():
- import sys
- if sys.platform == 'win32':
- import os
- os.system('pause')
- elif sys.platform.startswith('linux') or sys.platform == 'darwin':
- print('Press any key to continue . . .')
- import termios
- import tty
- stdin_file_desc = sys.stdin.fileno()
- old_stdin_tty_attr = termios.tcgetattr(stdin_file_desc)
- try:
- tty.setraw(stdin_file_desc)
- sys.stdin.read(1)
- finally:
- termios.tcsetattr(stdin_file_desc, termios.TCSADRAIN, old_stdin_tty_attr)
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment