├── py-requirements.txt ├── Readme.md └── youtube_downloader.py /py-requirements.txt: -------------------------------------------------------------------------------- 1 | pytube 2 | argparse 3 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Youtube Video Downloader 2 | 3 | ## How to install 4 | 1. Clone this repository on your computer 5 | `https://github.com/paveldat/youtube-video-downloader.git` 6 | 2. Install all the requirements 7 | `pip install -r requirements.txt` 8 | 3. Run the usage 9 | `python youtube_downloader.py -h` 10 | 11 | ## Advantages 12 | 1. This is an API that can easily be implemented into your project 13 | 2. Supports any links from Youtube 14 | 3. It has argparse for convenient use from the console 15 | 4. Accepts all possible arguments 16 | 17 | ## Usage `python youtube_downloader.py -h` 18 | ```shell 19 | usage: youtube_downloader.py [-h] [-o output_path] [-f filename] [-p filename_prefix] [-s] [-t timeout] [-m max_retries] url 20 | 21 | positional arguments: 22 | url URL to the video 23 | 24 | options: 25 | -h, --help show this help message and exit 26 | -o output_path, --output-path output_path 27 | Output path for writing media file. If one is not specified, defaults to the current working directory. 28 | -f filename, --filename filename 29 | Output filename (stem only) for writing media file. If one is not specified, the default filename is used. 30 | -p filename_prefix, --filename-prefix filename_prefix 31 | A string that will be prepended to the filename. For example a number in a playlist or the name of a series. If one is not specified, nothing will be prepended. This is separate from 32 | filename so you can use the default filename but still add a prefix. 33 | -s, --skip-existing Skip existing files, defaults to True. 34 | -t timeout, --timeout timeout 35 | Request timeout length in seconds. Uses system default. 36 | -m max_retries, --max-retries max_retries 37 | Number of retries to attempt after socket timeout. Defaults to 0. 38 | ``` 39 | -------------------------------------------------------------------------------- /youtube_downloader.py: -------------------------------------------------------------------------------- 1 | """ 2 | ██▄██ ▄▀▄ █▀▄ █▀▀ . █▀▄ █░█ 3 | █░▀░█ █▄█ █░█ █▀▀ . █▀▄ ▀█▀ 4 | ▀░░░▀ ▀░▀ ▀▀░ ▀▀▀ . ▀▀░ ░▀░ 5 | ▒▐█▀█─░▄█▀▄─▒▐▌▒▐▌░▐█▀▀▒██░░░░▐█▀█▄─░▄█▀▄─▒█▀█▀█ 6 | ▒▐█▄█░▐█▄▄▐█░▒█▒█░░▐█▀▀▒██░░░░▐█▌▐█░▐█▄▄▐█░░▒█░░ 7 | ▒▐█░░░▐█─░▐█░▒▀▄▀░░▐█▄▄▒██▄▄█░▐█▄█▀░▐█─░▐█░▒▄█▄░ 8 | """ 9 | 10 | import sys 11 | import argparse 12 | from pathlib import Path 13 | from enum import IntEnum 14 | from pytube import YouTube 15 | 16 | 17 | class DownloaderStatus(IntEnum): 18 | SUCCESS = 0 19 | FAILED = 130 20 | 21 | 22 | class YoutubeDownloader: 23 | """ 24 | Downloads video from Youtube 25 | """ 26 | 27 | @staticmethod 28 | def download(url: str, 29 | output_path: [str, Path] = None, 30 | filename: str = None, 31 | filename_prefix: str = None, 32 | skip_existing: bool = True, 33 | timeout: int = None, 34 | max_retries: int = 0) -> int: 35 | """ 36 | Downloads video from Youtube by url. 37 | 38 | Args: 39 | * url - URL to the video. 40 | * output_path - Output path for writing media file. If one is not 41 | specified, defaults to the current working 42 | directory. 43 | * filename - Output filename (stem only) for writing media file. 44 | If one is not specified, the default filename 45 | is used. 46 | * filename_prefix - 47 | A string that will be prepended to the filename. 48 | For example a number in a playlist or 49 | the name of a series. If one is not specified, 50 | nothing will be prepended. This is separate 51 | from filename so you can use the default filename 52 | but still add a prefix. 53 | * skip_existing - Skip existing files, defaults to True. 54 | * timeout - Request timeout length in seconds. Uses system default. 55 | * max_retries - Number of retries to attempt after socket timeout. 56 | Defaults to 0. 57 | 58 | Returns: 59 | * 0 - Video was downloaded successfuly. 60 | * 130 - An error has occured. 61 | """ 62 | 63 | try: 64 | print(f'\033[34m Trying to download video from: {url} \033[0m') 65 | youtubeObject = YouTube(url) 66 | youtubeObject = youtubeObject.streams.get_highest_resolution() 67 | 68 | print('\033[34m Using: \033[0m') 69 | print(f'\033[34m output_path: {output_path} \033[0m') 70 | print(f'\033[34m filename: {filename} \033[0m') 71 | print(f'\033[34m filename_prefix: {filename_prefix} \033[0m') 72 | print(f'\033[34m skip_existing: {skip_existing} \033[0m') 73 | print(f'\033[34m timeout: {timeout} \033[0m') 74 | print(f'\033[34m max_retries: {max_retries} \033[0m') 75 | 76 | youtubeObject.download( 77 | output_path=output_path, 78 | filename=filename, 79 | filename_prefix=filename_prefix, 80 | skip_existing=skip_existing, 81 | timeout=timeout, 82 | max_retries=max_retries) 83 | print('\033[1;91m Downloading completed. \033[1;m') 84 | return DownloaderStatus.SUCCESS.value 85 | except Exception as ex: 86 | print(f'\033[1;91m Exception has occured: {str(ex)} \033[1;m') 87 | return DownloaderStatus.FAILED.value 88 | 89 | 90 | def main(): 91 | arg_parse = argparse.ArgumentParser() 92 | arg_parse.add_argument('url', type=str, help='URL to the video.') 93 | arg_parse.add_argument('-o', '--output-path', metavar='output_path', 94 | default=None, type=str, 95 | help='Output path for writing media file.\ 96 | If one is not specified, defaults\ 97 | to the current working directory.' 98 | ) 99 | arg_parse.add_argument('-f', '--filename', metavar='filename', 100 | default=None, type=str, 101 | help='Output filename (stem only) for writing\ 102 | media file. If one is not specified, the\ 103 | default filename is used.') 104 | arg_parse.add_argument('-p', '--filename-prefix', type=str, 105 | metavar='filename_prefix', default=None, 106 | help='A string that will be prepended to the\ 107 | filename. For example a number in a\ 108 | playlist or the name of a series.\ 109 | If one is not specified, nothing\ 110 | will be prepended. This is separate\ 111 | from filename so you can use the default\ 112 | filename but still add a prefix.') 113 | arg_parse.add_argument('-s', '--skip-existing', 114 | action='store_false', default=True, 115 | help='Skip existing files, defaults to True.') 116 | arg_parse.add_argument('-t', '--timeout', metavar='timeout', 117 | type=int, default=None, 118 | help='Request timeout length in seconds.\ 119 | Uses system default.') 120 | arg_parse.add_argument('-m', '--max-retries', metavar='max_retries', 121 | type=int, default=0, 122 | help='Number of retries to attempt after\ 123 | socket timeout. Defaults to 0.') 124 | args = arg_parse.parse_args() 125 | 126 | return YoutubeDownloader.download( 127 | url=args.url, 128 | output_path=args.output_path, 129 | filename=args.filename, 130 | filename_prefix=args.filename_prefix, 131 | skip_existing=args.skip_existing, 132 | timeout=args.timeout, 133 | max_retries=args.max_retries 134 | ) 135 | 136 | 137 | if __name__ == '__main__': 138 | sys.exit(main()) 139 | --------------------------------------------------------------------------------