Search code examples
python-3.xgoogle-drive-apitelegramtelethon

How to upload downloaded telegram media directly on google drive?


I'm working on the telethon download_media method for downloading images and videos. It is working fine (as expected). Now, I want to directly upload the download_media to my google drive folder.

Sample code looks something like:

from telethon import TelegramClient, events, sync
from telethon.tl.types import PeerUser, PeerChat, PeerChannel
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

gauth = GoogleAuth()
drive = GoogleDrive(gauth)
gfile = drive.CreateFile({'parents': [{'id': 'drive_directory_path'}]})
api_id = #####
api_hash = ##########

c = client.get_entity(PeerChannel(1234567)) # some random channel id

for m in client.iter_messages(c):
        if m.photo:
            # below is the one way and it works
            # m.download_media("Media/")
            
            # I want to try something like this - below code
            gfile.SetContentFile(m.media)
            gfile.Upload()

This code is not working. How Can I define the google drive object for download_media?

Thanks in advance. Kindly assist!


Solution

  • The main problem is that according to PyDrive's documentation, setContentFile() expects a string with the file's local path, and then it just uses open(), so you're meant to use this with local files. In your code you're trying to feed it the media file so it won't work.

    To upload a bytes file with PyDrive you'll need to convert it to BytesIO and send it as the content. An example with a local file would look like this:

    drive = GoogleDrive(gauth)
    
    file = drive.CreateFile({'mimeType':'image/jpeg', 'title':'example.jpg'})
    filebytes = open('example.jpg', 'rb').read()
    
    file.content = io.BytesIO(filebytes)
    file.Upload()
    

    Normally you don't need to do it this way because setContentFile() does the opening and conversion for you, but this should give you the idea that if you get the bytes media file you can just convert it and assign it to file.content and then you can upload it.

    Now, if you look at the Telethon documentation, you will see that download_media() takes a file argument which you can set to bytes:

    file (str | file, optional):

    The output file path, directory, or stream-like object. If the path exists and is a file, it will be overwritten. If file is the type bytes, it will be downloaded in-memory as a bytestring (e.g. file=bytes).

    So you should be able to call m.download_media(file=bytes) to get a bytes object. Looking even deeper at the Telethon source code it appears that this does return a BytesIO object. With this in mind, you can try the following change in your loop:

    for m in client.iter_messages(c):
            if m.photo:
                gfile.content = io.BytesIO(m.download_media(file=bytes))
                gfile.Upload()
    

    Note that I only tested the PyDrive side since I currently don't have access to the Telegram API, but looking at the docs I believe this should work. Let me know what happens.

    Sources: