Jump to content

Display Episode Series Poster instead of episode main image?


Tolerant

Recommended Posts

Tolerant

import os
import discord
from discord.ext import tasks, commands
import requests
from io import BytesIO

# Discord bot token and Emby server details
discord_bot_token = os.getenv('NOWPLAYING_DISCORD_BOT_TOKEN')
emby_server_ip = '192.168.4.101'
emby_server_port = '8096'
api_key = os.getenv('EMBY_API_BOT_KEY')
channel_id = 1234567890  # The channel ID where the bot will post

# Initialize Discord bot with intents
intents = discord.Intents.default()
intents.messages = True
intents.guilds = True
intents.members = True
intents.message_content = True          # Necessary for reading message content
intents.reactions = True                # If you want to track reactions
intents.emojis_and_stickers = False     # If you want to track emojis and stickers
intents.integrations = True             # If you want to track integrations
intents.webhooks = True                 # If you want to track webhooks
intents.invites = False                 # If you want to track invites
intents.voice_states = False            # If you want to track voice state changes
intents.presences = True                # If you want to track user presences (e.g., online status)
intents.typing = False                  # Typically not needed, can be spammy

bot = commands.Bot(command_prefix='/', intents=intents)

# Variables to keep track of the last item played
last_item_id = None

@bot.event
async def on_ready():
    global last_item_id
    print(f'{bot.user.name} has connected to Discord!')
    channel = bot.get_channel(channel_id)
    existing_threads = channel.threads
    thread = discord.utils.get(existing_threads, name="Now Playing Updates")
    if thread is None:
        thread = await channel.create_thread(name="Now Playing Updates", type=discord.ChannelType.private_thread)

    # Fetch the last message in the thread
    messages = await thread.history(limit=1).flatten()
    bot.last_now_playing_message = messages[0] if messages else None
    bot.now_playing_thread = thread
    now_playing_check.start()
    
    
@tasks.loop(seconds=30)
async def now_playing_check():
    global last_item_id
    url = f'http://{emby_server_ip}:{emby_server_port}/Sessions'
    headers = {'X-Emby-Token': api_key}
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        now_playing_data = response.json()
        
        for session in now_playing_data:
            if 'NowPlayingItem' in session:
                item = session['NowPlayingItem']
                item_id = item.get('Id')
                media_type = item.get('Type')
                title = item.get('Name')
                
                if item_id and item_id != last_item_id:
                    last_item_id = item_id
                    
                    if media_type == 'Episode':
                        embed, file = await handle_episode(bot, item, emby_server_ip, emby_server_port, api_key)
                    elif media_type == 'Movie':
                        embed, file = await handle_movie(bot, item, emby_server_ip, emby_server_port, api_key, item_id)
                    else:
                        embed, file = None, None
                    
                    if embed and file:
                        # Delete the previous message if it exists
                        if bot.last_now_playing_message:
                            await bot.last_now_playing_message.delete()

                        # Send a new message
                        bot.last_now_playing_message = await bot.now_playing_thread.send(embed=embed, file=file)
                break
    else:
        if response.status_code == 404:
            print("Now Playing information not found: HTTP 404 - The requested data was not found.")
        else:
            print(f"Failed to retrieve 'Now Playing' information from Emby. Status Code: {response.status_code}")


async def handle_episode(bot, item, emby_server_ip, emby_server_port, api_key):
    series_title = item.get('SeriesName', 'Unknown Series')
    title = item.get('Name')
    episode_number = item.get('IndexNumber', 'Unknown Episode')
    season_number = item.get('ParentIndexNumber', None)  # Retrieve the season number if available
    item_id = item.get('Id')  # Retrieve the item_id

    image_url = f'http://{emby_server_ip}:{emby_server_port}/emby/Items/{item_id}/Images/Primary?api_key={api_key}'
    image_response = requests.get(image_url)

    if image_response.status_code == 200:
        image_data = BytesIO(image_response.content)
        image_data.seek(0)
        file = discord.File(fp=image_data, filename='now_playing_image.jpg')

        embed_title = f"{series_title}\n(S{season_number:02d}E{episode_number:02d}) {title}"

        embed = discord.Embed(title=embed_title, color=discord.Color.blue())

        # Fetch and include episode overview if available
        overview = item.get('Overview', 'No overview available')
        embed.add_field(name="Overview", value=overview, inline=False)

        embed.set_image(url="attachment://now_playing_image.jpg")

        if bot.last_now_playing_message:
            await bot.last_now_playing_message.edit(embed=embed, file=file)
        else:
            bot.last_now_playing_message = await bot.now_playing_thread.send(embed=embed, file=file)
    else:
        if image_response.status_code == 404:
            print("Image not found: HTTP 404 - The requested image was not found.")
        else:
            print(f"Failed to download image from {image_url}. Status Code: {image_response.status_code}")

    return None, None


async def handle_movie(bot, item, emby_server_ip, emby_server_port, api_key, item_id):
    title = item.get('Name')
    year = item.get('ProductionYear', 'Unknown Year')
    size_bytes = item.get('Size', 0)
    size_gb = round(size_bytes / (1024 ** 3), 2)
    genres = ', '.join(item.get('Genres', ['Unknown Genre']))

    overview = item.get('Overview', 'No overview available')

    # Construct the image URL with the item_id
    image_url = f'http://{emby_server_ip}:{emby_server_port}/emby/Items/{item_id}/Images/Primary?api_key={api_key}'
    image_response = requests.get(image_url)

    if image_response.status_code == 200:
        image_data = BytesIO(image_response.content)
        image_data.seek(0)
        file = discord.File(fp=image_data, filename='now_playing_image.jpg')

        embed = discord.Embed(title=f"{title} ({year})", description=f"**Size:** {size_gb} GB\n**Genres:** {genres}\n**Overview:** {overview}")
        embed.set_image(url="attachment://now_playing_image.jpg")

        return embed, file
    else:
        if image_response.status_code == 404:
            print("Image not found: HTTP 404 - The requested image was not found.")
        else:
            print(f"Failed to download image from {image_url}. Status Code: {image_response.status_code}")

    return None, None

bot.run(discord_bot_token)

Edited by Tolerant
Script updated
Link to comment
Share on other sites

Tolerant

What is the call I need to get back all the associated details about what shows up with nowplaying? I can't get GPT to figure it out cause there is no easy source of examples to draw from and just endless pages of API docs, too much to have it make sense of without telling me to give it what I want it to find. Ugg! I want to be able to see what's playing and show the image and also only show it for specific libraries. Help.

Link to comment
Share on other sites

Tolerant

Yes I have tried in vain to make sense of it but it just does not compute.. I just need to know if I get a file id from now playing.. which I can do.. how to send that back to get the rest of the info for that file? For example.. I can get this from the "response" and the tags returned and the basics I can figure out.
BUT.. I cannot for the life of me query with a file id and get back more associated data that I want. Or figure out how to give enough of it to GPT for it to figure it out.
I do now have it mostly working except I can't really go beyond what I have shown and I would rather see the actual Series poster not a episode main image. :(


image.png.d8127a23e4954b84a95ed7637a4d630c.png
 

 

image.png

Edited by Tolerant
Link to comment
Share on other sites

Tolerant

Since I can't seem to find any help to do it right I was able to work around it and use the path to the item to locally get the folder.jpg for the item, not the way I wanted to do it but it works, I guess.

I would still love to know how to do this correctly.

Link to comment
Share on other sites

adminExitium

If the Type is Episode in the returned response, then use the SeriesId field and if it's Movie, use the Id field. The API path for fetching the primary image is /emby/Items/<ID>/Images/Primary.

  • Thanks 1
Link to comment
Share on other sites

Tolerant

Atm GPT is struggling with audio

Requesting audio information from: http://192.168.4.101:8096/Items/19192

I really wish you guys just had something explaining this instead of just a list of parameters in the API cause it is really useless if someone does not understand how to put them together, and you don't show that.
 

Link to comment
Share on other sites

Have you taken a look at the built-in swagger docs?

Link to comment
Share on other sites

Tolerant

ROFL.. Yes?! A bunch of non connected stuff.. that just gives you slash stuff.. and no examples anywhere.. that make any freaking sense.

I have even figured out the local API call to see the server info etc.. but the docs are such a mess Even GPT cannot make heads or tails of this stuff and I am ready to scream.

Can you PLEASE JUST GIVE ME SOME EXAMPLES OF WORKING CALLS I CAN MODIFY?!?!? FOR EXAMPLE IF I AM LISTENING TO A SONG .. AND WANT TO SEE THE DETAILS IN DISCORD?!?!

OR .. MOVIE.. or MUSICVIDEO.. or EPISODE..  See where i am going with this?!?! IT's freaking impossible and all you seem to tell people is you have API docs.. but no one ever seems to help and give an example of how to use them or figure them out?!?!

Link to comment
Share on other sites

45 minutes ago, Tolerant said:

image.thumb.png.77093e01ea65fa31bb2933a92907f833.pngLoving it.

Try using swagger when you have the web app running on either localhost or https.

Link to comment
Share on other sites

Tolerant
1 hour ago, Luke said:

Try using swagger when you have the web app running on either localhost or https.

Can you please just answer some basic questions to get me started?!? SOME examples would REALLY be nice.

Link to comment
Share on other sites

Tolerant

I HAVE  been to the WEB Docs, I have seen the few examples, the "CLIENT" page which links to a setup.py that does NOT exist.. and someones project I found on github for a script to put stuff  into CSV and some other py project that does not work..

I just want to post whats playing with the right info to discord via a bot.. that seems like SUCH an easy thing, but I cannot find ANY working examples on the entire internet.

I paid for lifetime so I was hoping MAYBE you would share a few tips to help but all you keep saying is "read docs" and I have told you I don't understand them. Maybe it's cause I am disabled or old or dumb, I don't know but I don't get it and i can't figure this out without help so I am here ASKING FOR HELP.

Link to comment
Share on other sites

adminExitium
7 hours ago, Tolerant said:

http://192.168.4.101:8096/Items/19192

Try http://192.168.4.101:8096/Items?Ids=19192 instead. This should give you most of the necessary info but if you still need more you need to fetch it in the context of a user like (after authenticating as the same user): http://192.168.4.101:8096/Users/<UserId>/Items/19192

Link to comment
Share on other sites

Tolerant

This is by NO means 100% or how I would choose to do it exactly, BUT until someone takes an interest other than me it works for me.
There are a couple things that would need to be changed such as the local network path to music images for example, and setting the OS ENV for keys etc.
BUT anyone is welcome to improve or use it if they desire. I hope someone finds it at least helpful from an example standpoint of how to get started.

Discord NOW Playing BOT, posting to a thread in a "newmovies" channel. Adjust or edit as needed. Enjoy.

NOWPLAYING Discord Bot

NOWPLAYING is a Discord bot designed to display real-time now playing information from an Emby server in a designated Discord channel. It allows users to see the details of the media currently being played on the Emby server, enhancing the community's media sharing and viewing experience.

Features

  • Real-time updates of media playback from Emby server.
  • Displays detailed information including titles, artists, albums, and images.
  • Supports various media types including movies, episodes, audio, and music videos.
  • Configurable to monitor specific user activities and ignore others.
  • Automated messages for "Nothing Playing" scenarios.

https://github.com/Tolerable/NOWPLAYING

 

Edited by Tolerant
  • Thanks 1
Link to comment
Share on other sites

Tolerant

Still needs more work, but It seems good enough for Episodes, Movies, Music and MusicVideos. I have not made sure it will never have an error or work with every single condition as I only put a couple days into telling GPT to keep fixing errors and change things, but eventually I got this far. Anyone is encouraged to branch it or suggest edits etc. :)

  • Thanks 1
Link to comment
Share on other sites

Tolerant

Ok.. NOW it updates the BOT's STATUS message to show the now playing item, such as Artist - Title (Year), and the Images for Audio And Music Videos are also served along with their info. So now it handles Episodes, Movies, Music, MusicVideos, so far. 

Not perfect, expect some bugs, but.. it's something! :)
 

  • Thanks 1
Link to comment
Share on other sites

Tolerant

Made some updates, it is still a bit rough but closer to what I had envisioned now.

image.png.1a4ad3178ec3702e608b32e5142db9d4.png

image.png.7eb3453e94accbc851f3e8797071b624.png

image.png

image.png.98e618d35a8eaed8cb722454eaab7952.png

Edited by Tolerant
  • Thanks 1
Link to comment
Share on other sites

Tolerant

Darn it hate not being able to edit posts here later, lol. 
Still a work in progress.

Obviously the look is set by the selections in Emby itself and there is no other good square selection for audio in this plugin, sadly.
But I did manage to improve the layout some more and added the year as well.
The script is not exactly plug and play yet, but getting closer bit by bit.

image.png.226e7f004c58fa24f83362d4d36f9b09.png

Link to comment
Share on other sites

bakes82

Does this seriously poll the sessions every 30 seconds and then deletes messages from discord and re-adds them lol?  Why not use a webhook for the events and then get the “minor” details you’re missing..  I also thought there was already a discord plugin that did this also.

Link to comment
Share on other sites

Tolerant

It does keep checking yes. And there may be another way out there, I just know I wanted a way to do it and did not find enough information.


No one has to use it! It's for my own fun and learning, but I felt I should share because Emby gives me so much back.

Maybe I will find a better way to do it, but.. it works and Discord does not charge by the message or tokens, yet. ;)

Btw, I have no idea how to code. GPT writes my scripts and I just keep working at it until we get it right. So I am sure this is a horrible script.

I would suggest someone really smart rework it and branch the project with a MUCH better version. WINK WINK! HAHA!

Edited by Tolerant
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...