def _ffmpeg_downloader(audio_stream: Stream, video_stream: Stream, target: str) -> None: """ Given a YouTube Stream object, finds the correct audio stream, downloads them both giving them a unique name, them uses ffmpeg to create a new file with the audio and video from the previously downloaded files. Then deletes the original adaptive streams, leaving the combination. :param Stream audio_stream: A valid Stream object representing the audio to download :param Stream video_stream: A valid Stream object representing the video to download :param Path target: A valid Path object """ video_unique_name = _unique_name( safe_filename(video_stream.title), video_stream.subtype, "video", target=target ) audio_unique_name = _unique_name( safe_filename(video_stream.title), audio_stream.subtype, "audio", target=target ) _download(stream=video_stream, target=target, filename=video_unique_name) print("Loading audio...") _download(stream=audio_stream, target=target, filename=audio_unique_name) video_path = os.path.join(target, f"{video_unique_name}.{video_stream.subtype}") audio_path = os.path.join(target, f"{audio_unique_name}.{audio_stream.subtype}") final_path = os.path.join( target, f"{safe_filename(video_stream.title)}.{video_stream.subtype}" ) subprocess.run( # nosec ["ffmpeg", "-i", video_path, "-i", audio_path, "-codec", "copy", final_path,] ) os.unlink(video_path) os.unlink(audio_path)
def download(self, output_path=None, filename=None, filename_prefix=None, only_url=None): """Write the media stream to disk. :param output_path: (optional) Output path for writing media file. If one is not specified, defaults to the current working directory. :type output_path: str or None :param filename: (optional) Output filename (stem only) for writing media file. If one is not specified, the default filename is used. :type filename: str or None :param filename_prefix: (optional) 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 seperate from filename so you can use the default filename but still add a prefix. :type filename_prefix: str or None :rtype: str """ if only_url: return self.url output_path = output_path or os.getcwd() if filename: safe = safe_filename(filename) filename = '{filename}.{s.subtype}'.format(filename=safe, s=self) filename = filename or self.default_filename if filename_prefix: filename = '{prefix}{filename}'\ .format( prefix=safe_filename(filename_prefix), filename=filename, ) # file path fp = os.path.join(output_path, filename) bytes_remaining = self.filesize PLog( 'streams: downloading (%s total bytes) file to %s', self.filesize, fp, ) with open(fp, 'wb') as fh: for chunk in request.get(self.url, streaming=True): # reduce the (bytes) remainder by the length of the chunk. bytes_remaining -= len(chunk) # send to the on_progress callback. self.on_progress(chunk, fh, bytes_remaining) self.on_complete(fh) return fp
def Downloaderffmpeg( self, audio_stream: Stream, video_stream: Stream, target: str )-> str: video_unique_name = fileUnique( helpers.safe_filename(video_stream.title), video_stream.subtype, "video", target=target, ) audio_unique_name = fileUnique( helpers.safe_filename(video_stream.title), audio_stream.subtype, "audio", target=target, ) clidownload(stream=video_stream, target=target, filename=video_unique_name) clidownload(stream=audio_stream, target=target, filename=audio_unique_name) video_path = os.path.join( target, f"{video_unique_name}.{video_stream.subtype}" ) audio_path = os.path.join( target, f"{audio_unique_name}.{audio_stream.subtype}" ) final_path = os.path.join( # target, f"{helpers.safe_filename(video_stream.title)}.{video_stream.subtype}" target, f"{helpers.safe_filename(video_stream.title)}.mp4" ) try: subprocess.run( [ "ffmpeg", "-i", video_path, "-i", audio_path, "-codec", "copy", final_path, "-loglevel", "quiet" ] ) except: print("Ada kesalahan saat menggabungkan video") sleep(0.5) print("Tapi tenang, file video dan audio masih ada") finally: print(s("%s" % prGreen("Success"))) print(s("File disimpan di %s" % prGreen(final_path))) os.unlink(video_path) os.unlink(audio_path) return final_path return None
def write_audio(audio, video, position, channel_name="", channel_id="", group=None, include_title=False, include_channel=False): """Write audio Stream object to a file. If an output folder is not specified, audio will be placed in a folder corresponding to the name of the video's author (i.e. channel). :param audio: The audio Stream to download :param position: Channel-wise position of the video in the url list :param channel_name: The name of the channel as given on its main page (default "") :param channel_id: The name of the channel as it appears in the channel's URL (default "") :param group: The folder to output the audio stream to (default None) :param include_title: Include video title in audio filename (default True) :param include_channel: Save file to channel subfolder (default False) """ safe_title = helpers.safe_filename(video.title) safe_author = helpers.safe_filename(video.author) out_path = path.join("corpus", "raw_audio") if group is not None: out_path = path.join(out_path, group) punc_and_whitespace = "[\s\_\-\.\?\!,;:'\"\\\/]+" if (include_channel): if channel_name and channel_id: safe_channel_name = sub(punc_and_whitespace, "", channel_name) out_path = path.join( out_path, "{0}_{1}".format(safe_channel_name, channel_id)) else: safe_author = sub(punc_and_whitespace, "", video.author) out_path = path.join(out_path, safe_author) if not path.exists(out_path): makedirs(out_path) try: if include_title: audio.download(filename=safe_title, output_path=out_path, filename_prefix="{0}_{1}_".format( safe_author, position), skip_existing=True) else: audio.download(filename=str(position), output_path=out_path, filename_prefix="{0}_".format(safe_author), skip_existing=True) except: logging.critical( "Video {0}: Could not save audio stream for video {0} from channel {1} ({2})" .format(position, video.author, video.title)) # Be polite sleep(1)
def download( self, title: str, srt=True, output_path=None, filename_prefix=None, ) -> str: """Write the media stream to disk. :param title: Output filename (stem only) for writing media file. If one is not specified, the default filename is used. :type title: str :param srt: Set to True to download srt, false to download xml. Defaults to True. :type srt bool :param output_path: (optional) Output path for writing media file. If one is not specified, defaults to the current working directory. :type output_path: str or None :param filename_prefix: (optional) 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 filename so you can use the default filename but still add a prefix. :type filename_prefix: str or None :rtype: str """ if title.endswith(".srt") or title.endswith(".xml"): filename = ".".join(title.split(".")[:-1]) else: filename = title if filename_prefix: filename = "{}{}".format(safe_filename(filename_prefix), filename) filename = safe_filename(filename) filename += " ({})".format(self.code) if srt: filename += ".srt" else: filename += ".xml" file_path = os.path.join(target_directory(output_path), filename) with open(file_path, "w", encoding="utf-8") as file_handle: if srt: file_handle.write(self.generate_srt_captions()) else: file_handle.write(self.xml_captions) return file_path
def get_file_path( self, filename: Optional[str], output_path: Optional[str], filename_prefix=None, ) -> str: if filename: filename = "{}.{}".format(safe_filename(filename), self.subtype) else: filename = self.default_filename if filename_prefix: filename = "{}{}".format(safe_filename(filename_prefix), filename) return os.path.join(target_directory(output_path), filename)
def on_DL_pressed(self): if self.can_dl: if self.HighestQual.isChecked() and not self.AudioOnly.isChecked(): self.vid_stream = self.vid.streams.filter(custom_filter_functions=[lambda s: s.includes_video_track]).order_by('resolution').desc().first() if self.vid_stream.is_progressive: fullpath, _ = QFileDialog.getSaveFileName(self, "Save file", helpers.safe_filename(self.vid.title), "{} (*.{});;All files (*.*)".format(self.suffix_description(self.vid_stream.subtype), self.vid_stream.subtype)) if not fullpath: return self.fullpath = pathlib.Path(fullpath) worker = Worker_1() self.threadpool.start(worker) else: self.adapt = True self.aud_stream = self.vid.streams.filter(only_audio=True, subtype=self.vid_stream.subtype).first() fullpath, _ = QFileDialog.getSaveFileName(self, "Save file", helpers.safe_filename(self.vid.title), "{} (*.{});;All files (*.*)".format(self.suffix_description(self.aud_stream.subtype), self.aud_stream.subtype)) if not fullpath: return self.fullpath = pathlib.Path(fullpath) worker = Worker_2() self.threadpool.start(worker) elif self.HighestQual.isChecked() and self.AudioOnly.isChecked(): self.aud_stream = self.vid.streams.filter(only_audio=True).order_by('abr').desc().first() fullpath, _ = QFileDialog.getSaveFileName(self, "Save file", helpers.safe_filename(self.vid.title), "{} (*.{});;All files (*.*)".format(self.suffix_description(self.aud_stream.subtype), self.aud_stream.subtype)) if not fullpath: return self.fullpath = pathlib.Path(fullpath) worker = Worker_3() self.threadpool.start(worker) else: print(self.SelectStream.currentIndex) self.vid_stream = self.vid.streams.all()[self.SelectStream.currentIndex()] fullpath, _ = QFileDialog.getSaveFileName(self, "Save file", helpers.safe_filename(self.vid.title), "{} (*.{});;All files (*.*)".format(self.suffix_description(self.vid_stream.subtype), self.vid_stream.subtype)) if not fullpath: return self.fullpath = pathlib.Path(fullpath) worker = Worker_1() self.threadpool.start(worker)
def parallel_streaming(self, output_path=None, filename=None): """Write the media stream to buffer :rtype: io.BytesIO buffer """ buffer = io.BytesIO() logger.debug( 'downloading (%s total bytes) file to BytesIO buffer', self.filesize, ) chunks = request.get_parallel(self.url, self.on_progress_parallel) output_path = output_path or os.getcwd() if filename: safe = safe_filename(filename) filename = '{filename}.{s.subtype}'.format(filename=safe, s=self) filename = filename or self.default_filename # file path fp = os.path.join(output_path, filename) with open(fp, 'wb') as fh: for k in sorted(chunks.keys()): fh.write(chunks[k]) self.on_complete(fh) return fp
def update_one_json(prep_json, video_dir, audio_dir): prep_json["title_clean"] = safe_filename(prep_json["title"]) prep_json["description_clean"] = clean_description( prep_json["description"]) video_path = osp.join(video_dir, f"{safe_filename(prep_json['title'])}.mp4") audio_path = osp.join(audio_dir, f"{safe_filename(prep_json['title'])}.mp3") try: prep_json["theme"] = CHANNEL_TITLE_2_THEME[prep_json["channelTitle"]] except KeyError: try: prep_json["theme"] = QUERY_2_THEME[prep_json["query"]] except KeyError: print( f"Theme cannot be extracted from channel title {prep_json['channelTitle']} " f"or query {prep_json.get('query')}") for fpath, fpath_name in zip([video_path, audio_path], ["video_path", "audio_path"]): prep_json[fpath_name] = "/".join(list(fpath.split("/"))[-3:]) prep_json[f"{fpath_name}_exists"] = osp.exists(fpath) if not osp.exists(fpath): print(f"Path {fpath} doesn't exist") return prep_json
def download_youtube_by_itag(yt, itag, target): target = target or os.getcwd() filepath = None try: url = yt.watch_url stream = yt.streams.get_by_itag(itag) title = stream.title resolution = stream.resolution video_codec = stream.video_codec abr = stream.abr audio_codec = stream.audio_codec fps = stream.fps bitrate = stream.bitrate filesize = stream.filesize filename = '{title}_{video}_{video_codec}_{audio}_{audio_codec}_{fps}_{bitrate}_{filesize}'.format( title=title, video=resolution, video_codec=video_codec, audio=abr, audio_codec=audio_codec, fps=fps, bitrate=bitrate, filesize=filesize) filename = to_unicode(safe_filename(filename)) logger.debug("Filename = {filename}".format(filename=filename)) yt.register_on_progress_callback(on_progress) filepath = yt.streams.get_by_itag(itag).download(output_path=target, filename=filename) except: logger.error( "Unable to download YT, url = [{url}], itag = [{itag}]".format( url=url, itag=itag)) return filepath
def get_folder_file_from(url): """ create folder for a single video to download Returns: tuple : folder_name, file_valid_name """ with YoutubeDL({ # Download single video instead of a playlist if in doubt. "noplaylist": True, # to make fetching info faster because we are not interested in DASH info "youtube_include_dash_manifest": False, # don't print log messages in stdout "quiet": True }) as ydl: info_dict = ydl.extract_info(url, download=False) title = info_dict.get('title', UNKNOWN_NAME) # name on youtube without file extension title = safe_filename(title) # valid name to pass to IDM valid_name = check_and_file_ext(title) # name on youtube with file extension original_name = title + valid_name[-4:] + "\n" mkdir_and_chdir(title) with open(NANES_FILE, 'w') as file: file.write(original_name) return title, "00-" + valid_name
def download_song(yt, name): global current_download stream = yt.streams.filter( only_audio=True, file_extension='mp4').order_by('resolution').desc().first() if not stream is None: print("Downloading " + yt.watch_url) filename = helpers.safe_filename(name) stream.subtype = "m4a" file_target = '{filename}.{file_type}'.format(filename=filename, file_type=stream.subtype) if os.path.isfile(file_target): print("File already detected") return current_download = file_target stream.download(filename=filename) print("Download completed") return True else: print("Unable to download " + yt.watch_url) return False
def playVideo(title, fpath): print("Playing", safe_filename(title)) player = vlc.MediaPlayer(fpath) player.play() print("Playing") while True: pass
def download(self, output_path=None, filename=None): """Write the media stream to disk. :param output_path: (optional) Output path for writing media file. If one is not specified, defaults to the current working directory. :type output_path: str or None :param filename: (optional) Output filename (stem only) for writing media file. If one is not specified, the default filename is used. :type filename: str or None :rtype: None """ output_path = output_path or os.getcwd() if filename: safe = safe_filename(filename) filename = '{filename}.{s.subtype}'.format(filename=safe, s=self) filename = filename or self.default_filename # file path fp = os.path.join(output_path, filename) bytes_remaining = self.filesize logger.debug( 'downloading (%s total bytes) file to %s', self.filesize, fp, ) tmpRangefp = None isTimeOut = False with open(fp, 'wb') as fh: while True: for chunk in request.get(self.url, streaming=True, conRangefp=tmpRangefp): if not chunk: print 'streams time out sleep 10s' fh.flush() time.sleep(10) nfize = os.path.getsize(fp) print fp, nfize if nfize < self.filesize: tmpRangefp = fp isTimeOut = True break else: tmpRangefp = None # reduce the (bytes) remainder by the length of the chunk. bytes_remaining -= len(chunk) # send to the on_progress callback. self.on_progress(chunk, fh, bytes_remaining) if isTimeOut: isTimeOut = False print tmpRangefp else: self.on_complete(fh) break
def main(): """Command line application to download youtube videos.""" # noinspection PyTypeChecker parser = argparse.ArgumentParser(description=main.__doc__) args = _parse_args(parser) if args.verbose: log_filename = None if args.logfile: log_filename = args.logfile setup_logger(logging.DEBUG, log_filename=log_filename) logger.debug(f'Pytube version: {__version__}') if not args.url or "youtu" not in args.url: parser.print_help() sys.exit(1) if "/playlist" in args.url: print("Loading playlist...") playlist = Playlist(args.url) if not args.target: args.target = safe_filename(playlist.title) for youtube_video in playlist.videos: try: _perform_args_on_youtube(youtube_video, args) except exceptions.PytubeError as e: print(f"There was an error with video: {youtube_video}") print(e) else: print("Loading video...") youtube = YouTube(args.url) _perform_args_on_youtube(youtube, args)
def download_playlist(url): """ download the whole playlist of the given url NOTE: youtubedl was used instead of pytube because it gives stable result and that's because pytube sometimes didn't return the correct name of the given link """ ydl = YoutubeDL({ # this line is VERY IMPORTANT because if there's private viedeos # or anything unavilable to download youtube-dl won't crash # and will continue to the next videos in the playlist "ignoreerrors": True, # to make fetching info faster because we are not interested in DASH info "youtube_include_dash_manifest": False, # don't print log messages in stdout "quiet": True }) with ydl: playlist = ydl.extract_info( url, download=False # We just want to extract the info ) name_playlist = safe_filename(playlist.get('title')) mkdir_and_chdir(name_playlist) download_multiple_videos(playlist, name_playlist)
def main(): """Command line application to download youtube videos.""" # noinspection PyTypeChecker parser = argparse.ArgumentParser(description=main.__doc__) args = _parse_args(parser) if args.verbosity: log_level = min(args.verbosity, 4) * 10 setup_logger(logging.FATAL - log_level) if not args.url or "youtu" not in args.url: parser.print_help() sys.exit(1) if "/playlist" in args.url: print("Loading playlist...") playlist = Playlist(args.url) if not args.target: args.target = safe_filename(playlist.title()) for youtube_video in playlist.videos: try: _perform_args_on_youtube(youtube_video, args) except PytubeError as e: print(f"There was an error with video: {youtube_video}") print(e) else: print("Loading video...") youtube = YouTube(args.url) _perform_args_on_youtube(youtube, args)
def download_channel(url): """ download the whole channel of the given url (the url must ends with "/videos" so you need to select the videos tab in the channel page and then copy the url NOTE: youtubedl was used instead of pytube because it gives stable result and that's because pytube sometimes didn't return the correct name of the given link """ ydl = YoutubeDL({ # this line is VERY IMPORTANT because if there's private viedeos # or anything unavilable to download youtube-dl won't crash # and will continue to the next videos in the playlist "ignoreerrors": True, # Download single video instead of a playlist if in doubt. "noplaylist": True, # to make fetching info faster because we are not interested in DASH info "youtube_include_dash_manifest": False, # don't print log messages in stdout "quiet": True }) with ydl: channel = ydl.extract_info( url, download=False # We just want to extract the info ) # original output without split is "Uploads from CHANNEL_NAME" (quotes not included) # so we need to split and pick the last item name_channel = safe_filename(channel.get("title").split(' from ')[-1]) # print(name_channel) # print("="*50) mkdir_and_chdir(name_channel) download_multiple_videos(channel, name_channel)
def merge_and_output(self): print( "\n[+++++] Download completed, now it's time to merge the video and audio!\n" ) aud_path = ('"' + str(self.fetched_path) + '\\' + helpers.safe_filename(self.title) + ' - aud' + '.mp4"') vid_path = ('"' + str(self.fetched_path) + '\\' + helpers.safe_filename(self.title) + '.mp4"') mrg_path = ('"' + str(self.fetched_path) + '\\' + helpers.safe_filename(self.title) + ' -- output.mp4"') subprocess.run('ffmpeg -i {} -i {} -c:v copy -c:a aac {} -y'.format( vid_path, aud_path, mrg_path)) # -y: overwrite without asking if self.fetched_option == 'C': os.system("DEL {} {}".format(aud_path, vid_path)) elif self.fetched_option == "AC": os.system("DEL {}".format(vid_path)) print("\n***** Sorry for this mess, we almost finished ^_^ *****\n")
def is_downloaded(fname): fname= safe_filename(fname) for file in glob.glob('Files/Musics/*.*'): li= file.rindex('.') name= file[13:li] if(name==fname): return file return "NO"
def destination_filepath(self, output_path=None, filename=None, filename_prefix=None): output_path = output_path or os.getcwd() if filename: safe = safe_filename(filename) filename = '{filename}.{s.subtype}'.format(filename=safe, s=self) filename = filename or self.default_filename if filename_prefix: filename = '{prefix}{filename}'\ .format( prefix=safe_filename(filename_prefix), filename=filename, ) # file path fp = os.path.join(output_path, filename) return fp
def default_filename(self) -> str: """Generate filename based on the video title. :rtype: str :returns: An os file system compatible filename. """ filename = safe_filename(self.title) return f"{filename}.{self.subtype}"
def default_filename(self): """Generate filename based on the video title. :rtype: str :returns: An os file system compatible filename. """ title = self.player_config_args['player_response']['videoDetails']['title'] filename = safe_filename(title) return '{filename}.{s.subtype}'.format(filename=filename, s=self)
def default_filename(self): """Generate filename based on the video title. :rtype: str :returns: An os file system compatible filename. """ title = self.player_config_args['title'] filename = safe_filename(title) return '{filename}.{s.subtype}'.format(filename=filename, s=self)
def pl_merge(self): # merge vid, aud and get the output print( "\n[+++++] Download completed, now it's time to merge the video and audio!\n" ) aud_path = ('"' + str(self.pl_path) + '\\' + helpers.safe_filename(self.single_yt_title) + ' - aud' + '.mp4"') vid_path = ('"' + str(self.pl_path) + '\\' + helpers.safe_filename(self.single_yt_title) + '.mp4"') mrg_path = ('"' + str(self.pl_path) + '\\' + helpers.safe_filename(self.single_yt_title) + ' -- output.mp4"') subprocess.run('ffmpeg -i {} -i {} -c:v copy -c:a aac {} -y'.format( vid_path, aud_path, mrg_path)) # -y: overwrite without asking os.system("DEL {} {}".format(aud_path, vid_path)) #os.system("DEL {}".format(vid_path)) print("\n***** Sorry for this mess, we almost finished ^_^ *****\n")
def sanitize_filename(s: str) -> str: """ Processes given string, making it safe to be a correct filename. :param s: input string :return: string safe for filesystem """ s = safe_filename(s) s = s.replace(' ', '_') return s.encode('ascii', 'ignore').decode('ascii')
def default_filename(self): """Generate filename based on the video title. :rtype: str :returns: An os file system compatible filename. """ filename = safe_filename(self.title) return '{filename}.{s.subtype}'.format(filename=filename, s=self)
def go_pytube(): try: go_yt = yt(yt_url) name = safe_filename(go_yt.title) print(u'名稱:', name) return go_yt, name except reME: print(u'不正常的網址...') e = input(u'按下 Enter按鍵 離開') exit()
def get_video(self, resolution, myfolder=None, fps=None): """ download video in mp4 format :param myfolder: path to folder for downloaded video and audio :type myfolder: path-like or str or None :param resolution: resolution ends with 'p' :type resolution: str :param fps: frame per second :type fps: int :return: None """ self.index += 1 title = self.yt.title idx = self.index valid_filename = safe_filename(title) + '.mp4' file_path = os.path.join( myfolder, valid_filename) if myfolder else valid_filename progressive_video = self.yt.streams.filter(progressive=True, resolution=resolution, fps=fps) # download progressive video if possible if progressive_video: print("[Downloading progressive video...]") progressive_video.last().download(output_path=myfolder, filename=valid_filename) return if not FFMPEG_AVAILABLE: raise FfmpegNotAvailableError( 'ffmpeg not found. Cannot perform downloading.' 'Make sure ffmpeg is installed and added in PATH') # search for video with specific resolution and fps video_search_result = self.yt.streams.filter(only_video=True, resolution=resolution, fps=fps) if video_search_result: print("[Downloading...]") video_path = video_search_result.last().download( output_path=myfolder, filename=f'video{idx}') audio_path = self.yt.streams.filter( only_audio=True).order_by('abr').last().download( output_path=myfolder, filename=f'audio{idx}') subprocess.run([ 'ffmpeg', '-i', video_path, '-i', audio_path, '-acodec', 'aac', '-vsync', 'vfr', '-preset', 'veryfast', file_path ]) os.remove(video_path) os.remove(audio_path) else: quality = resolution + str(fps) if fps else resolution raise ValueError( "there is no video with quality {}".format(quality))
def vid_to_aud(var): startdl(var) var.streams.filter( only_audio=True, subtype='webm').order_by('bitrate').last().download(dlpath) filepath = os.path.join(dlpath, safe_filename(var.title)) webmaudio = ffmpeg.input(filepath + '.webm') mp3audio = filepath + '.mp3' ffmpeg.output(webmaudio, mp3audio).run() os.remove(filepath + '.webm') finishdl(var)
def download_youtube_video(url=None, itag=None, audio_only=False, output_path=None, filename=None, filename_prefix=None, proxies=None, progress_callback=None, video_and_stream=None): """ Download a YouTube Video. :param url: Full URL to YouTube Video or YouTube Video ID :type url: str :param itag: YouTube Stream ITAG to Download :type itag: int :param audio_only: Download only the audio for the video. Takes longer than video. :type audio_only: bool :param output_path: Path to folder to output file. :type output_path: str :param filename: Filename override. Does not override extension. :type filename: str :param filename_prefix: Currently Does Not Work on pytube :type filename_prefix: str :param proxies: Dictionary containing protocol (key) and address (value) for the proxies :type proxies: dict :return: Filename of downloaded video/audio :rtype: str """ if url is None and video_and_stream is None: raise ValueError( 'You must provide either a url or video/stream object tuple') if output_path: makedirs(output_path, exist_ok=True) if video_and_stream is None: if 'http' not in url: url = 'https://www.youtube.com/watch?v=%s' % url video = YouTube(url, proxies=proxies) if itag: itag = int(itag) stream = video.streams.get_by_itag(itag) else: stream = video.streams.filter(only_audio=audio_only).first() else: video, stream = video_and_stream if progress_callback: video.register_on_progress_callback(progress_callback) print('Download Started: %s' % video.title) if filename: filename = safe_filename(filename) stream.download(output_path=output_path, filename=filename) file_type = '.' + stream.mime_type.split('/')[1] filename = stream.default_filename if filename is None else filename + file_type print('Download Complete! Saved to file: %s' % filename) return filename
def download(self, output_path=None, filename=None): """Write the media stream to disk. :param output_path: (optional) Output path for writing media file. If one is not specified, defaults to the current working directory. :type output_path: str or None :param filename: (optional) Output filename (stem only) for writing media file. If one is not specified, the default filename is used. :type filename: str or None :rtype: None """ output_path = output_path or os.getcwd() if filename: safe = safe_filename(filename) filename = '{filename}.{s.subtype}'.format(filename=safe, s=self) filename = filename or self.default_filename # file path fp = os.path.join(output_path, filename) bytes_remaining = self.filesize logger.debug( 'downloading (%s total bytes) file to %s', self.filesize, fp, ) with open(fp, 'wb') as fh: for chunk in request.get(self.url, streaming=True): # reduce the (bytes) remainder by the length of the chunk. bytes_remaining -= len(chunk) # send to the on_progress callback. self.on_progress(chunk, fh, bytes_remaining) self.on_complete(fh)
def test_safe_filename(): """Unsafe characters get stripped from generated filename""" assert helpers.safe_filename('abc1245$$') == 'abc1245' assert helpers.safe_filename('abc##') == 'abc'