Created
June 13, 2025 22:14
-
-
Save thieman/21908b175b37b61d50202f91408fef1a to your computer and use it in GitHub Desktop.
Plex cron causing resource leak
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/etc/py3/bin/python | |
| import argparse | |
| from collections import Counter | |
| import json | |
| from datadog import initialize, statsd | |
| from plexapi.server import PlexServer | |
| import requests | |
| initialize(**{ | |
| 'statsd_socket_path': '/var/run/datadog/dsd.socket', | |
| }) | |
| def set_plex_trmnl_visibility(visible): | |
| plugin_id = None | |
| items = requests.get(f"{TRMNL_HOST}/playlists/items", headers={'Authorization': f'Bearer {TRMNL_KEY}'}).json() | |
| for item in items.get('data', []): | |
| if item.get('plugin_setting', {}).get('name') == 'Plex': | |
| plugin_id = item.get('id') | |
| break | |
| if not plugin_id: | |
| raise ValueError("Plex TRMNL plugin not found") | |
| requests.patch(f"{TRMNL_HOST}/playlists/items/{plugin_id}", | |
| headers={ | |
| 'Authorization': f'Bearer {TRMNL_KEY}', | |
| 'Content-Type': 'application/json', | |
| }, | |
| data=json.dumps({'visible': visible})).raise_for_status() | |
| def main(): | |
| parser = argparse.ArgumentParser(description='Plex stats') | |
| parser.add_argument('stats', type=str, help='which stats to collect') | |
| args = parser.parse_args() | |
| plex = PlexServer(HOST, TOKEN) | |
| if args.stats in ['all', 'session']: | |
| emit_session_stats(plex) | |
| if args.stats in ['all', 'media']: | |
| emit_media_stats(plex) | |
| def emit_session_stats(plex): | |
| audio, video = 0, 0 | |
| if not plex.sessions(): | |
| statsd.gauge('plex.session', 0) | |
| set_plex_trmnl_visibility(False) | |
| else: | |
| set_plex_trmnl_visibility(True) | |
| user_counter = Counter() | |
| for session in plex.sessions(): | |
| for username in session.usernames: | |
| user_counter[username] += 1 | |
| for transcode_session in session.transcodeSessions: | |
| if transcode_session.videoDecision == 'transcode': | |
| video += 1 | |
| if transcode_session.audioDecision == 'transcode': | |
| audio += 1 | |
| for user, count in user_counter.items(): | |
| statsd.gauge('plex.session', count, tags=[f'user:{user}']) | |
| statsd.gauge('plex.transcode.video', video) | |
| statsd.gauge('plex.transcode.audio', audio) | |
| def emit_media_stats(plex): | |
| for section in plex.library.sections(): | |
| statsd.gauge('plex.library.count', len(section.all()), tags=[f'type:{section.type}']) | |
| if section.type == 'movie': | |
| for movie in section.all(): | |
| if len(movie.media) > 1: | |
| statsd.gauge('plex.media.duplicate', 1, tags=['type:movie', f'title:{movie.title}']) | |
| size = 0 | |
| for media in movie.media: | |
| for part in media.parts: | |
| size += part.size | |
| statsd.gauge('plex.media.size', size, tags=[f'type:movie', f'title:{movie.title}']) | |
| if section.type == 'show': | |
| for show in section.all(): | |
| statsd.gauge('plex.show.episodes', len(show.episodes()), tags=[f'title:{show.title}']) | |
| for episode in show.episodes(): | |
| if len(episode.media) > 1: | |
| statsd.gauge('plex.media.duplicate', 1, tags=['type:show', f'title:{show.title}', f'episode:{episode.seasonEpisode}']) | |
| size = 0 | |
| for media in episode.media: | |
| for part in media.parts: | |
| size += part.size | |
| statsd.gauge('plex.media.size', size, tags=[f'type:show', f'title:{show.title}', f'episode:{episode.seasonEpisode}']) | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment