add jellyfin api client
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from pypresence import Presence
|
||||
from settings import settings
|
||||
from discord.models import DiscordRPCPayload
|
||||
from discord.models import DiscordRPCUpdatePayload
|
||||
import logging
|
||||
|
||||
class DiscordRPC:
|
||||
@@ -12,7 +12,7 @@ class DiscordRPC:
|
||||
self.rpc.connect()
|
||||
self.logger.info("Connected to Discord RPC.")
|
||||
|
||||
def update(self, payload: DiscordRPCPayload):
|
||||
def update(self, payload: DiscordRPCUpdatePayload):
|
||||
self.logger.info("Updating Discord RPC presence...")
|
||||
self.rpc.update(
|
||||
activity_type=payload.activity_type,
|
||||
|
||||
45
jellyfin.py
45
jellyfin.py
@@ -1,45 +0,0 @@
|
||||
from jellyfin_apiclient_python import JellyfinClient
|
||||
from getmac import get_mac_address
|
||||
import os
|
||||
|
||||
def get_client():
|
||||
server_address = os.getenv('JELLYFIN_ADDRESS')
|
||||
username = os.getenv('JELLYFIN_USER')
|
||||
password = os.getenv('JELLYFIN_PASSWORD')
|
||||
host_mac = get_mac_address(hostname="localhost")
|
||||
|
||||
client = JellyfinClient()
|
||||
client.config.app('jellydisc', '0.0.1', os.uname().nodename, host_mac)
|
||||
client.config.data['auth.ssl'] = True
|
||||
|
||||
client.auth.connect_to_address(server_address)
|
||||
client.auth.login(server_address, username, password)
|
||||
|
||||
return client
|
||||
|
||||
def get_active_media(client):
|
||||
server_address = os.getenv('JELLYFIN_ADDRESS')
|
||||
sessions = client.jellyfin.get_sessions()
|
||||
|
||||
for session in sessions:
|
||||
# Check if there is a NowPlayingItem
|
||||
media = session.get('NowPlayingItem')
|
||||
if not media:
|
||||
continue
|
||||
|
||||
# Skip non-audio media
|
||||
media_type = media.get('Type')
|
||||
if media_type != 'Audio':
|
||||
continue
|
||||
|
||||
media_id = media.get('Id')
|
||||
image = f"{server_address}/Items/{media_id}/Images/Primary?maxWidth=300&maxHeight=300"
|
||||
|
||||
return {
|
||||
'id': media_id,
|
||||
'artist': media.get('AlbumArtist', 'Unknown Artist'),
|
||||
'title': media.get('Name', 'Unknown Title'),
|
||||
'image': image,
|
||||
}
|
||||
|
||||
return None
|
||||
95
jellyfin/api_client.py
Normal file
95
jellyfin/api_client.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from settings import settings
|
||||
from jellyfin_apiclient_python import JellyfinClient
|
||||
from getmac import get_mac_address
|
||||
from jellyfin.models import JellyfinMediaItem, JellyfinMediaType
|
||||
import logging
|
||||
import os
|
||||
|
||||
class JellyfinApiClient:
|
||||
def __init__(self):
|
||||
machine_name = os.uname().nodename
|
||||
unique_id = get_mac_address(hostname="localhost")
|
||||
|
||||
self.logger = logging.getLogger('JellyfinApiClient')
|
||||
|
||||
self.logger.info("Connecting to Jellyfin server...")
|
||||
self.client = JellyfinClient()
|
||||
self.client.config.app('jellydisc', '0.0.1', machine_name, unique_id)
|
||||
self.client.config.data['auth.ssl'] = True
|
||||
|
||||
self.client.auth.connect_to_address(settings.jellyfin_server_url)
|
||||
self.client.auth.login(
|
||||
settings.jellyfin_server_url,
|
||||
settings.jellyfin_username,
|
||||
settings.jellyfin_password
|
||||
)
|
||||
|
||||
self.logger.info("Connected to Jellyfin server.")
|
||||
|
||||
def get_current_playback(self) -> JellyfinMediaItem | None:
|
||||
self.logger.info("Fetching current playback information...")
|
||||
sessions = self.client.jellyfin.get_sessions()
|
||||
|
||||
if not sessions:
|
||||
self.logger.info("No active playback found.")
|
||||
return None
|
||||
|
||||
for session in sessions:
|
||||
if session.get('NowPlayingItem'):
|
||||
self.logger.info("Current playback information fetched.")
|
||||
return self.to_model(session['NowPlayingItem'])
|
||||
|
||||
self.logger.info("No active playback found.")
|
||||
return None
|
||||
|
||||
def get_image_url(self, media_id: str) -> str:
|
||||
server_address = settings.jellyfin_server_url.rstrip('/')
|
||||
return f"{server_address}/Items/{media_id}/Images/Primary?maxWidth=300&maxHeight=300"
|
||||
|
||||
def to_model(self, item: dict) -> JellyfinMediaItem:
|
||||
media_type = item.get('Type')
|
||||
|
||||
if media_type == 'Audio':
|
||||
return self.to_music_model(item)
|
||||
elif media_type == 'Episode':
|
||||
return self.to_episode_model(item)
|
||||
elif media_type == 'Movie':
|
||||
return self.to_movie_model(item)
|
||||
|
||||
raise ValueError(f"Unsupported media type: {media_type}")
|
||||
|
||||
def to_music_model(self, item: dict) -> JellyfinMediaItem:
|
||||
media_id = item.get('Id')
|
||||
|
||||
return JellyfinMediaItem(
|
||||
name=item.get('Name'),
|
||||
type=JellyfinMediaType.AUDIO,
|
||||
image_url=self.get_image_url(media_id),
|
||||
metadata={
|
||||
'artist': item.get('AlbumArtist'),
|
||||
}
|
||||
)
|
||||
|
||||
def to_movie_model(self, item: dict) -> JellyfinMediaItem:
|
||||
media_id = item.get('Id')
|
||||
|
||||
return JellyfinMediaItem(
|
||||
name=item.get('Name'),
|
||||
type=JellyfinMediaType.MOVIE,
|
||||
image_url=self.get_image_url(media_id),
|
||||
metadata={}
|
||||
)
|
||||
|
||||
def to_episode_model(self, item: dict) -> JellyfinMediaItem:
|
||||
media_id = item.get('Id')
|
||||
|
||||
return JellyfinMediaItem(
|
||||
name=item.get('Name'),
|
||||
type=JellyfinMediaType.EPISODE,
|
||||
image_url=self.get_image_url(media_id),
|
||||
metadata={
|
||||
'series': item.get('SeriesName'),
|
||||
'season': item.get('ParentIndexNumber'),
|
||||
'episode': item.get('IndexNumber'),
|
||||
}
|
||||
)
|
||||
13
jellyfin/models.py
Normal file
13
jellyfin/models.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from pydantic import BaseModel
|
||||
from enum import Enum
|
||||
|
||||
class JellyfinMediaType(str, Enum):
|
||||
AUDIO = 'Audio'
|
||||
MOVIE = 'Movie'
|
||||
EPISODE = 'Episode'
|
||||
|
||||
class JellyfinMediaItem(BaseModel):
|
||||
name: str
|
||||
type: JellyfinMediaType
|
||||
image_url: str
|
||||
metadata: dict
|
||||
52
main.py
52
main.py
@@ -1,46 +1,12 @@
|
||||
from dotenv import load_dotenv
|
||||
from jellyfin import get_client, get_active_media
|
||||
from discord import get_rpc
|
||||
from pypresence.types import ActivityType
|
||||
import time
|
||||
from discord.rpc import DiscordRPC
|
||||
from jellyfin.api_client import JellyfinApiClient
|
||||
import logging
|
||||
|
||||
load_dotenv()
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
rpc = get_rpc()
|
||||
client = get_client()
|
||||
discordRPC = DiscordRPC()
|
||||
jellyfinApiClient = JellyfinApiClient()
|
||||
|
||||
def generate_media_id(media):
|
||||
return f"{media['artist']}-{media['title']}"
|
||||
|
||||
def main_loop(last_media_id):
|
||||
media = get_active_media(client)
|
||||
|
||||
if media is None:
|
||||
print("No active media found.")
|
||||
rpc.clear()
|
||||
return None
|
||||
|
||||
media_id = generate_media_id(media)
|
||||
|
||||
if media_id != last_media_id:
|
||||
print(f"Updating Discord RPC: Listening to {media['title']} by {media['artist']}")
|
||||
|
||||
rpc.update(
|
||||
activity_type=ActivityType.LISTENING,
|
||||
state=f"by {media['artist']}",
|
||||
details=f"Listening to {media['title']}",
|
||||
large_image=media['image'],
|
||||
large_text=media['title'],
|
||||
)
|
||||
return media_id
|
||||
else:
|
||||
print("No change in media. Skipping update.")
|
||||
return last_media_id
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
last_media_id = None
|
||||
print("Starting Jellyfin Discord Rich Presence...")
|
||||
while True:
|
||||
last_media_id = main_loop(last_media_id)
|
||||
time.sleep(15)
|
||||
print(jellyfinApiClient.get_current_playback())
|
||||
|
||||
Reference in New Issue
Block a user