yt-dlp | A youtube-dl fork with additional features and fixes | Video Utils library
kandi X-RAY | yt-dlp Summary
Support
Quality
Security
License
Reuse
- Create an option parser .
- Extract mvpd auth data .
- Processes info .
- Validate options .
- Parse MPD formats .
- Extract metadata from a video .
- Interpret an expression .
- Extract an anime entry .
- Parse options .
- Get information about a video .
yt-dlp Key Features
yt-dlp Examples and Code Snippets
-x, --extract-audio Convert video files to audio-only files (requires ffmpeg and ffprobe) --audio-format FORMAT Format to convert the audio to when -x is used. (currently supported: best (default), aac, alac, flac, m4a, mp3, opus, vorbis, wav). You can specify multiple rules using similar syntax as --remux-video --audio-quality QUALITY Specify ffmpeg audio quality to use when converting the audio with -x. Insert a value between 0 (best) and 10 (worst) for VBR or a specific bitrate like 128K (default 5) --remux-video FORMAT Remux the video into another container if necessary (currently supported: avi, flv, gif, mkv, mov, mp4, webm, aac, aiff, alac, flac, m4a, mka, mp3, ogg, opus, vorbis, wav). If target container does not support the video/audio codec, remuxing will fail. You can specify multiple rules; e.g. "aac>m4a/mov>mp4/mkv" will remux aac to m4a, mov to mp4 and anything else to mkv --recode-video FORMAT Re-encode the video into another format if necessary. The syntax and supported formats are the same as --remux-video --postprocessor-args NAME:ARGS Give these arguments to the postprocessors. Specify the postprocessor/executable name and the arguments separated by a colon ":" to give the argument to the specified postprocessor/executable. Supported PP are: Merger, ModifyChapters, SplitChapters, ExtractAudio, VideoRemuxer, VideoConvertor, Metadata, EmbedSubtitle, EmbedThumbnail, SubtitlesConvertor, ThumbnailsConvertor, FixupStretched, FixupM4a, FixupM3u8, FixupTimestamp and FixupDuration. The supported executables are: AtomicParsley, FFmpeg and FFprobe. You can also specify "PP+EXE:ARGS" to give the arguments to the specified executable only when being used by the specified postprocessor. Additionally, for ffmpeg/ffprobe, "_i"/"_o" can be appended to the prefix optionally followed by a number to pass the argument before the specified input/output file, e.g. --ppa "Merger+ffmpeg_i1:-v quiet". You can use this option multiple times to give different arguments to different postprocessors. (Alias: --ppa) -k, --keep-video Keep the intermediate video file on disk after post-processing --no-keep-video Delete the intermediate video file after post-processing (default) --post-overwrites Overwrite post-processed files (default) --no-post-overwrites Do not overwrite post-processed files --embed-subs Embed subtitles in the video (only for mp4, webm and mkv videos) --no-embed-subs Do not embed subtitles (default) --embed-thumbnail Embed thumbnail in the video as cover art --no-embed-thumbnail Do not embed thumbnail (default) --embed-metadata Embed metadata to the video file. Also embeds chapters/infojson if present unless --no-embed-chapters/--no-embed-info-json are used (Alias: --add-metadata) --no-embed-metadata Do not add metadata to file (default) (Alias: --no-add-metadata) --embed-chapters Add chapter markers to the video file (Alias: --add-chapters) --no-embed-chapters Do not add chapter markers (default) (Alias: --no-add-chapters) --embed-info-json Embed the infojson as an attachment to mkv/mka video files --no-embed-info-json Do not embed the infojson as an attachment to the video file --parse-metadata FROM:TO Parse additional metadata like title/artist from other fields; see "MODIFYING METADATA" for details --replace-in-metadata FIELDS REGEX REPLACE Replace text in a metadata field using the given regex. This option can be used multiple times --xattrs Write metadata to the video file's xattrs (using dublin core and xdg standards) --concat-playlist POLICY Concatenate videos in a playlist. One of "never", "always", or "multi_video" (default; only when the videos form a single show). All the video files must have same codecs and number of streams to be concatable. The "pl_video:" prefix can be used with "--paths" and "--output" to set the output filename for the concatenated files. See "OUTPUT TEMPLATE" for details --fixup POLICY Automatically correct known faults of the file. One of never (do nothing), warn (only emit a warning), detect_or_warn (the default; fix file if we can, warn otherwise), force (try fixing even if file already exists) --ffmpeg-location PATH Location of the ffmpeg binary; either the path to the binary or its containing directory --exec [WHEN:]CMD Execute a command, optionally prefixed with when to execute it (after_move if unspecified), separated by a ":". Supported values of "WHEN" are the same as that of --use-postprocessor. Same syntax as the output template can be used to pass any field as arguments to the command. After download, an additional field "filepath" that contains the final path of the downloaded file is also available, and if no fields are passed, %(filepath)q is appended to the end of the command. This option can be used multiple times --no-exec Remove any previously defined --exec --convert-subs FORMAT Convert the subtitles to another format (currently supported: ass, lrc, srt, vtt) (Alias: --convert-subtitles) --convert-thumbnails FORMAT Convert the thumbnails to another format (currently supported: jpg, png, webp). You can specify multiple rules using similar syntax as --remux-video --split-chapters Split video into multiple files based on internal chapters. The "chapter:" prefix can be used with "--paths" and "--output" to set the output filename for the split files. See "OUTPUT TEMPLATE" for details --no-split-chapters Do not split video based on chapters (default) --remove-chapters REGEX Remove chapters whose title matches the given regular expression. The syntax is the same as --download-sections. This option can be used multiple times --no-remove-chapters Do not remove any chapters from the file (default) --force-keyframes-at-cuts Force keyframes at cuts when downloading/splitting/removing sections. This is slow due to needing a re-encode, but the resulting video may have fewer artifacts around the cuts --no-force-keyframes-at-cuts Do not force keyframes around the chapters when cutting/splitting (default) --use-postprocessor NAME[:ARGS] The (case sensitive) name of plugin postprocessors to be enabled, and (optionally) arguments to be passed to it, separated by a colon ":". ARGS are a semicolon ";" delimited list of NAME=VALUE. The "when" argument determines when the postprocessor is invoked. It can be one of "pre_process" (after video extraction), "after_filter" (after video passes filter), "before_dl" (before each video download), "post_process" (after each video download; default), "after_move" (after moving video file to it's final locations), "after_video" (after downloading and processing all formats of a video), or "playlist" (at end of playlist). This option can be used multiple times to add different postprocessors
import json import yt_dlp URL = 'https://www.youtube.com/watch?v=BaW_jenozKc' # ℹ️ See help(yt_dlp.YoutubeDL) for a list of available options and public functions ydl_opts = {} with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(URL, download=False) # ℹ️ ydl.sanitize_info makes the info json-serializable print(json.dumps(ydl.sanitize_info(info)))
import yt_dlp INFO_FILE = 'path/to/video.info.json' with yt_dlp.YoutubeDL() as ydl: error_code = ydl.download_with_info_file(INFO_FILE) print('Some videos failed to download' if error_code else 'All videos successfully downloaded')
import yt_dlp URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc'] ydl_opts = { 'format': 'm4a/bestaudio/best', # ℹ️ See help(yt_dlp.postprocessor) for a list of available Postprocessors and their arguments 'postprocessors': [{ # Extract audio using ffmpeg 'key': 'FFmpegExtractAudio', 'preferredcodec': 'm4a', }] } with yt_dlp.YoutubeDL(ydl_opts) as ydl: error_code = ydl.download(URLS)
import yt_dlp URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc'] def longer_than_a_minute(info, *, incomplete): """Download only videos longer than a minute (or with unknown duration)""" duration = info.get('duration') if duration and duration < 60: return 'The video is too short' ydl_opts = { 'match_filter': longer_than_a_minute, } with yt_dlp.YoutubeDL(ydl_opts) as ydl: error_code = ydl.download(URLS)
import yt_dlp URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc'] class MyLogger: def debug(self, msg): # For compatibility with youtube-dl, both debug and info are passed into debug # You can distinguish them by the prefix '[debug] ' if msg.startswith('[debug] '): pass else: self.info(msg) def info(self, msg): pass def warning(self, msg): pass def error(self, msg): print(msg) # ℹ️ See "progress_hooks" in help(yt_dlp.YoutubeDL) def my_hook(d): if d['status'] == 'finished': print('Done downloading, now post-processing ...') ydl_opts = { 'logger': MyLogger(), 'progress_hooks': [my_hook], } with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download(URLS)
import yt_dlp URLS = ['https://www.youtube.com/watch?v=BaW_jenozKc'] # ℹ️ See help(yt_dlp.postprocessor.PostProcessor) class MyCustomPP(yt_dlp.postprocessor.PostProcessor): def run(self, info): self.to_screen('Doing stuff') return [], info with yt_dlp.YoutubeDL() as ydl: # ℹ️ "when" can take any value in yt_dlp.utils.POSTPROCESS_WHEN ydl.add_post_processor(MyCustomPP(), when='pre_process') ydl.download(URLS)
import yt_dlp URL = ['https://www.youtube.com/watch?v=BaW_jenozKc'] def format_selector(ctx): """ Select the best video and the best audio that won't result in an mkv. NOTE: This is just an example and does not handle all cases """ # formats are already sorted worst to best formats = ctx.get('formats')[::-1] # acodec='none' means there is no audio best_video = next(f for f in formats if f['vcodec'] != 'none' and f['acodec'] == 'none') # find compatible audio extension audio_ext = {'mp4': 'm4a', 'webm': 'webm'}[best_video['ext']] # vcodec='none' means there is no video best_audio = next(f for f in formats if ( f['acodec'] != 'none' and f['vcodec'] == 'none' and f['ext'] == audio_ext)) # These are the minimum required fields for a merged format yield { 'format_id': f'{best_video["format_id"]}+{best_audio["format_id"]}', 'ext': best_video['ext'], 'requested_formats': [best_video, best_audio], # Must be + separated list of protocols 'protocol': f'{best_video["protocol"]}+{best_audio["protocol"]}' } ydl_opts = { 'format': format_selector, } with yt_dlp.YoutubeDL(ydl_opts) as ydl: ydl.download(URLS)
# Download and merge the best video-only format and the best audio-only format, # or download the best combined format if video-only format is not available $ yt-dlp -f "bv+ba/b" # Download best format that contains video, # and if it doesn't already have an audio stream, merge it with best audio-only format $ yt-dlp -f "bv*+ba/b" # Same as above $ yt-dlp # Download the best video-only format and the best audio-only format without merging them # For this case, an output template should be used since # by default, bestvideo and bestaudio will have the same file name. $ yt-dlp -f "bv,ba" -o "%(title)s.f%(format_id)s.%(ext)s" # Download and merge the best format that has a video stream, # and all audio-only formats into one file $ yt-dlp -f "bv*+mergeall[vcodec=none]" --audio-multistreams # Download and merge the best format that has a video stream, # and the best 2 audio-only formats into one file $ yt-dlp -f "bv*+ba+ba.2" --audio-multistreams # The following examples show the old method (without -S) of format selection # and how to use -S to achieve a similar but (generally) better result # Download the worst video available (old method) $ yt-dlp -f "wv*+wa/w" # Download the best video available but with the smallest resolution $ yt-dlp -S "+res" # Download the smallest video available $ yt-dlp -S "+size,+br" # Download the best mp4 video available, or the best video if no mp4 available $ yt-dlp -f "bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4] / bv*+ba/b" # Download the best video with the best extension # (For video, mp4 > mov > webm > flv. For audio, m4a > aac > mp3 ...) $ yt-dlp -S "ext" # Download the best video available but no better than 480p, # or the worst video if there is no video under 480p $ yt-dlp -f "bv*[height<=480]+ba/b[height<=480] / wv*+ba/w" # Download the best video available with the largest height but no better than 480p, # or the best video with the smallest resolution if there is no video under 480p $ yt-dlp -S "height:480" # Download the best video available with the largest resolution but no better than 480p, # or the best video with the smallest resolution if there is no video under 480p # Resolution is determined by using the smallest dimension. # So this works correctly for vertical videos as well $ yt-dlp -S "res:480" # Download the best video (that also has audio) but no bigger than 50 MB, # or the worst video (that also has audio) if there is no video under 50 MB $ yt-dlp -f "b[filesize<50M] / w" # Download largest video (that also has audio) but no bigger than 50 MB, # or the smallest video (that also has audio) if there is no video under 50 MB $ yt-dlp -f "b" -S "filesize:50M" # Download best video (that also has audio) that is closest in size to 50 MB $ yt-dlp -f "b" -S "filesize~50M" # Download best video available via direct link over HTTP/HTTPS protocol, # or the best video available via any protocol if there is no such video $ yt-dlp -f "(bv*+ba/b)[protocol^=http][protocol!*=dash] / (bv*+ba/b)" # Download best video available via the best protocol # (https/ftps > http/ftp > m3u8_native > m3u8 > http_dash_segments ...) $ yt-dlp -S "proto" # Download the best video with either h264 or h265 codec, # or the best video if there is no such video $ yt-dlp -f "(bv*[vcodec~='^((he|a)vc|h26[45])']+ba) / (bv*+ba/b)" # Download the best video with best codec no better than h264, # or the best video with worst codec if there is no such video $ yt-dlp -S "codec:h264" # Download the best video with worst codec no worse than h264, # or the best video with best codec if there is no such video $ yt-dlp -S "+codec:h264" # More complex examples # Download the best video no better than 720p preferring framerate greater than 30, # or the worst video (still preferring framerate greater than 30) if there is no such video $ yt-dlp -f "((bv*[fps>30]/bv*)[height<=720]/(wv*[fps>30]/wv*)) + ba / (b[fps>30]/b)[height<=720]/(w[fps>30]/w)" # Download the video with the largest resolution no better than 720p, # or the video with the smallest resolution available if there is no such video, # preferring larger framerate for formats with the same resolution $ yt-dlp -S "res:720,fps" # Download the video with smallest resolution no worse than 480p, # or the video with the largest resolution available if there is no such video, # preferring better codec and then larger total bitrate for the same resolution $ yt-dlp -S "+res:480,codec,br"
from yt_dlp import YoutubeDL
with YoutubeDL() as ydl:
info_dict = ydl.extract_info('https://youtu.be/0KFSuoHEYm0', download=False)
video_url = info_dict.get("url", None)
video_id = info_dict.get("id", None)
video_title = info_dict.get('title', None)
print("Title: " + video_title) # <= Here, you got the video title
#[youtube] 0KFSuoHEYm0: Downloading webpage
#[youtube] 0KFSuoHEYm0: Downloading android player API JSON
#Title: TJ Watt gets his 4th sack of the game vs. Browns
import yt_dlp as youtube_dl
from yt_dlp.utils import DownloadError
url = "https://www.twitch.tv/videos/1410795876"
class loggerOutputs:
def error(msg):
print("Captured Error: "+msg)
def warning(msg):
print("Captured Warning: "+msg)
def debug(msg):
print("Captured Log: "+msg)
options = {
"quiet": True,
"format": "bestaudio/worst",
"logger": loggerOutputs,
}
with youtube_dl.YoutubeDL(options) as ydl:
try:
info = ydl.extract_info(url, download=False)
except DownloadError:
print("An exception has been caught")
from subprocess import Popen, PIPE, DEVNULL
import threading
import time
COPY_BUFSIZE = 65424
playlist = [
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
]
# Writer thread (read from yt-dlp and write to FFmpeg in chunks of COPY_BUFSIZE bytes).
def writer(yt_dlp_proc, encoder_proc):
while True:
yt_dlp_buf = yt_dlp_proc.stdout.read(COPY_BUFSIZE)
print("READ: yt_dlp")
if not yt_dlp_buf:
print("yt-dlp buffer empty")
break
written = encoder_proc.stdin.write(yt_dlp_buf)
print("WRITE: encoder. Bytes: " + str(written))
encoder_proc.stdin.close() # Close stdin pipe (closing stdin "pushes" the remaining data to stdout).
encoder_proc.wait() # Wait for sub-process finish execution.
if __name__ == "__main__":
rtmp_url = "rtmp://127.0.0.1/live/H1P_x5WPF"
ffplay_cmd = ['ffplay', '-listen', '1', '-i', rtmp_url] # Start the TCP server first, before the sending client.
ffplay_process = Popen(ffplay_cmd, stderr=DEVNULL) # Use FFplay sub-process for receiving the RTMP video.
stream_cmd = [
"ffmpeg", "-loglevel", "error",
"-hide_banner", "-re", "-i", "-",
"-c:v", "libx264",
"-f", "flv",
"-b:v", "3000k", "-minrate", "3000k",
"-maxrate", "3000k", "-bufsize", "3000k",
"-r", "25", "-pix_fmt", "yuv420p",
rtmp_url #"rtmp://127.0.0.1/live/H1P_x5WPF"
]
print(f'Stream command:\n"{" ".join(stream_cmd)}"')
encoder_cmd = [
"ffmpeg", "-re", "-i", "-", "-f", "mpegts",
"-c", "copy", "-"
]
print(f'Encoder command:\n"{" ".join(encoder_cmd)}"')
stream_p = Popen(stream_cmd, stdin=PIPE, stderr=DEVNULL)
for video in playlist:
yt_dlp_cmd = [
"yt-dlp", "-q",
video["url"],
"-o", "-"
]
print("Now playing: " + video["url"])
with Popen(yt_dlp_cmd, stdout=PIPE) as yt_dlp_p:
with Popen(encoder_cmd, stdin=PIPE, stdout=PIPE, stderr=DEVNULL) as encoder_p:
thread = threading.Thread(target=writer, args=(yt_dlp_p, encoder_p))
thread.start() # Start writer thread.
while True:
encoder_buf = encoder_p.stdout.read(COPY_BUFSIZE)
if not encoder_buf:
print("encoder_buf empty")
break
print("READ: encoder")
stream_bytes_written = stream_p.stdin.write(encoder_buf)
print("WRITE: stream, Bytes: " + str(stream_bytes_written))
thread.join() # Wait for writer thread to end.
yt_dlp_p.wait()
stream_p.stdin.close() # Close stdin pipe (closing stdin "pushes" the remaining data to stdout).
stream_p.wait() # Wait for sub-process finish execution.
time.sleep(3) # Wait 3 seconds before closing FFplay
ffplay_process.kill() # Forcefully close FFplay sub-process
from pytube import YouTube
from subprocess import Popen, run, PIPE, DEVNULL
import time
playlist = [
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
]
n = len(playlist)
# Build string for concat demuxer https://video.stackexchange.com/a/18256/18277
filter_complex_str = ''
for i in range(n):
filter_complex_str += f'[{i}:v]setpts=PTS-STARTPTS[v{i}];[{i}:a]asetpts=PTS-STARTPTS[a{i}];' # "[0:v]setpts=PTS-STARTPTS[v0];[0:a]asetpts=PTS-STARTPTS[a0];[1:v]setpts=PTS-STARTPTS[v1];[1:a]asetpts=PTS-STARTPTS[a1];[2:v]setpts=PTS-STARTPTS[v2];[2:a]asetpts=PTS-STARTPTS[a2]"
for i in range(n):
filter_complex_str += f'[v{i}][a{i}]' # ";[v0][a0][v1][a1][v2][a2]"
filter_complex_str += f'concat=n={n}:v=1:a=1[v][a]'
# Get the video stream URL of every YouTube HTTP URL.
# Add -i before each URL (to be used as FFmpeg input).
playlist_url = []
for video in playlist:
yt = YouTube(video["url"])
# https://github.com/pytube/pytube/issues/301
stream_url = yt.streams[0].url # Get the URL of the video stream
playlist_url.append('-i')
playlist_url.append(stream_url)
rtmp_url = "rtmp://127.0.0.1/live/H1P_x5WPF"
ffplay_cmd = ['ffplay', '-listen', '1', '-i', rtmp_url] # Start the TCP server first, before the sending client.
ffplay_process = Popen(ffplay_cmd, stderr=DEVNULL) # Use FFplay sub-process for receiving the RTMP video.
stream_cmd = [
"ffmpeg", "-loglevel", "error",
"-hide_banner", "-re"] + playlist_url + ["-filter_complex",
filter_complex_str, # '[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[v][a]'
"-map", "[v]", "-map", "[a]",
"-c:v", "libx264",
"-f", "flv",
"-b:v", "3000k", "-minrate", "3000k",
"-maxrate", "3000k", "-bufsize", "3000k",
"-r", "25", "-pix_fmt", "yuv420p",
rtmp_url]
run(stream_cmd)
time.sleep(60) # Wait 60 seconds before closing FFplay
ffplay_process.kill() # Forcefully close FFplay sub-process
'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe & {yt-dlp https://www.youtube.com/watch?v=jtjnnykvnh4}'
ydl_opts = {
'ratelimit': 500000
}
with yt_dlp.YoutubeDL(params=ydl_opts) as ydl:
ydl.download([link])
'ffmpeg_location': './FFMPEG_CONFIG/ffmpeg.exe',
heroku buildpacks:set heroku/python
heroku buildpacks:add --index 1 https://github.com/FFmpeg/FFmpeg.git
Trending Discussions on yt-dlp
Trending Discussions on yt-dlp
QUESTION
My bot is on Heroku.
[youtube:tab] PL4fGSI1pDJn5kI81J1fYWK5eZRl1zJ5kM: Downloading webpage
WARNING: [youtube:tab] unable to extract yt initial data; please report this issue on https://github.com/yt-dlp/yt-dlp , filling out the "Broken site" issue template properly. Confirm you are on the latest version using yt-dlp -U
WARNING: [youtube:tab] Incomplete yt initial data received. Retrying ...
[youtube:tab] PL4fGSI1pDJn5kI81J1fYWK5eZRl1zJ5kM: Downloading webpage (retry #1)
WARNING: [youtube:tab] unable to extract yt initial data; please report this issue on https://github.com/yt-dlp/yt-dlp , filling out the "Broken site" issue template properly. Confirm you are on the latest version using yt-dlp -U
WARNING: [youtube:tab] Incomplete yt initial data received. Retrying ...
[youtube:tab] PL4fGSI1pDJn5kI81J1fYWK5eZRl1zJ5kM: Downloading webpage (retry #2)
WARNING: [youtube:tab] unable to extract yt initial data; please report this issue on https://github.com/yt-dlp/yt-dlp , filling out the "Broken site" issue template properly. Confirm you are on the latest version using yt-dlp -U
WARNING: [youtube:tab] Incomplete yt initial data received. Retrying ...
[youtube:tab] PL4fGSI1pDJn5kI81J1fYWK5eZRl1zJ5kM: Downloading webpage (retry #3)
WARNING: [youtube:tab] unable to extract yt initial data; please report this issue on https://github.com/yt-dlp/yt-dlp , filling out the "Broken site" issue template properly. Confirm you are on the latest version using yt-dlp -U
WARNING: [youtube:tab] Incomplete yt initial data received
ERROR: [youtube:tab] PL4fGSI1pDJn5kI81J1fYWK5eZRl1zJ5kM: Playlists that require authentication may not extract correctly without a successful webpage download. If you are not downloading private content, or your cookies are only for the first account and channel, pass "--extractor-args youtubetab:skip=authcheck" to skip this check
If I write to the heroku console:
yt-dlp --cookies cookies.txt https://www.youtube.com/playlist?list=PLSdfU8nTff5QcNqq-muG9AZrYiVy4S3zV
the download of music starts (music is not played), but each time it is not very good to write a command to the console. Thanks in advance for your help
ANSWER
Answered 2022-Mar-10 at 08:54removed from code: "cookiefile": "/config/cookies/cookies.txt"
and everything worked
QUESTION
I'm using yt-dlp (in Python) to extract information from Twitch videos.
If I try to extract information from a non-existing video or a private one, I get an exception, which is the expected behaviour. But if I set to "quiet" mode and if I catch the potential exception, and I still get the error logged. Here is the code:
import yt_dlp as youtube_dl
from yt_dlp.utils import DownloadError
url = "https://www.twitch.tv/videos/1410795876"
options = {
"quiet": True,
"format": "bestaudio/worst",
}
with youtube_dl.YoutubeDL(options) as ydl:
try:
info = ydl.extract_info(url, download=False)
except DownloadError:
print("An exception has been caught")
When using this script, the output is the following:
ERROR: [twitch:vod] 1410795876: Failed to download m3u8 information: HTTP Error 403: Forbidden (caused by ); please report this issue on https://github.com/yt-dlp/yt-dlp , filling out the "Broken site" issue template properly. Confirm you are on the latest version using -U (caused by ); please report this issue on https://github.com/yt-dlp/yt-dlp , filling out the "Broken site" issue template properly. Confirm you are on the latest version using -U
An exception has been caught
Is there a way to hide this error log? And if not, is there a way using yt-dlp to check that the video is accessible so that I will not call extract_info
if the video is not? Thank you.
ANSWER
Answered 2022-Mar-02 at 20:27I guess that the easiest way to hide all logging errors is by implementing your own log handler. Consider the following change to your code, I'm sure you'll figure out how to implement what you want from here:
import yt_dlp as youtube_dl
from yt_dlp.utils import DownloadError
url = "https://www.twitch.tv/videos/1410795876"
class loggerOutputs:
def error(msg):
print("Captured Error: "+msg)
def warning(msg):
print("Captured Warning: "+msg)
def debug(msg):
print("Captured Log: "+msg)
options = {
"quiet": True,
"format": "bestaudio/worst",
"logger": loggerOutputs,
}
with youtube_dl.YoutubeDL(options) as ydl:
try:
info = ydl.extract_info(url, download=False)
except DownloadError:
print("An exception has been caught")
In the following example they disable logging by implementing a "FakeLogger": https://github.com/ytdl-org/youtube-dl/blob/master/test/test_http.py
QUESTION
This is my first question here therefore I hope this format is fine. I've searched for the issue on internet and checked the documentation of yt-dlp however could not find something useful or maybe I just dont understand what to do.
In a normal case, I was using youtube-dl to download musics from youtube and play it in my discord bot but its download rate restrictions became problematic (60-80 KiB/s). For this reason I started the use yt-dlp. It works fine if I use url directly. However, when I use a searchword instead of url, seems like code does not extract the info to get url. (Below code was working fine with youtube-dl too) Here is the code:
@bot.command()
async def play(ctx, *, searchword):
ydl_opts = {}
voice = ctx.voice_client
#get the title and url from video
Below code works fine with direct url but when I write something such as (! is my command prefix by the way) !play By the Sword download does not start but console says:
Downloading playlist: By the Sword
if searchword[0:4] == "http" or searchword[0:3] == "www":
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(searchword, download = False)
title = info["title"]
url = searchword
if searchword[0:4] != "http" and searchword[0:3] != "www":
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(f"ytsearch:{searchword}", download = False)["entries"][0]
title = info["title"]
url = info["webpage_url"]
ydl_opts = {
'format' : 'bestaudio/best',
"outtmpl" : f"{title}.mp3",
"postprocessors":
[{"key" : "FFmpegExtractAudio", "preferredcodec" : "mp3", "preferredquality" : "192"}],
}
def download(url):
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, download, url)
#playing and queueing audio
if voice.is_playing():
queuelist.append(title)
await ctx.send(f"Added to queue: {title}")
else:
voice.play(discord.FFmpegPCMAudio(f"{title}.mp3"), after = lambda e : check_queue())
await ctx.send(f"Playing {title} !!!")
filestodelete.append(title)
def check_queue():
try:
if queuelist[0] != None:
voice.play(discord.FFmpegPCMAudio(f"{queuelist[0]}.mp3"), after = lambda e : check_queue())
filestodelete.append(queuelist[0])
queuelist.pop(0)
except IndexError:
for file in filestodelete:
os.remove(f"{file}.mp3")
filestodelete.clear()
I am not sure if the problem is about
info = ydl.extract_info(f"ytsearch:{searchword}", download = False)["entries"][0]
or downloading itself. Documentation of yt-dlp says that,
Tip: If you are porting your code from youtube-dl to yt-dlp, one important point to look out for is that we do not guarantee the return value of YoutubeDL.extract_info to be json serializable, or even be a dictionary. It will be dictionary-like, but if you want to ensure it is a serializable dictionary, pass it through YoutubeDL.sanitize_info as shown in the example above
Here is the link: https://github.com/yt-dlp/yt-dlp#embedding-yt-dlp Thanks.
ANSWER
Answered 2022-Feb-27 at 12:23Apparently, I solved the issue by myself by using a different way. As I mentioned above youtube_dl works fine however yt_dlp .extract_info()
is problematic. A simple solution to fix this is extracting info by using youtube_dl module, then download file by using yt_dlp.
Use this for downlaoding:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
But this for extracting url and title from a given string which is not URL
if searchword[0:4] != "http" and searchword[0:3] != "www":
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(f"ytsearch:{searchword}", download = False)["entries"][0]
title = info["title"]
url = info["webpage_url"]
Do not forget to include both modules!
QUESTION
I'm trying to use yt-dlp rather than youtube-dl due to the bottlenecking on download speeds in youtube-dl but I can't get it to work.
My mpv.conf file looks like:
script-opts=ytdl_hook-ytdl_path=/usr/local/bin/yt-dlp
When trying to get mpv to run I get this warning:
[ytdl_hook] script-opts: unknown key ytdl_path, ignoring
Does anyone know what the problem is? I've read through the mpv docs and it says this should work.
mpv is version 0.27.2
ANSWER
Answered 2022-Feb-19 at 18:05I had the same problem as you, on mpv version 0.32.0. The problem is that the ytdl-hook settings category is not an option in these older versions. Your solutions are to update your mpv version or to create a link from youtube-dl to yt-dlp. Hope this helps.
QUESTION
I am building a discord music bot that uses FFMPEG to download and play the music. yt-dlp seems to not be able to find the ffmpeg file for some reason although I give it the direct path. It works perfectly fine whe I run it on my cpu, but it doesn't work in heroku.
Here's the error in heroku:
2022-02-14T00:34:29.047823+00:00 app[worker.1]: During handling of the above exception, another exception occurred:
2022-02-14T00:34:29.047823+00:00 app[worker.1]:
2022-02-14T00:34:29.047825+00:00 app[worker.1]: Traceback (most recent call last):
2022-02-14T00:34:29.047854+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/discord/client.py", line 343, in _run_event
2022-02-14T00:34:29.047855+00:00 app[worker.1]: await coro(*args, **kwargs)
2022-02-14T00:34:29.047856+00:00 app[worker.1]: File "/app/engine.py", line 97, in on_reaction_add
2022-02-14T00:34:29.047856+00:00 app[worker.1]: ydl.download([f'{result["link"]}'])
2022-02-14T00:34:29.047863+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 3212, in download
2022-02-14T00:34:29.047863+00:00 app[worker.1]: self.__download_wrapper(self.extract_info)(
2022-02-14T00:34:29.047865+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 3185, in wrapper
2022-02-14T00:34:29.047865+00:00 app[worker.1]: res = func(*args, **kwargs)
2022-02-14T00:34:29.047875+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1372, in extract_info
2022-02-14T00:34:29.047876+00:00 app[worker.1]: return self.__extract_info(url, self.get_info_extractor(ie_key), download, extra_info, process)
2022-02-14T00:34:29.047877+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1381, in wrapper
2022-02-14T00:34:29.047877+00:00 app[worker.1]: return func(self, *args, **kwargs)
2022-02-14T00:34:29.047891+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1465, in __extract_info
2022-02-14T00:34:29.047891+00:00 app[worker.1]: return self.process_ie_result(ie_result, download, extra_info)
2022-02-14T00:34:29.047893+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1517, in process_ie_result
2022-02-14T00:34:29.047894+00:00 app[worker.1]: ie_result = self.process_video_result(ie_result, download=download)
2022-02-14T00:34:29.047894+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 2607, in process_video_result
2022-02-14T00:34:29.047894+00:00 app[worker.1]: self.process_info(new_info)
2022-02-14T00:34:29.047896+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 3161, in process_info
2022-02-14T00:34:29.047896+00:00 app[worker.1]: self.report_error('Postprocessing: %s' % str(err))
2022-02-14T00:34:29.047904+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 930, in report_error
2022-02-14T00:34:29.047904+00:00 app[worker.1]: self.trouble(f'{self._format_err("ERROR:", self.Styles.ERROR)} {message}', *args, **kwargs)
2022-02-14T00:34:29.047905+00:00 app[worker.1]: File "/app/.heroku/python/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 871, in trouble
2022-02-14T00:34:29.047905+00:00 app[worker.1]: raise DownloadError(message, exc_info)
2022-02-14T00:34:29.047921+00:00 app[worker.1]: yt_dlp.utils.DownloadError: ERROR: Postprocessing: ffprobe and ffmpeg not found. Please install or provide the path using --ffmpeg-location
Here's my setup: ![root level directory]: https://i.stack.imgur.com/HKo8L.png
Here's the code:
yt_opts = {
'format': 'bestaudio/best',
'ffmpeg_location': './FFMPEG_CONFIG/ffmpeg.exe',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}]
}
# Getting Song From Thy YOUTUBE
await channel.send("> Getting Song From Youtube")
with yt_dlp.YoutubeDL(yt_opts) as ydl:
ydl.download([f'{result["link"]}'])
ANSWER
Answered 2022-Feb-14 at 00:57You can't do this:
'ffmpeg_location': './FFMPEG_CONFIG/ffmpeg.exe',
ffmpeg.exe
is a Windows binary. It cannot be used on Heroku (and should not be part of your repository to begin with).
Most users who want to use ffmpeg
on Heroku use the buildpack (see also multiple buildpacks), e.g.:
heroku buildpacks:set heroku/python
heroku buildpacks:add --index 1 https://github.com/FFmpeg/FFmpeg.git
Then remove that hard-coded ffmpeg_location
, commit, and redeploy. Your app should find ffmpeg
.
For local development you have a couple of of options:
- Update your
PATH
to make sureffmpeg.exe
can be found - Update your code to set an
ffmpeg_location
, ideally from an environment variable
QUESTION
from yt_dlp import YoutubeDL
with YoutubeDL() as ydl:
ydl.download('https://youtu.be/0KFSuoHEYm0')
this is the relevant bit of code producing the output.
what I would like to do is grab the 2nd last line from the output below, specifying the video title.
I have tried a few variations of
output = subprocess.getoutput(ydl)
as well as
output = subprocess.Popen( ydl, stdout=subprocess.PIPE ).communicate()[0]
the output I am attempting to capture is the 2nd last line here:
[youtube] 0KFSuoHEYm0: Downloading webpage
[youtube] 0KFSuoHEYm0: Downloading android player API JSON
[info] 0KFSuoHEYm0: Downloading 1 format(s): 22
[download] Destination: TJ Watt gets his 4th sack of the game vs. Browns [0KFSuoHEYm0].mp4
[download] 100% of 13.10MiB in 00:01
There is also documentation on yt-dlp on how to pull title from metadata or include as something in the brackets behind YoutubeDL(), but I can not quite figure it out.
This is part of the first project I am making in python. I am missing an understanding of many concepts any help would be much appreciated.
ANSWER
Answered 2022-Jan-07 at 15:39Credits: answer to question: How to get information from youtube-dl in python ??
Modify your code as follows:
from yt_dlp import YoutubeDL
with YoutubeDL() as ydl:
info_dict = ydl.extract_info('https://youtu.be/0KFSuoHEYm0', download=False)
video_url = info_dict.get("url", None)
video_id = info_dict.get("id", None)
video_title = info_dict.get('title', None)
print("Title: " + video_title) # <= Here, you got the video title
This is the output:
#[youtube] 0KFSuoHEYm0: Downloading webpage
#[youtube] 0KFSuoHEYm0: Downloading android player API JSON
#Title: TJ Watt gets his 4th sack of the game vs. Browns
QUESTION
Edited* Solution: Remove "pause". I'm running a python script which calls upon powershell to execute a line of code:
def download():
subprocess.call('C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe yt-dlp https://www.youtube.com/watch?v=jtjnnykvnh4;pause', shell=True)
download()
The problem was that after executing, it would output "Press Enter to continue..." This interrupts the program.*in my original example I forgot to include the ";pause" which is what turned out to be what was causing the interruption in the program, as kindly pointed out by the marked answer. Below is the fixed line of code which does not prompt "press enter to continue" after running:
def download():
subprocess.call('C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe yt-dlp https://www.youtube.com/watch?v=jtjnnykvnh4;kill $pid', shell=True)
download()
Apologies for confusion caused by the original post. Thanks for the help.
ANSWER
Answered 2022-Jan-07 at 03:17PowerShell normally exits unless you specify -NoExit
at the commandline. Even then it will not include the message you are seeing unless you add a pause
at the end instead. Even so, I would expect your command to look more like
'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe & {yt-dlp https://www.youtube.com/watch?v=jtjnnykvnh4}'
My guess this has more to do with Python, though I have not encountered it before...have you tried executing the PowerShell line from another commandline (cmd on Windows or bash on Linux/Mac or another favourite) to verify that you get the same result independently of Python?
Another possibility is that it is the yt-dlp
tool that you are using that has the pause
effect (I am not familiar with the tool). Is it a PowerShell module? Or is it something that can be run on the commandline and you don't need PowerShell as a middleman anyway? Would it have a "silent" or "-q
" argument, or another more relevant argument?
QUESTION
I have implemented yt-dlp as part of my Python script, it works well, but I am unable to get the rate-limit feature to work. If you run the same command from the CLI the rate is limited correctly, is anyone able to tell me the correct syntax?
I have tried several combinations such as rate-limit, limit-rate 0.5m, 500k, 500KiB, 500, and none seem to work
ydl_opts = {
'limit-rate': '500k',
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([link])
I am using the docs here; https://github.com/yt-dlp/yt-dlp But am confused as the CLI command works but not the embedded script version,
I also tried replacing - with _ but still to no effect, do you have any ideas? Other options in the ydl_opts work without issue
Hopefully we can resolve the correct syntax rather than having to implement Trickle or throttling the socket
Thank you if you can help
ANSWER
Answered 2021-Nov-07 at 17:07Looking at the source code you'll find that the option you're looking for is called ratelimit
. Its value should be a float:
ydl_opts = {
'ratelimit': 500000
}
with yt_dlp.YoutubeDL(params=ydl_opts) as ydl:
ydl.download([link])
QUESTION
I've written a downloader in python 3.9.1 that utilizes yt-dlp, using this format:
outTmpl = "{}"
rows = c.fetchall()
ydl_opts = dict()
for row in rows:
ydl_opts = {
'verbose': True,
'outtmpl': outTmpl.format(row[1]),
'abort-on-unavailable-fragment': True,
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([row[0]])
However some downloads skip fragments (due to timeouts or whatever), leaving me with something that is cut short.
Any idea what i need to put in the ydl_opts? I thought 'abort-on-unavailable-fragment': True
would suffice, but the download carries on and processes the file with ffmpeg.
Any idea how i fix this please and if it starts failing to download segments, just abort the job and remove any files its created? Maybe the option i'm calling is incorrect?
ANSWER
Answered 2021-Nov-16 at 16:50The option is named skip_unavailable_fragments
. See the docstring of FragmentFD
if it starts failing to download segments, just abort the job and remove any files its created?
There is no functionality in yt-dlp to automatically remove the temporary files. You will have to catch the error and do it yourself
QUESTION
I'm trying to make a continuous livestream of videos downloaded via yt-dlp. I need to port this (working) bash command into Python.
(
youtube-dl -v --buffer-size 16k https://youtube.com/watch?v=QiInzFHIDp4 -o - | ffmpeg -i - -f mpegts -c copy - ;
youtube-dl -v --buffer-size 16k https://youtube.com/watch?v=QiInzFHIDp4 -o - | ffmpeg -i - -f mpegts -c copy - ;
) | ffmpeg -re -i - -c:v libx264 -f flv rtmp://127.0.0.1/live/H1P_x5WPF
My Python attempt is cutting off the last ~2 seconds of each video. My suspicion is that although the first pipe, yt-dlp, has an empty stdout, there is still data travelling between the second and third pipe. I haven't been able to figure out a way to properly handle the data between those two pipes at the end of the video.
from subprocess import Popen, PIPE, DEVNULL
COPY_BUFSIZE = 65424
playlist = [
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
]
if __name__ == "__main__":
stream_cmd = [
"ffmpeg", "-loglevel", "error",
"-hide_banner", "-re", "-i", "-",
"-c:v", "libx264",
"-f", "flv",
"-b:v", "3000k", "-minrate", "3000k",
"-maxrate", "3000k", "-bufsize", "3000k",
"-r", "25", "-pix_fmt", "yuv420p",
"rtmp://127.0.0.1/live/H1P_x5WPF"
]
print(f'Stream command:\n"{" ".join(stream_cmd)}"')
encoder_cmd = [
"ffmpeg", "-re", "-i", "-", "-f", "mpegts",
"-c", "copy", "-"
]
print(f'Encoder command:\n"{" ".join(encoder_cmd)}"')
stream_p = Popen(stream_cmd, stdin=PIPE, stderr=DEVNULL)
for video in playlist:
yt_dlp_cmd = [
"yt-dlp", "-q",
video["url"],
"-o", "-"
]
print("Now playing: " + video["url"])
with Popen(yt_dlp_cmd, stdout=PIPE) as yt_dlp_p:
with Popen(encoder_cmd, stdin=PIPE, stdout=PIPE, stderr=DEVNULL) as encoder_p:
while True:
yt_dlp_buf = yt_dlp_p.stdout.read(COPY_BUFSIZE)
print("READ: yt_dlp")
if not yt_dlp_buf:
print("yt-dlp buffer empty")
# Handle any data in 2nd/3rd pipes before breaking?
break
written = encoder_p.stdin.write(yt_dlp_buf)
print("WRITE: encoder. Bytes: " + str(written))
encoder_buf = encoder_p.stdout.read(COPY_BUFSIZE)
# if not encoder_buf:
# print("encoder_buf empty")
# break
print("READ: encoder")
stream_bytes_written = stream_p.stdin.write(encoder_buf)
print("WRITE: stream, Bytes: " + str(stream_bytes_written))
$20 BTC bounty to anyone who can help.
Running Python 3.6.9 on MacOS.
ANSWER
Answered 2021-Nov-07 at 18:06Closing the stdin
pipe is required for "pushing" the sub-process remaining (buffered) data to stdout
pipe.
For example, add encoder_p.stdin.close()
after finish writing all data to encoder_p.stdin
.
I don't understand how your code is working.
In my machine, it gets stack at encoder_buf = encoder_p.stdout.read(COPY_BUFSIZE)
.
I solved the problem using a "writer thread".
The "writer thread" reads data from yt_dlp_p
and write it to encoder_p.stdin
.
Note: In your specific case, it could work without a thread (because the data is just passed through FFmpeg, and not being encoded), but usually, the encoded data is not ready right after writing the input to FFmpeg.
My code sample uses FFplay sub-process for playing the video (we need the video player because the RTMP streaming requires a "listener" in order to keep streaming).
Here is a complete code sample:
from subprocess import Popen, PIPE, DEVNULL
import threading
import time
COPY_BUFSIZE = 65424
playlist = [
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
]
# Writer thread (read from yt-dlp and write to FFmpeg in chunks of COPY_BUFSIZE bytes).
def writer(yt_dlp_proc, encoder_proc):
while True:
yt_dlp_buf = yt_dlp_proc.stdout.read(COPY_BUFSIZE)
print("READ: yt_dlp")
if not yt_dlp_buf:
print("yt-dlp buffer empty")
break
written = encoder_proc.stdin.write(yt_dlp_buf)
print("WRITE: encoder. Bytes: " + str(written))
encoder_proc.stdin.close() # Close stdin pipe (closing stdin "pushes" the remaining data to stdout).
encoder_proc.wait() # Wait for sub-process finish execution.
if __name__ == "__main__":
rtmp_url = "rtmp://127.0.0.1/live/H1P_x5WPF"
ffplay_cmd = ['ffplay', '-listen', '1', '-i', rtmp_url] # Start the TCP server first, before the sending client.
ffplay_process = Popen(ffplay_cmd, stderr=DEVNULL) # Use FFplay sub-process for receiving the RTMP video.
stream_cmd = [
"ffmpeg", "-loglevel", "error",
"-hide_banner", "-re", "-i", "-",
"-c:v", "libx264",
"-f", "flv",
"-b:v", "3000k", "-minrate", "3000k",
"-maxrate", "3000k", "-bufsize", "3000k",
"-r", "25", "-pix_fmt", "yuv420p",
rtmp_url #"rtmp://127.0.0.1/live/H1P_x5WPF"
]
print(f'Stream command:\n"{" ".join(stream_cmd)}"')
encoder_cmd = [
"ffmpeg", "-re", "-i", "-", "-f", "mpegts",
"-c", "copy", "-"
]
print(f'Encoder command:\n"{" ".join(encoder_cmd)}"')
stream_p = Popen(stream_cmd, stdin=PIPE, stderr=DEVNULL)
for video in playlist:
yt_dlp_cmd = [
"yt-dlp", "-q",
video["url"],
"-o", "-"
]
print("Now playing: " + video["url"])
with Popen(yt_dlp_cmd, stdout=PIPE) as yt_dlp_p:
with Popen(encoder_cmd, stdin=PIPE, stdout=PIPE, stderr=DEVNULL) as encoder_p:
thread = threading.Thread(target=writer, args=(yt_dlp_p, encoder_p))
thread.start() # Start writer thread.
while True:
encoder_buf = encoder_p.stdout.read(COPY_BUFSIZE)
if not encoder_buf:
print("encoder_buf empty")
break
print("READ: encoder")
stream_bytes_written = stream_p.stdin.write(encoder_buf)
print("WRITE: stream, Bytes: " + str(stream_bytes_written))
thread.join() # Wait for writer thread to end.
yt_dlp_p.wait()
stream_p.stdin.close() # Close stdin pipe (closing stdin "pushes" the remaining data to stdout).
stream_p.wait() # Wait for sub-process finish execution.
time.sleep(3) # Wait 3 seconds before closing FFplay
ffplay_process.kill() # Forcefully close FFplay sub-process
I found a simpler solution using pytube and concat filter (without pipes).
I don't know if the solution is relevant for you...
Code sample:
from pytube import YouTube
from subprocess import Popen, run, PIPE, DEVNULL
import time
playlist = [
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
{
# 15 second video
"url": "https://youtube.com/watch?v=QiInzFHIDp4"
},
]
n = len(playlist)
# Build string for concat demuxer https://video.stackexchange.com/a/18256/18277
filter_complex_str = ''
for i in range(n):
filter_complex_str += f'[{i}:v]setpts=PTS-STARTPTS[v{i}];[{i}:a]asetpts=PTS-STARTPTS[a{i}];' # "[0:v]setpts=PTS-STARTPTS[v0];[0:a]asetpts=PTS-STARTPTS[a0];[1:v]setpts=PTS-STARTPTS[v1];[1:a]asetpts=PTS-STARTPTS[a1];[2:v]setpts=PTS-STARTPTS[v2];[2:a]asetpts=PTS-STARTPTS[a2]"
for i in range(n):
filter_complex_str += f'[v{i}][a{i}]' # ";[v0][a0][v1][a1][v2][a2]"
filter_complex_str += f'concat=n={n}:v=1:a=1[v][a]'
# Get the video stream URL of every YouTube HTTP URL.
# Add -i before each URL (to be used as FFmpeg input).
playlist_url = []
for video in playlist:
yt = YouTube(video["url"])
# https://github.com/pytube/pytube/issues/301
stream_url = yt.streams[0].url # Get the URL of the video stream
playlist_url.append('-i')
playlist_url.append(stream_url)
rtmp_url = "rtmp://127.0.0.1/live/H1P_x5WPF"
ffplay_cmd = ['ffplay', '-listen', '1', '-i', rtmp_url] # Start the TCP server first, before the sending client.
ffplay_process = Popen(ffplay_cmd, stderr=DEVNULL) # Use FFplay sub-process for receiving the RTMP video.
stream_cmd = [
"ffmpeg", "-loglevel", "error",
"-hide_banner", "-re"] + playlist_url + ["-filter_complex",
filter_complex_str, # '[0:v][0:a][1:v][1:a][2:v][2:a]concat=n=3:v=1:a=1[v][a]'
"-map", "[v]", "-map", "[a]",
"-c:v", "libx264",
"-f", "flv",
"-b:v", "3000k", "-minrate", "3000k",
"-maxrate", "3000k", "-bufsize", "3000k",
"-r", "25", "-pix_fmt", "yuv420p",
rtmp_url]
run(stream_cmd)
time.sleep(60) # Wait 60 seconds before closing FFplay
ffplay_process.kill() # Forcefully close FFplay sub-process
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
Vulnerabilities
No vulnerabilities reported
Install yt-dlp
Support
Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from over 650 million Knowledge Items
Find more librariesExplore Kits - Develop, implement, customize Projects, Custom Functions and Applications with kandi kits
Save this library and start creating your kit
Share this Page