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 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 - 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 pydantic import BaseModel
from pypresence.types import ActivityType from pypresence.types import ActivityType
class DiscordRPCUpdatePayload(BaseModel): class DiscordRPCUpdatePayload(BaseModel):
id: str id: str
title: str title: str

View File

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

View File

@@ -7,6 +7,7 @@ import logging
import time import time
import os import os
class JellyfinApiClient: class JellyfinApiClient:
def __init__(self): def __init__(self):
machine_name = os.uname().nodename machine_name = os.uname().nodename
@@ -50,7 +51,8 @@ class JellyfinApiClient:
session_id = session.get('Id') session_id = session.get('Id')
current_item = self.client.jellyfin.get_now_playing(session_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.") self.logger.info("Current playback information fetched.")
return self.to_model(current_item) return self.to_model(current_item)
@@ -89,11 +91,11 @@ class JellyfinApiClient:
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')
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) (start, end) = self.get_playback_info(item)
return JellyfinMediaItem( return JellyfinMediaItem(
id=media_id, id=media_id,
name=item.get('Name'), name=item.get('Name'),
@@ -103,9 +105,8 @@ class JellyfinApiClient:
end=end, end=end,
metadata={ metadata={
'artist': item.get('AlbumArtist'), '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: def to_movie_model(self, item: dict) -> JellyfinMediaItem:
media_id = item.get('Id') media_id = item.get('Id')

View File

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

View File

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

View File

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

View File

@@ -3,3 +3,5 @@ getmac==0.9.5
pypresence==4.6.1 pypresence==4.6.1
pydantic==2.12.5 pydantic==2.12.5
pydantic-settings==2.12.0 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 import Field
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings): class Settings(BaseSettings):
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")
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") discord_app_id: str = Field(..., env="DISCORD_APP_ID")
poll_interval: int = Field(15, env="POLL_INTERVAL") # default 15 seconds 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() settings = Settings()