add documentation

This commit is contained in:
2025-12-10 03:45:39 +01:00
parent 4a33281ca4
commit aa2e450454
4 changed files with 94 additions and 1 deletions

View File

@@ -5,7 +5,14 @@ import logging
class DiscordRPC: class DiscordRPC:
"""
Client for interacting with Discord Rich Presence (RPC).
"""
def __init__(self): def __init__(self):
"""
Initializes the Discord RPC client and connects to Discord via IPC.
"""
self.logger = logging.getLogger('DiscordRPC') self.logger = logging.getLogger('DiscordRPC')
self.logger.info("Connecting to Discord RPC...") self.logger.info("Connecting to Discord RPC...")
@@ -14,6 +21,12 @@ class DiscordRPC:
self.logger.info("Connected to Discord RPC.") self.logger.info("Connected to Discord RPC.")
def update(self, payload: DiscordRPCUpdatePayload): def update(self, payload: DiscordRPCUpdatePayload):
"""
Updates the Discord RPC presence with the provided payload.
Args:
payload (DiscordRPCUpdatePayload): The payload containing presence information.
"""
self.logger.info("Updating Discord RPC presence...") self.logger.info("Updating Discord RPC presence...")
self.rpc.update( self.rpc.update(
activity_type=payload.activity_type, activity_type=payload.activity_type,
@@ -27,6 +40,9 @@ class DiscordRPC:
self.logger.info("Discord RPC presence updated.") self.logger.info("Discord RPC presence updated.")
def clear(self): def clear(self):
"""
Clears the Discord RPC presence.
"""
self.logger.info("Clearing Discord RPC presence...") self.logger.info("Clearing Discord RPC presence...")
self.rpc.clear() self.rpc.clear()
self.logger.info("Discord RPC presence cleared.") self.logger.info("Discord RPC presence cleared.")

View File

@@ -3,13 +3,21 @@ from jellyfin_apiclient_python import JellyfinClient
from getmac import get_mac_address from getmac import get_mac_address
from jellyfin.models import JellyfinMediaItem, JellyfinMediaType, JellyfinMusicMediaMetadata, JellyfinMovieMediaMetadata, JellyfinEpisodeMediaMetadata from jellyfin.models import JellyfinMediaItem, JellyfinMediaType, JellyfinMusicMediaMetadata, JellyfinMovieMediaMetadata, JellyfinEpisodeMediaMetadata
from datetime import datetime from datetime import datetime
from typing import Optional, Tuple
import logging import logging
import time import time
import os import os
class JellyfinApiClient: class JellyfinApiClient:
"""
Client for interacting with the Jellyfin server API.
"""
def __init__(self): def __init__(self):
"""
Initializes the Jellyfin API client and authenticates with the server.
"""
machine_name = os.uname().nodename machine_name = os.uname().nodename
unique_id = get_mac_address(hostname="localhost") unique_id = get_mac_address(hostname="localhost")
@@ -26,6 +34,9 @@ class JellyfinApiClient:
self.logger.info("Connected to Jellyfin server.") self.logger.info("Connected to Jellyfin server.")
def authenticate(self): def authenticate(self):
"""
Authenticates with the Jellyfin server.
"""
self.logger.info("Authenticating with Jellyfin server...") self.logger.info("Authenticating with Jellyfin server...")
self.client.auth.connect_to_address(settings.jellyfin_server_url) self.client.auth.connect_to_address(settings.jellyfin_server_url)
self.client.auth.login( self.client.auth.login(
@@ -37,6 +48,12 @@ class JellyfinApiClient:
self.logger.info("Authenticated with Jellyfin server.") self.logger.info("Authenticated with Jellyfin server.")
def get_current_playback(self) -> JellyfinMediaItem | None: def get_current_playback(self) -> JellyfinMediaItem | None:
"""
Fetches the current playback information from the Jellyfin server.
Returns:
JellyfinMediaItem | None: The current playback media item or None if no active playback is found.
"""
if time.time() - self.last_auth_time > settings.jellyfin_auth_timeout: if time.time() - self.last_auth_time > settings.jellyfin_auth_timeout:
self.authenticate() self.authenticate()
@@ -60,10 +77,26 @@ class JellyfinApiClient:
return None return None
def get_image_url(self, media_id: str) -> str: def get_image_url(self, media_id: str) -> str:
"""
Constructs the image URL for a given media item.
Args:
media_id (str): The ID of the media item.
Returns:
str: The constructed image URL.
"""
server_address = settings.jellyfin_server_url.rstrip('/') server_address = settings.jellyfin_server_url.rstrip('/')
return f"{server_address}/Items/{media_id}/Images/Primary?maxWidth=300&maxHeight=300" return f"{server_address}/Items/{media_id}/Images/Primary?maxWidth=300&maxHeight=300"
def to_model(self, item: dict) -> JellyfinMediaItem: def to_model(self, item: dict) -> JellyfinMediaItem:
"""
Converts a Jellyfin media item dictionary to a JellyfinMediaItem model.
Args:
item (dict): The Jellyfin media item dictionary.
Returns:
JellyfinMediaItem: The converted JellyfinMediaItem model.
"""
media_type = item.get('Type') media_type = item.get('Type')
if media_type == 'Audio': if media_type == 'Audio':
@@ -73,7 +106,16 @@ class JellyfinApiClient:
elif media_type == 'Movie': elif media_type == 'Movie':
return self.to_movie_model(item) return self.to_movie_model(item)
def get_playback_info(self, media: dict) -> tuple[int, int]: def get_playback_info(
self, media: dict) -> Tuple[Optional[int], Optional[int]]:
"""
Extracts playback start and end timestamps from a media item.
Args:
media (dict): The Jellyfin media item dictionary.
Returns:
Tuple[Optional[int], Optional[int]]: A tuple containing the start and end timestamps in seconds, or (None, None) if the media is paused.
"""
play_state = media.get('PlayState') play_state = media.get('PlayState')
is_paused = play_state.get('IsPaused') is_paused = play_state.get('IsPaused')
@@ -92,6 +134,14 @@ class JellyfinApiClient:
return (start, end) return (start, end)
def to_music_model(self, item: dict) -> JellyfinMediaItem: def to_music_model(self, item: dict) -> JellyfinMediaItem:
"""
Converts a Jellyfin music media item dictionary to a JellyfinMediaItem model.
Args:
item (dict): The Jellyfin music media item dictionary.
Returns:
JellyfinMediaItem: The converted JellyfinMediaItem model.
"""
media_id = item.get('Id') media_id = item.get('Id')
parent_id = item.get('ParentId') parent_id = item.get('ParentId')
premiere_date = item.get('PremiereDate') premiere_date = item.get('PremiereDate')
@@ -115,6 +165,14 @@ class JellyfinApiClient:
) )
def to_movie_model(self, item: dict) -> JellyfinMediaItem: def to_movie_model(self, item: dict) -> JellyfinMediaItem:
"""
Converts a Jellyfin movie media item dictionary to a JellyfinMediaItem model.
Args:
item (dict): The Jellyfin movie media item dictionary.
Returns:
JellyfinMediaItem: The converted JellyfinMediaItem model.
"""
media_id = item.get('Id') media_id = item.get('Id')
premiere_date = item.get('PremiereDate') premiere_date = item.get('PremiereDate')
@@ -134,6 +192,14 @@ class JellyfinApiClient:
) )
def to_episode_model(self, item: dict) -> JellyfinMediaItem: def to_episode_model(self, item: dict) -> JellyfinMediaItem:
"""
Converts a Jellyfin episode media item dictionary to a JellyfinMediaItem model.
Args:
item (dict): The Jellyfin episode media item dictionary.
Returns:
JellyfinMediaItem: The converted JellyfinMediaItem model.
"""
media_id = item.get('Id') media_id = item.get('Id')
parent = self.client.jellyfin.get_item(item.get('ParentId')) parent = self.client.jellyfin.get_item(item.get('ParentId'))
parent_id = parent.get('ParentId') parent_id = parent.get('ParentId')

View File

@@ -4,6 +4,14 @@ from pypresence.types import ActivityType
def to_rpc_payload(media_item: JellyfinMediaItem) -> DiscordRPCUpdatePayload: def to_rpc_payload(media_item: JellyfinMediaItem) -> DiscordRPCUpdatePayload:
"""
Converts a JellyfinMediaItem to a DiscordRPCUpdatePayload.
Args:
media_item (JellyfinMediaItem): The Jellyfin media item to convert.
Returns:
DiscordRPCUpdatePayload: The converted Discord RPC update payload.
"""
if media_item.type == JellyfinMediaType.AUDIO: if media_item.type == JellyfinMediaType.AUDIO:
return DiscordRPCUpdatePayload( return DiscordRPCUpdatePayload(
id=media_item.id, id=media_item.id,

View File

@@ -3,6 +3,9 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings): class Settings(BaseSettings):
"""
Application settings loaded from environment variables or a .env file.
"""
jellyfin_server_url: str = Field(..., env="JELLYFIN_SERVER_URL") jellyfin_server_url: str = Field(..., env="JELLYFIN_SERVER_URL")
jellyfin_username: str = Field(..., env="JELLYFIN_USERNAME") jellyfin_username: str = Field(..., env="JELLYFIN_USERNAME")
jellyfin_password: str = Field(..., env="JELLYFIN_PASSWORD") jellyfin_password: str = Field(..., env="JELLYFIN_PASSWORD")