Compare commits

...

2 Commits

Author SHA1 Message Date
59183e7021 add autopep8 formatting 2025-12-10 02:51:46 +01:00
9242b02957 add colored logs 2025-12-10 02:50:17 +01:00
10 changed files with 45 additions and 17 deletions

View File

@@ -2,3 +2,5 @@
- Updated Jellyfin item fetching logic to skip items that are not `Audio`, `Episode`, or `Movie` types, preventing errors when unsupported media types are encountered
- Updated Jellyfin image fetching logic to use `ParentId` for episodes and music tracks to ensure correct artwork is displayed in Discord Rich Presence
- Added `coloredlogs` dependency for improved logging output
- Added a formatting script (`scripts/format.sh`) that uses `autopep8` to automatically format the codebase for better readability and consistency

View File

@@ -1,6 +1,7 @@
from pydantic import BaseModel
from pypresence.types import ActivityType
class DiscordRPCUpdatePayload(BaseModel):
id: str
title: str

View File

@@ -3,6 +3,7 @@ from settings import settings
from discord.models import DiscordRPCUpdatePayload
import logging
class DiscordRPC:
def __init__(self):
self.logger = logging.getLogger('DiscordRPC')

View File

@@ -7,6 +7,7 @@ import logging
import time
import os
class JellyfinApiClient:
def __init__(self):
machine_name = os.uname().nodename
@@ -50,7 +51,8 @@ class JellyfinApiClient:
session_id = session.get('Id')
current_item = self.client.jellyfin.get_now_playing(session_id)
if current_item and current_item.get('Type') in ['Audio', 'Episode', 'Movie']:
if current_item and current_item.get(
'Type') in ['Audio', 'Episode', 'Movie']:
self.logger.info("Current playback information fetched.")
return self.to_model(current_item)
@@ -89,11 +91,11 @@ class JellyfinApiClient:
media_id = item.get('Id')
parent_id = item.get('ParentId')
premiere_date = item.get('PremiereDate')
premiere_year = datetime.fromisoformat(premiere_date).year if premiere_date else None
premiere_year = datetime.fromisoformat(
premiere_date).year if premiere_date else None
(start, end) = self.get_playback_info(item)
return JellyfinMediaItem(
id=media_id,
name=item.get('Name'),
@@ -103,9 +105,8 @@ class JellyfinApiClient:
end=end,
metadata={
'artist': item.get('AlbumArtist'),
'album': f"{item.get('Album')} ({premiere_year})" if premiere_date else item.get('Album')
}
)
'album': f"{
item.get('Album')} ({premiere_year})" if premiere_date else item.get('Album')})
def to_movie_model(self, item: dict) -> JellyfinMediaItem:
media_id = item.get('Id')
@@ -133,7 +134,7 @@ class JellyfinApiClient:
season_number = item.get('ParentIndexNumber')
episode_number = item.get('IndexNumber')
subtitle=f"S{season_number:02}E{episode_number:02} of {series_name}"
subtitle = f"S{season_number:02}E{episode_number:02} of {series_name}"
(start, end) = self.get_playback_info(item)

View File

@@ -1,11 +1,13 @@
from pydantic import BaseModel
from enum import Enum
class JellyfinMediaType(str, Enum):
AUDIO = 'Audio'
MOVIE = 'Movie'
EPISODE = 'Episode'
class JellyfinMediaItem(BaseModel):
id: str
name: str

View File

@@ -2,18 +2,24 @@ from discord.models import DiscordRPCUpdatePayload
from jellyfin.models import JellyfinMediaItem, JellyfinMediaType
from pypresence.types import ActivityType
def to_rpc_payload(media_item: JellyfinMediaItem) -> DiscordRPCUpdatePayload:
if media_item.type == JellyfinMediaType.AUDIO:
return DiscordRPCUpdatePayload(
id=media_item.id,
title=f"Listening to {media_item.name}",
subtitle=f"by {media_item.metadata.get('artist', 'Unknown Artist')}",
title=f"Listening to {
media_item.name}",
subtitle=f"by {
media_item.metadata.get(
'artist',
'Unknown Artist')}",
image_url=media_item.image_url,
details=media_item.metadata.get('album', 'Unknown Album'),
details=media_item.metadata.get(
'album',
'Unknown Album'),
activity_type=ActivityType.LISTENING,
start=media_item.start,
end=media_item.end
)
end=media_item.end)
elif media_item.type == JellyfinMediaType.MOVIE:
return DiscordRPCUpdatePayload(
id=media_item.id,

View File

@@ -2,16 +2,18 @@ from discord.rpc import DiscordRPC
from jellyfin.api_client import JellyfinApiClient
from jellyfin.utils import to_rpc_payload
from settings import settings
import coloredlogs
import logging
import time
logging.basicConfig(
coloredlogs.install(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
discordRPC = DiscordRPC()
jellyfinApiClient = JellyfinApiClient()
def main():
while True:
try:
@@ -28,5 +30,6 @@ def main():
discordRPC.clear()
break
if __name__ == "__main__":
main()

View File

@@ -3,3 +3,5 @@ getmac==0.9.5
pypresence==4.6.1
pydantic==2.12.5
pydantic-settings==2.12.0
coloredlogs==15.0.1
autopep8==2.3.2

6
scripts/format.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
which autopep8 &> /dev/null || { echo "autopep8 not found, please install it."; exit 1; }
autopep8 --in-place --aggressive --aggressive --recursive --exclude bin,lib,include,venv,.git,__pycache__ .
echo "Code formatted with autopep8."

View File

@@ -1,16 +1,20 @@
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
jellyfin_server_url: str = Field(..., env="JELLYFIN_SERVER_URL")
jellyfin_username: str = Field(..., env="JELLYFIN_USERNAME")
jellyfin_password: str = Field(..., env="JELLYFIN_PASSWORD")
jellyfin_auth_timeout: int = Field(10 * 60, env="JELLYFIN_AUTH_TIMEOUT") # default 10 minutes
jellyfin_auth_timeout: int = Field(
10 * 60, env="JELLYFIN_AUTH_TIMEOUT") # default 10 minutes
discord_app_id: str = Field(..., env="DISCORD_APP_ID")
poll_interval: int = Field(15, env="POLL_INTERVAL") # default 15 seconds
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8")
settings = Settings()