11#!/usr/bin/env python3
2+ import argparse
23import json
34import os
45import ssl
1314
1415BASE_DIR = Path (__file__ ).resolve ().parent .parent
1516CONFIG_PATH = BASE_DIR / 'config' / 'strm-sync.yaml'
17+ RUNTIME_PATH = BASE_DIR / 'config' / 'runtime.yaml'
1618OUT_PATH = BASE_DIR / 'data' / 'strm-health-report.json'
17- EMBY_URL = 'http://127.0.0.1:8096/emby'
18- TEST_URL = 'https://127.0.0.1:8095'
1919USER_AGENT = 'VidHub/2.2.2'
20- USER_ID = 'a5fc8f5b7cf843dc8f9c3c904f937090'
2120
2221
2322class NoRedirect (urllib .request .HTTPRedirectHandler ):
@@ -26,11 +25,26 @@ def redirect_request(self, req, fp, code, msg, headers, newurl):
2625
2726
2827def load_cfg ():
29- return yaml .safe_load (CONFIG_PATH .read_text (encoding = 'utf-8' )) or {}
28+ sync = yaml .safe_load (CONFIG_PATH .read_text (encoding = 'utf-8' )) or {}
29+ runtime = yaml .safe_load (RUNTIME_PATH .read_text (encoding = 'utf-8' )) or {} if RUNTIME_PATH .exists () else {}
30+ return sync , runtime
3031
3132
32- def emby_api_key (cfg ):
33- return (cfg .get ('emby2alist' , {}) or {}).get ('api_key' ) or 'YOUR_EMBY_API_KEY'
33+ def emby_api_key (sync_cfg , runtime_cfg ):
34+ key = (sync_cfg .get ('emby2alist' , {}) or {}).get ('api_key' ) or (runtime_cfg .get ('emby' , {}) or {}).get ('api_key' )
35+ if not key or key == 'YOUR_EMBY_API_KEY' :
36+ raise RuntimeError ('Emby API key is not configured' )
37+ return key
38+
39+
40+ def service_urls (runtime_cfg ):
41+ emby = (runtime_cfg .get ('emby' , {}) or {}).get ('host' , 'http://127.0.0.1:8096' ).rstrip ('/' ) + '/emby'
42+ nginx = runtime_cfg .get ('nginx' , {}) or {}
43+ if nginx .get ('https_enabled' ):
44+ proxy = f"https://127.0.0.1:{ int (nginx .get ('https_port' , 8095 ))} "
45+ else :
46+ proxy = f"http://127.0.0.1:{ int (nginx .get ('http_port' , 8091 ))} "
47+ return emby , proxy
3448
3549
3650def fetch_json (url : str ):
@@ -39,17 +53,25 @@ def fetch_json(url: str):
3953 return json .load (r )
4054
4155
42- def main ():
43- cfg = load_cfg ()
44- api_key = emby_api_key (cfg )
56+ def main (limit : int = 0 ):
57+ sync_cfg , runtime_cfg = load_cfg ()
58+ api_key = emby_api_key (sync_cfg , runtime_cfg )
59+ emby_url , test_url = service_urls (runtime_cfg )
60+ users = fetch_json (emby_url + '/Users?' + urllib .parse .urlencode ({'api_key' : api_key }))
61+ admin = next ((user for user in users if (user .get ('Policy' ) or {}).get ('IsAdministrator' )), None )
62+ user = admin or (users [0 ] if users else None )
63+ if not user or not user .get ('Id' ):
64+ raise RuntimeError ('No Emby user is available for playback health checks' )
65+ user_id = user ['Id' ]
4566 params = {
4667 'api_key' : api_key ,
4768 'Recursive' : 'true' ,
4869 'IncludeItemTypes' : 'Movie,Episode,Video' ,
4970 'Fields' : 'Path,MediaSources' ,
5071 'Limit' : '10000' ,
5172 }
52- url = EMBY_URL + '/Items?' + urllib .parse .urlencode (params )
73+ params ['UserId' ] = user_id
74+ url = emby_url + '/Items?' + urllib .parse .urlencode (params )
5375 data = fetch_json (url )
5476 items = data .get ('Items' , [])
5577
@@ -69,6 +91,9 @@ def main():
6991 'mediaSourceId' : media_source_id ,
7092 'file_exists' : bool (path and os .path .exists (path )),
7193 })
94+ available_strm_items = len (strm_items )
95+ if limit > 0 :
96+ strm_items = strm_items [:limit ]
7297
7398 opener = urllib .request .build_opener (
7499 urllib .request .HTTPSHandler (context = ssl ._create_unverified_context ()),
@@ -83,8 +108,8 @@ def main():
83108 continue
84109
85110 test_url = (
86- f"{ TEST_URL } /emby/videos/{ item ['id' ]} /stream.strm?AutoOpenLiveStream=false"
87- f"&UserId={ USER_ID } &MaxStreamingBitrate=500000000&reqformat=json&IsPlayback=true"
111+ f"{ test_url } /emby/videos/{ item ['id' ]} /stream.strm?AutoOpenLiveStream=false"
112+ f"&UserId={ user_id } &MaxStreamingBitrate=500000000&reqformat=json&IsPlayback=true"
88113 f"&api_key={ api_key } &MediaSourceId={ urllib .parse .quote (item ['mediaSourceId' ])} &Static=true"
89114 )
90115 req = urllib .request .Request (test_url , headers = {'User-Agent' : USER_AGENT }, method = 'GET' )
@@ -111,6 +136,7 @@ def main():
111136
112137 report = {
113138 'generated_at' : time .strftime ('%Y-%m-%dT%H:%M:%S%z' ),
139+ 'available_strm_items' : available_strm_items ,
114140 'total_strm_items' : len (strm_items ),
115141 'healthy_redirect' : len (healthy ),
116142 'missing_file' : len (missing ),
@@ -126,7 +152,9 @@ def main():
126152
127153if __name__ == '__main__' :
128154 try :
129- main ()
155+ parser = argparse .ArgumentParser (description = 'Check Emby STRM files and proxy redirects.' )
156+ parser .add_argument ('--limit' , type = int , default = 0 , help = 'Check only the first N STRM items; 0 checks all.' )
157+ main (max (0 , parser .parse_args ().limit ))
130158 except Exception as e :
131159 print (json .dumps ({'ok' : False , 'error' : str (e )}, ensure_ascii = False ), file = sys .stderr )
132160 sys .exit (1 )
0 commit comments