Skip to content

Commit

Permalink
Add event filter for notifications (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
RemiRigal authored May 6, 2022
1 parent a441355 commit 02406ab
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 61 deletions.
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,13 @@ plexautolanguages:
# A notification is sent whenever a language change is performed
enable: true
# An array of Apprise configurations, see Apprise docs for more information: https://github.com/caronc/apprise
# The array 'users' can be specified in order to link notification URLs with specific users
# Defaults to all users if not present
# The array 'events' can be specified in order to get notifications only for specific events
# Valid event values: "play_or_activity" "new_episode" "updated_episode" "scheduler"
# Defaults to all events if not present
apprise_configs:
# This URL will be notified of all changes
# This URL will be notified of all changes during all events
- "discord://webhook_id/webhook_token"
# These URLs will only be notified of language change for users "MyUser1" and "MyUser2"
- urls:
Expand All @@ -146,9 +151,18 @@ plexautolanguages:
users:
- "MyUser1"
- "MyUser2"
# This URL will only be notified of language change for user "MyUser3"
- urls: "tgram://bottoken/ChatID"
users: "MyUser3"
# This URL will only be notified of language change for user "MyUser3" during play or activity events
- urls:
- "tgram://bottoken/ChatID"
users:
- "MyUser3"
events:
- "play_or_activity"
# This URL will be notified of language change during scheduler tasks only
- urls:
- "gotify://hostname/token"
events:
- "scheduler"
- "..."

# Whether or not to enable the debug mode, defaults to 'false'
Expand Down
3 changes: 2 additions & 1 deletion plex_auto_languages/alerts/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from plex_auto_languages.alerts.base import PlexAlert
from plex_auto_languages.utils.logger import get_logger
from plex_auto_languages.constants import EventType

if TYPE_CHECKING:
from plex_auto_languages.plex_server import PlexServer
Expand Down Expand Up @@ -70,4 +71,4 @@ def process(self, plex: PlexServer):
if user is None:
return
logger.debug(f"[Activity] User: {user.name} | Episode: {item}")
plex.change_default_tracks_if_needed(user.name, item)
plex.change_tracks(user.name, item, EventType.PLAY_OR_ACTIVITY)
3 changes: 2 additions & 1 deletion plex_auto_languages/alerts/playing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from plex_auto_languages.alerts.base import PlexAlert
from plex_auto_languages.utils.logger import get_logger
from plex_auto_languages.constants import EventType

if TYPE_CHECKING:
from plex_auto_languages.plex_server import PlexServer
Expand Down Expand Up @@ -73,4 +74,4 @@ def process(self, plex: PlexServer):
plex.cache.default_streams.setdefault(item.key, pair_id)

# Change tracks if needed
plex.change_default_tracks_if_needed(username, item)
plex.change_tracks(username, item, EventType.PLAY_OR_ACTIVITY)
5 changes: 3 additions & 2 deletions plex_auto_languages/alerts/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from plex_auto_languages.alerts.base import PlexAlert
from plex_auto_languages.utils.logger import get_logger
from plex_auto_languages.constants import EventType

if TYPE_CHECKING:
from plex_auto_languages.plex_server import PlexServer
Expand Down Expand Up @@ -37,12 +38,12 @@ def process(self, plex: PlexServer):

# Change tracks for all users
logger.info(f"[Status] Processing newly added episode {plex.get_episode_short_name(item)}")
plex.process_new_or_updated_episode(item.key)
plex.process_new_or_updated_episode(item.key, EventType.NEW_EPISODE)

# Process updated episodes
if len(updated) > 0:
logger.debug(f"[Status] Found {len(updated)} updated episode(s)")
for item in updated:
# Change tracks for all users
logger.info(f"[Status] Processing updated episode {plex.get_episode_short_name(item)}")
plex.process_new_or_updated_episode(item.key, new=False)
plex.process_new_or_updated_episode(item.key, EventType.UPDATED_EPISODE)
3 changes: 2 additions & 1 deletion plex_auto_languages/alerts/timeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from plex_auto_languages.alerts.base import PlexAlert
from plex_auto_languages.utils.logger import get_logger
from plex_auto_languages.constants import EventType

if TYPE_CHECKING:
from plex_auto_languages.plex_server import PlexServer
Expand Down Expand Up @@ -60,4 +61,4 @@ def process(self, plex: PlexServer):

# Change tracks for all users
logger.info(f"[Timeline] Processing newly added episode {plex.get_episode_short_name(item)}")
plex.process_new_or_updated_episode(self.item_id)
plex.process_new_or_updated_episode(self.item_id, EventType.NEW_EPISODE)
9 changes: 9 additions & 0 deletions plex_auto_languages/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from enum import Enum


class EventType(Enum):

PLAY_OR_ACTIVITY = 0
NEW_EPISODE = 1
UPDATED_EPISODE = 2
SCHEDULER = 3
60 changes: 24 additions & 36 deletions plex_auto_languages/plex_server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys
import itertools
from typing import List, Union
from typing import Union
from datetime import datetime, timedelta
from plexapi.media import MediaPart
from plexapi.library import ShowSection
Expand All @@ -11,9 +11,10 @@
from plex_auto_languages.utils.logger import get_logger
from plex_auto_languages.utils.configuration import Configuration
from plex_auto_languages.plex_alert_handler import PlexAlertHandler
from plex_auto_languages.track_changes import TrackChanges
from plex_auto_languages.track_changes import TrackChanges, NewOrUpdatedTrackChanges
from plex_auto_languages.utils.notifier import Notifier
from plex_auto_languages.plex_server_cache import PlexServerCache
from plex_auto_languages.constants import EventType


logger = get_logger()
Expand Down Expand Up @@ -136,7 +137,8 @@ def get_user_by_id(self, user_id: Union[int, str]):
return None
return matching_users[0]

def process_new_or_updated_episode(self, item_id: Union[int, str], new: bool = True):
def process_new_or_updated_episode(self, item_id: Union[int, str], event_type: EventType):
track_changes = NewOrUpdatedTrackChanges(event_type)
for user_id in self.get_all_user_ids():
# Switch to the user's Plex instance
user_plex = self.get_plex_instance_of_user(user_id)
Expand All @@ -157,58 +159,44 @@ def process_new_or_updated_episode(self, item_id: Union[int, str], new: bool = T
user = self.get_user_by_id(user_id)
if user is None:
return
self.change_default_tracks_if_needed(user.name, reference, episodes=[user_item], notify=False)
self.notify_updated_or_new_episode(self.fetch_item(item_id), new)

def change_default_tracks_if_needed(self, username: str, episode: Episode, episodes: List[Episode] = None,
notify: bool = True):
track_changes = TrackChanges(username, episode)
logger.debug(f"[Language Update] "
f"Checking language update for show {episode.show()} and user '{username}' based on episode {episode}")
if episodes is None:
# Get episodes to update
episodes = track_changes.get_episodes_to_update(
self.config.get("update_level"), self.config.get("update_strategy"))
track_changes.change_track_for_user(user.name, reference, user_item)

# Notify changes
if track_changes.has_changes:
self.notify_changes(track_changes)

def change_tracks(self, username: str, episode: Episode, event_type: EventType):
track_changes = TrackChanges(username, episode, event_type)
# Get episodes to update
episodes = track_changes.get_episodes_to_update(self.config.get("update_level"), self.config.get("update_strategy"))

# Get changes to perform
track_changes.compute(episodes)
if not track_changes.has_changes:
logger.debug(f"[Language Update] No changes to perform for show {episode.show()} and user '{username}'")
return False

# Perform changes
track_changes.apply()

# Notify changes
if notify:
if track_changes.has_changes:
self.notify_changes(track_changes)
return True

def notify_changes(self, track_changes: TrackChanges):
def notify_changes(self, track_changes: Union[TrackChanges, NewOrUpdatedTrackChanges]):
logger.info(f"Language update: {track_changes.inline_description}")
if self.notifier is None:
return
title = f"PlexAutoLanguages - {track_changes.reference_name}"
self.notifier.notify_user(title, track_changes.description, track_changes.username)

def notify_updated_or_new_episode(self, episode: Episode, new: bool):
title = f"PlexAutoLanguages - {'New' if new else 'Updated'} episode"
message = (
f"Episode: {self.get_episode_short_name(episode)}\n"
f"Updated language for all users"
)
inline_message = message.replace("\n", " | ")
logger.info(f"Language update for new episode: {inline_message}")
if self.notifier is None:
return
self.notifier.notify(title, message)
title = f"PlexAutoLanguages - {track_changes.title}"
if isinstance(track_changes, TrackChanges):
self.notifier.notify_user(title, track_changes.description, track_changes.username, track_changes.event_type)
else:
self.notifier.notify(title, track_changes.description, track_changes.event_type)

def start_deep_analysis(self):
# History
min_date = datetime.now() - timedelta(days=1)
history = self._plex.history(mindate=min_date)
for episode in [media for media in history if isinstance(media, Episode)]:
user = self.get_user_by_id(episode.accountID)
if user is None:
continue
episode.reload()
self.change_default_tracks_if_needed(user.name, episode)
self.change_tracks(user.name, episode, EventType.SCHEDULER)
86 changes: 84 additions & 2 deletions plex_auto_languages/track_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,31 @@
from plexapi.media import AudioStream, SubtitleStream, MediaPart

from plex_auto_languages.utils.logger import get_logger
from plex_auto_languages.constants import EventType


logger = get_logger()


class TrackChanges():

def __init__(self, username: str, reference: Episode):
def __init__(self, username: str, reference: Episode, event_type: EventType):
self._reference = reference
self._username = username
self._event_type = event_type
self._audio_stream, self._subtitle_stream = self._get_selected_streams(reference)
self._changes = []
self._description = ""
self._title = ""
self._computed = False

@property
def computed(self):
return self._computed

@property
def event_type(self):
return self._event_type

@property
def description(self):
Expand All @@ -25,9 +37,13 @@ def description(self):
def inline_description(self):
return self._description.replace("\n", " | ")

@property
def title(self):
return self._title

@property
def reference_name(self):
return f"'{self._reference.show().title}' (S{self._reference.seasonNumber:02}E{self._reference.episodeNumber:02})"
return f"{self._reference.show().title} (S{self._reference.seasonNumber:02}E{self._reference.episodeNumber:02})"

@property
def has_changes(self):
Expand All @@ -53,6 +69,8 @@ def get_episodes_to_update(self, update_level: str, update_strategy: str):
return episodes

def compute(self, episodes: List[Episode]):
logger.debug(f"[Language Update] Checking language update for show "
f"{self._reference.show()} and user '{self._username}' based on episode {self._reference}")
self._changes = []
for episode in episodes:
episode.reload()
Expand All @@ -71,8 +89,12 @@ def compute(self, episodes: List[Episode]):
(current_subtitle_stream is None or matching_subtitle_stream.id != current_subtitle_stream.id):
self._changes.append((episode, part, SubtitleStream.STREAMTYPE, matching_subtitle_stream))
self._update_description(episodes)
self._computed = True

def apply(self):
if not self.has_changes:
logger.debug(f"[Language Update] No changes to perform for episode {self._reference} and user '{self.username}'")
return
logger.debug(f"[Language Update] Performing {len(self._changes)} change(s) for show {self._reference.show()}")
for episode, part, stream_type, new_stream in self._changes:
stream_type_name = "audio" if stream_type == AudioStream.STREAMTYPE else "subtitle"
Expand All @@ -90,6 +112,7 @@ def _is_episode_after(self, episode: Episode):

def _update_description(self, episodes: List[Episode]):
if len(episodes) == 0:
self._title = ""
self._description = ""
return
season_numbers = [e.seasonNumber for e in episodes]
Expand All @@ -101,6 +124,7 @@ def _update_description(self, episodes: List[Episode]):
range_str = f"{from_str} - {to_str}" if from_str != to_str else from_str
nb_updated = len({e.key for e, _, _, _ in self._changes})
nb_total = len(episodes)
self._title = self._reference.show().title
self._description = (
f"Show: {self._reference.show().title}\n"
f"User: {self._username}\n"
Expand Down Expand Up @@ -160,3 +184,61 @@ def _get_selected_streams(episode: Union[Episode, MediaPart]):
audio_stream = ([a for a in episode.audioStreams() if a.selected] + [None])[0]
subtitle_stream = ([s for s in episode.subtitleStreams() if s.selected] + [None])[0]
return audio_stream, subtitle_stream


class NewOrUpdatedTrackChanges():

def __init__(self, event_type: EventType):
self._episode = None
self._event_type = event_type
self._track_changes = []
self._description = ""
self._title = ""

@property
def episode_name(self):
if self._episode is None:
return ""
return f"{self._episode.show().title} (S{self._episode.seasonNumber:02}E{self._episode.episodeNumber:02})"

@property
def event_type(self):
return self._event_type

@property
def description(self):
return self._description

@property
def inline_description(self):
return self._description.replace("\n", " | ")

@property
def title(self):
return self._title

@property
def has_changes(self):
return sum([1 for tc in self._track_changes if tc.has_changes]) > 0

def change_track_for_user(self, username: str, reference: Episode, episode: Episode):
self._episode = episode
track_changes = TrackChanges(username, reference, self._event_type)
track_changes.compute([episode])
track_changes.apply()
self._track_changes.append(track_changes)
self._update_description()

def _update_description(self):
if len(self._track_changes) == 0:
self._title = ""
self._description = ""
self._episode = None
return
event_str = "New" if self._event_type == EventType.NEW_EPISODE else "Updated"
self._title = f"{event_str}: {self.episode_name}"
self._description = (
f"Episode: {self.episode_name}\n"
f"Status: {event_str} episode\n"
f"Updated for all users"
)
Loading

0 comments on commit 02406ab

Please sign in to comment.