Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add event filter for notifications #37

Merged
merged 1 commit into from
May 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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