4

I have a Discord Bot and I want to send a message whenever a certain user uploads a video. I have had a look at the Youtube API (https://developers.google.com/youtube/v3/docs/videos) but have not found how to do what I want.

  • 4
    You can check in this [documentation](https://developers.google.com/youtube/v3/guides/push_notifications) on how to subscribe to Push notification. It is stated here that the YouTube Data API (v3) supports push notifications via [PubSubHubbub](https://github.com/pubsubhubbub/), a server-to-server publish/subscribe protocol for Web-accessible resources. Notifications are pushed out to subscribers via HTTP webhooks, which is much more efficient than polling-based solutions. – KENdi Apr 11 '17 at 16:41

2 Answers2

2

Well ..I do it using checking for the video every 1 minute and check if the video link(or id) is not equal to the last video then post the video in that particular channel you want to post to. I use google-api-python-client Firstly pip install google-api-python-client

from from googleapiclient.discovery import build

In your on_ready function

@client.event
async def on_ready():

    youtube=build('youtube','v3',developerKey='Enter your key here')
    req=youtube.playlistItems().list(
       playlistId='The Playlist id of the channel you want to post video i.e. the id of video playlist of the channel',
       part='snippet',
       maxResults=1
    )
    res=req.execute()
    vedioid=res['items'][0]['snippet']['resourceId']['videoId']
    link="https://www.youtube.com/watch?v="+vedioid
    ch=await client.fetch_channel(the  channel id from where im checking for the new video)
    await ch.send(link)


    yt.start()#Starting tasks loop which is made below for checking every minute if the new video is equal or unequal to old video link

Making the tasks loop for checking the video

@tasks.loop(seconds=60)
async def yt():      
     youtube=build('youtube','v3',developerKey='Enter your key here')
     req=youtube.playlistItems().list(
         playlistId='The Playlist id of the channel you want to post video i.e. the id of video playlist of the channel',
         part='snippet',
         maxResults=1
     )
     res=req.execute()
     vedioid=res['items'][0]['snippet']['resourceId']['videoId']
     link="https://www.youtube.com/watch?v="+vedioid
     ch=await client.fetch_channel(Fetching same channel from which you are checking for the video link)

     async for message in ch.history(limit=1):#looping through the channel to get  the latest message i can do this using last message also but I prefer using channel.history        
     if str(link) != str(message.content):
          ch2=await client.fetch_channel(the channel you want to post video to)
        
          await ch2.send(f'@everyone,**User** just posted a vedio!Go and check it out!\n{link}')
      
          await ch.send(link2)#this is important as after posting the video the link must also be posted to the check channel so that the bot do not send other link
     else:
          pass

So basically what Im doing is using a private channel to post the latest video of the bot as soon as it is ready because if by chance the bot goes offline in between and then comes online it posts the link to that channel then im making as tasks loop in which Im checking every minute that if the latest video link fof that youtube channel is not equal to the video link in my private channel that means the uploader has uploaded a video so post the video in the channel i wish to post.If its equal then do nothing i.e. pass You can use a json file or a database if you are using instead of like I used channel to check for the video.It works fine.

1

Youtube provides a proper webhook api for this so that you do not need to use polling. You can read more about it here.

You will need a webserver to receive their authentication get request and following post requests that are sent whenever a video is published. Furthermore you need to send a get request to their server to subscribe after your webserver is running.

Showing the entire code including how to connect it to a bot would be too much for this answer. You can see the full code, including the bot in this github repo. Alternatively I made a youtube tutorial for this question which you can view here.

Below the code of the webserver and the get request in python:

# Function to start the web server async
async def web_server(self):
    # This is required to use decorators for routes
    routes = web.RouteTableDef()

    # The youtube API sends a get request for the initial authorization
    @routes.get('/')
    async def authenticate(request):
        if 'hub.challenge' not in request.query:
            return web.Response(status=400)
        print("Authenticated")
        challenge_response = request.query.get('hub.challenge')
        return web.Response(text=challenge_response, status=200)

    # The youtube API sends post requests when new videos are posted
    @routes.post('/')
    async def receive(request):
        # Ensure the post is of the proper type
        content_type = request.content_type
        if content_type != 'application/atom+xml':
            return web.Response(status=400)
        
        # Read all the data in the body and convert it to a dict
        body_content = await request.content.read(n=-1)
        data = xmltodict.parse(body_content, 'UTF-8')
        
        # Ensure this is a proper video and its not already been announced before
        if 'entry' in data['feed'] and data['feed']['entry']['yt:videoId'] not in self.memory:
            entry = data['feed']['entry']
            # Add video id to memory to prevent duplicates
            self.memory.add(entry['yt:videoId'])
            # store wanted data in a simple dict
            video_data = {
                'title': entry['title'],
                'video_url': entry['link']['@href'],
                'channel_name': entry['author']['name'],
                'channel_url': entry['author']['uri'],
                'date_published': entry['published'],
                'video_id': entry['yt:videoId']
            }
            # Trigger the new_video event with video data
            self.bot.dispatch('new_video', video_data)
        return web.Response(status=200)

    # Create application and connect the routes
    app = web.Application()
    app.add_routes(routes)

    # Prepare the app runner
    runner = web.AppRunner(app)
    await runner.setup()

    # Prepare the website
    self.site = web.TCPSite(runner, '127.0.0.1', 8080)

    # Wait until the discord bot is fully started
    await self.bot.wait_until_ready()
    # Start the web server
    await self.site.start()
    # Send subscribe request to API
    await self.subscribe()
    
# Function to subscribe to the website 
async def subscribe(self):
    # Start web client to send post request
    async with ClientSession() as session:
        # Grab the ID from the channel url
        channel_id = self.bot.config["target_channel"].split('/')[-1]
        # Prepare the form data required to subscribe
        payload = {
            'hub.callback': self.bot.config['callback_url'],
            'hub.mode': 'subscribe',
            'hub.topic': f'https://www.youtube.com/xml/feeds/videos.xml?channel_id={channel_id}',
            'hub.lease_seconds': '', # Might want to define this, max 828000 seconds
            'hub.secret': '',
            'hub.verify': 'async',
            'hub.verify_token': ''
        }
        # Send post request to the API
        async with session.post('https://pubsubhubbub.appspot.com/subscribe', data=payload) as response:
            # if status is 202 it worked
            if response.status == 202:
                print("Subscribe request sent")
            else:
                print("Failed to subscribe")
Liz
  • 11
  • 1