"""Remove LDAP plugins through Jellyfin's administrative API; keep a private rollback copy.""" import datetime,json,pathlib,shutil,sqlite3,urllib.request,urllib.error db=sqlite3.connect('file:/var/lib/jellyfin/data/jellyfin.db?mode=ro',uri=True) key=db.execute('SELECT AccessToken FROM ApiKeys ORDER BY Id LIMIT 1').fetchone()[0];db.close() def api(path,method=None): body=urllib.request.urlopen(urllib.request.Request('http://127.0.0.1:8096'+path,headers={'Authorization':'MediaBrowser Token="'+key+'", Client="DecoderIntegration", Device="Maintenance", DeviceId="decoder-maintenance", Version="7"'},method=method),timeout=30).read();return json.loads(body) if body else None backup=pathlib.Path('/var/lib/jellyfin/decoder-plugin-backups')/('ldap-removed-'+datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ'));backup.mkdir(mode=0o700) for folder in pathlib.Path('/var/lib/jellyfin/plugins').iterdir(): if folder.is_dir() and 'ldap' in folder.name.lower():shutil.copytree(folder,backup/folder.name) removed=[] for plugin in api('/Plugins'): if 'ldap' in plugin['Name'].lower(): try:api('/Plugins/'+plugin['Id']+'/'+plugin['Version'],'DELETE') except urllib.error.HTTPError as ex: if ex.code!=404:raise removed.append({'name':plugin['Name'],'version':plugin['Version']}) for folder in pathlib.Path('/var/lib/jellyfin/plugins').iterdir(): if folder.is_dir() and 'ldap' in folder.name.lower(): if (backup/folder.name).exists():shutil.rmtree(folder) else:shutil.move(str(folder),str(backup/folder.name)) assert not any('ldap' in p.name.lower() for p in pathlib.Path('/var/lib/jellyfin/plugins').iterdir() if p.is_dir()) print(json.dumps({'removed':removed,'backup':str(backup),'restart_required':True}))