Search code examples
pythonpython-3.xtelegram-botpython-telegram-botpytube

How can I use the `link` variable in my callbackQuery function?


I wanna make generate buttons for each format and then download the video with the chosen format ...

How can I do it?

steps:

1 - ask for the link

2- get the link and show all the available streams (make buttons for them)

3-download the video with the specified resolution (here we need a callback query handler)

Step 1

def ask_for_link(update,context):
    bot.send_message(chat_id=update.effective_chat.id ,text="Please Paste the video link here ... ")
    return STREAMS

Step 2

def streams (update , context) :
    try:
        link=str(update.message.text)

        Video_Url = YouTube(link)
        Video= Video_Url.streams
        chooseRes = []
        for i in Video :
            chooseRes.append([str(i).split()[2][str(i).split()[2].index("=")+2:len(str(i).split()[2])-1] , str(i).split()[3][str(i).split()[3].index("=")+2:len(str(i).split()[3])-1]])
        print(chooseRes)

        # list of resolution menu's buttons
        res_menu_buttons_list = []

         # a loop which create a button for each of resolutions
        for each in chooseRes:
            res_menu_buttons_list.append(InlineKeyboardButton(f"{each[0]}  ❤️  {each[1]}", callback_data = each[1] ))
       # print(res_menu_buttons_list)

        #it'll show the buttons on the screen 
        replyMarkup=InlineKeyboardMarkup(build_res_menu(res_menu_buttons_list, n_cols=2 )) 
        #button's explanaions 
        update.message.reply_text("Choose an Option from the menu below ... ",reply_markup=replyMarkup)
        return CLICK_FORMAT

    except Exception as e:
        print(e)
        bot.send_message(chat_id=update.effective_chat.id , text = "Oooops !!! Something Went Wrong ... \n\n ( Make sure that you wrote the link correctly )")
        quit(update,context)


# make columns based on how we declared 
def build_res_menu(buttons,n_cols,header_buttons=None,footer_buttons=None):
    res_menu_buttons = [buttons[i:i + n_cols] for i in range(0 , len(buttons), n_cols)]
    if header_buttons:
        res_menu_buttons.insert(0, header_buttons)
    if footer_buttons:
        res_menu_buttons.append(footer_buttons)
    return res_menu_buttons

till now everything went good and smooth... Bot asks for the link, Gets the link, and generates a button for each format ...

now we need a CallBackQueryHandler for downloading the video with the chosen format ...

I did so but it doesn't work properly ... I need to use the link variable in my function but I have no idea HOW ... I tried to return it from the streams function but I couldn't cause I need to return the function too I use (ConverstationHandler)

anyway, it's the thing that I have till now ...

Step 3

def click_format(update,context):
    query = update.callback_query
    """
       My problem is exactly HERE 
       as You see I tried to Use Downloader. link but it doesn't work ... 
       I receive this error : AttributeError: 'function' object has no attribute 'link'

    """
    Video = YouTube(Downloader.link).streams.filter(res=query.data).first().download()

        
    format_vid=re.sub(Video.mime_type[:Video.mime_type.index("/")+1],"",Video.mime_type)  
    bot.send_message(chat_id=update.effective_chat.id , text = f"{Video.title} has Downloaded successfully ... ")
    bot.send_video(chat_id=update.effective_chat.id ,video=open(f"{Video.title}.{format_vid}" , 'rb'), supports_streaming=True)
      
    try:
        os.remove(f"{Video.title}.{format_vid}")
        print("removed")
    except:
        print("Can't remove")
        quit(update,context)

It's My ConverstationHandler :

DOWNLOADER , CLICK_FORMAT = 0 ,1

YouTube_Downloader_converstation_handler=ConversationHandler(
    entry_points=[CommandHandler("YouTubeDownloader", ask_for_link)] , 
    states={
        STREAMS :[MessageHandler(Filters.text , callback=streams )],
        CLICK_FORMAT : [CallbackQueryHandler(click_format)]
          
        },
    fallbacks=[CommandHandler("quit" , quit)]) 

dispatcher.add_handler(YouTube_Downloader_converstation_handler)

Can anyone help me with the third step? I want to download the video in the chosen Format .... How can I use the link variable in My click_formatfunction ????


Solution

  • IISC your problem is that you want to access the variable link defined in streams in the conversation state CLICK_FORMAT/ in the callback click_format.

    As the update variable in the click_format contains information about the button click and not about the previously sent message, you'll have to store the link in a variable that click_format can access. You could naively use a global variable for that, but that has multiple downsides. Most notable, this will not work well when multiple users try to user your bot at the same time.

    However, PTB comes with a bulit-in mechanism just for that: context.user_data is a dictionary where you can store information associated to that user. So you could store the link as e.g. context.user_data['link'] = link and the access it in click_format as link = context.user_data['link'].

    Please see the wiki page for an explanation of user_data with example and also see conversationbot.py example, where this feature is used in conjunction with a ConversationHandler.