Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow downloading to buffer #3298

Open
5 tasks done
iuriguilherme opened this issue Apr 4, 2022 · 9 comments
Open
5 tasks done

Allow downloading to buffer #3298

iuriguilherme opened this issue Apr 4, 2022 · 9 comments
Labels
enhancement New feature or request

Comments

@iuriguilherme
Copy link

Checklist

Description

Although for the command line interface, almost all the time it is desirable to save the file to a local path. However when using ytdl as a module using the io library to save files to buffers may be desirable.

My use case is a bunch of chatbots which download a video from an URL and then uploads the file back to the user, allowing a interface for people with no access to a CLI or in an environment where it would be difficult to use (such as a locked smartphone).

It would be more efficient if the script can download the file to a io.BufferIO object then upload it without having to make a temporary file in the disk, as it already does with other kind of media (for example with a qr code generator).

@iuriguilherme iuriguilherme added enhancement New feature or request triage Untriaged issue labels Apr 4, 2022
@pukkandan pukkandan removed the triage Untriaged issue label Apr 4, 2022
@pukkandan
Copy link
Member

The filename is what is passed around b/w the various classes/functions instead of a file object, with special handling for - (stdout). That makes this a bit hard to implement

@dirkf
Copy link
Contributor

dirkf commented Apr 4, 2022

Indeed.

See also ytdl-org/youtube-dl#30815.

@pukkandan
Copy link
Member

Assuming single threaded code, contextlib.redirect_stdout can be used as a workaround

@FirefoxMetzger
Copy link
Contributor

FirefoxMetzger commented Jul 12, 2022

I tried the contextlib.redirect_stdout workaround and it appears to work fine 😄. Here is an example snippet for youtube:

from yt_dlp import YoutubeDL
from contextlib import redirect_stdout
from pathlib import Path

youtube_id = "some-video-id"

ctx = {
    "outtmpl": "-",
    'logtostderr': True
}

buffer = io.BytesIO()
with redirect_stdout(buffer), YoutubeDL(ctx) as foo:
    foo.download([youtube_id])

# write out the buffer for demonstration purposes
Path(f"{youtube_id}.mp4").write_bytes(buffer.getvalue())

Edit: A caveat is that this only seems to work on linux. On Windows the redirect to stdout seems to break and I get a ERROR: unable to open for writing: fileno.

@pukkandan
Copy link
Member

Edit: A caveat is that this only seems to work on linux. On Windows the redirect to stdout seems to break and I get a ERROR: unable to open for writing: fileno.

This should now work in windows too

@pukkandan pukkandan reopened this Jul 30, 2022
@a-amir01
Copy link

a-amir01 commented May 3, 2023

@pukkandan, I was trying the above workaround and it works great. But If I want to download the video in a specific format

ctx = {
    "outtmpl": "-",
    'logtostderr': True,
    'format': '600'
}

buffer = io.BytesIO()
with redirect_stdout(buffer), YoutubeDL(ctx) as video:
    video.download([youtube_id])

print(buffer.getbuffer().nbytes / 1000000.0)

By the time it reaches the print statement the buffer is closed.

Traceback (most recent call last):
File "videoConverter.py", line 131, in
print(buffer.getbuffer().nbytes / 1000000.0)
ValueError: I/O operation on closed file.

But this only happens when the field 'format' is present in the ctx object.

Would you happen to know why this is happening and how I can fix it ?

@Grub4K
Copy link
Member

Grub4K commented Jul 17, 2023

You can overwrite the close method on the buffer, so YoutubeDL doesn't close it and you can keep using it as a file, like

old_buffer_close = buffer.close
buffer.close = lambda *_: ...
# YoutubeDL code
old_buffer_close()  # Close when done

@Tom1380
Copy link

Tom1380 commented Jul 23, 2023

Any idea on how to do this for subtitles? This is what I got so far:

import io
from contextlib import *
from yt_dlp import *
from pathlib import *

youtube_id = 'https://youtube.com/watch?v=0nwbzoIP14E'

ctx = {
    "outtmpl": "-",
    'logtostderr': True,
    'skip_download': True,
    'writesubtitles': True,
    'sub_langs': 'en.*',
}

buffer = io.BytesIO()
with redirect_stdout(buffer), YoutubeDL(ctx) as foo:
    foo.download([youtube_id])

print(buffer.getvalue())

It's still writing the subtitles to a file and the buffer seems to be empty.

@pukkandan
Copy link
Member

You can't #3554

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

7 participants