diff --git a/README.md b/README.md index 7604e0a..2312ce6 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,14 @@ optional arguments: ~/.jf-accounts. --host HOST address to host web ui on. -p PORT, --port PORT port to host web ui on. - -g, --get_policy tool to grab a JF users policy (access, perms, etc.) - and output as json to be used as a user template. + -g, --get_defaults tool to grab a JF users policy (access, perms, etc.) + and homescreen layout and output it as json to be used + as a user template. ``` ### Setup -#### Policy template -* You may want to restrict a user from accessing certain libraries (e.g 4K Movies), or display their account on the login screen by default. Jellyfin stores these settings as a user's policy. -* Make a temporary account and change its settings, then run `jf-accounts --get_policy`. Choose your user, and the policy will be stored at the location you set in `user_template`, and used for all subsequent new accounts. +#### New user template +* You may want to restrict a user from accessing certain libraries (e.g 4K Movies), display their account on the login screen by default, or set a default homecrseen layout. Jellyfin stores these settings in the user's policy, configuration and displayPreferences. +* Make a temporary account and change its settings, then run `jf-accounts --get_defaults`. Choose your user, and this data will be stored at the location you set in `user_template`, `user_configuration` and `user_displayprefs` (or their default locations), and used for all subsequent new accounts. #### Emails/Password Resets * When someone initiates forget password on Jellyfin, a file named `passwordreset*.json` is created in its configuration directory. This directory is monitored and when created, the program reads the username, expiry time and PIN, puts it into a template and sends it to whatever address is specified in `emails.json`. * **The default forget password popup references the `passwordreset*.json` file created. This is confusing for users, so a quick fix is to edit the `MessageForgotPasswordFileCreated` string in Jellyfin's language folder.** @@ -196,10 +197,14 @@ password = smtp password invites = ; Path to store emails addresses in JSON emails = -; Path to the user policy template. Can be acquired with get-template. +; Path to the user policy template. Can be acquired with get-defaults (jf-accounts -g). user_template = +; Path to the user configuration template (part of homescreen layout). Can be acquired with get-defaults (jf-accounts -g). +user_configuration = +; Path to the user display preferences template (part of homescreen layout). Can be acquired with get-defaults (jf-accounts -g). +user_displayprefs = ; Path to custom bootstrap.css -custom_css = +custom_css = ``` diff --git a/data/config-default.ini b/data/config-default.ini index d32be37..ce8da3b 100644 --- a/data/config-default.ini +++ b/data/config-default.ini @@ -105,8 +105,12 @@ password = smtp password invites = ; Path to store emails addresses in JSON emails = -; Path to the user policy template. Can be acquired with get-template. +; Path to the user policy template. Can be acquired with get-defaults (jf-accounts -g). user_template = +; Path to the user configuration template (part of homescreen layout). Can be acquired with get-defaults (jf-accounts -g). +user_configuration = +; Path to the user display preferences template (part of homescreen layout). Can be acquired with get-defaults (jf-accounts -g). +user_displayprefs = ; Path to custom bootstrap.css custom_css = diff --git a/jellyfin_accounts/web_api.py b/jellyfin_accounts/web_api.py index 949f578..c389c0d 100644 --- a/jellyfin_accounts/web_api.py +++ b/jellyfin_accounts/web_api.py @@ -124,7 +124,7 @@ def newUser(): valid = False if valid: log.debug('User password valid') - try: + try: jf.authenticate(config['jellyfin']['username'], config['jellyfin']['password']) user = jf.newUser(data['username'], data['password']) @@ -142,7 +142,21 @@ def newUser(): default_policy = json.load(f) jf.setPolicy(user.json()['Id'], default_policy) except: - log.debug('setPolicy failed') + log.error('Failed to set new user policy. ' + + 'Ignore if you didn\'t create a template') + try: + with open(config['files']['user_configuration'], 'r') as f: + default_configuration = json.load(f) + with open(config['files']['user_displayprefs'], 'r') as f: + default_displayprefs = json.load(f) + if jf.setConfiguration(user.json()['Id'], + default_configuration): + jf.setDisplayPreferences(user.json()['Id'], + default_displayprefs) + log.debug('Set homescreen layout.') + except: + log.error('Failed to set new user homescreen kayout.' + + 'Ignore if you didn\'t create a template') if config.getboolean('password_resets', 'enabled'): try: with open(config['files']['emails'], 'r') as f: diff --git a/jf-accounts b/jf-accounts index 687a5cb..9927f6d 100755 --- a/jf-accounts +++ b/jf-accounts @@ -21,10 +21,11 @@ parser.add_argument("--host", help="address to host web ui on.") parser.add_argument("-p", "--port", help="port to host web ui on.") -parser.add_argument("-g", "--get_policy", +parser.add_argument("-g", "--get_defaults", help=("tool to grab a JF users " + "policy (access, perms, etc.) and " + - "output as json to be used as a user template."), + "homescreen layout and " + + "output it as json to be used as a user template."), action='store_true') args, leftovers = parser.parse_known_args() @@ -91,6 +92,11 @@ for key in config['files']: log.debug(f'Using default {key}') config['files'][key] = str(data_dir / (key + '.json')) +for key in ['user_configuration', 'user_displayprefs']: + if key not in config['files']: + log.debug(f'Using default {key}') + config['files'][key] = str(data_dir / (key + '.json')) + def default_css(): css = {} @@ -138,7 +144,7 @@ if ('public_server' not in config['jellyfin'] or config['jellyfin']['public_server'] == ''): config['jellyfin']['public_server'] = config['jellyfin']['server'] -if args.get_policy: +if args.get_defaults: import json from jellyfin_accounts.jf_api import Jellyfin jf = Jellyfin(config['jellyfin']['server'], @@ -147,14 +153,37 @@ if args.get_policy: config['jellyfin']['device'], config['jellyfin']['device_id']) # No auth needed. - print("Make sure the user is publicly visible!") - users = jf.getUsers() + print(""" + This tool lets you grab various settings from a user, + so that they can be applied every time a new account is + created. """) + print("Step 1: User Policy.") + print(""" + A user policy stores a users permissions (e.g access rights and + most of the other settings in the 'Profile' and 'Access' tabs + of a user). """) + success = False + msg = "Get public users only or all users? (requires auth) [public/all]: " + public = False + while not success: + choice = input(msg) + if choice == 'public': + public = True + print("Make sure the user is publicly visible!") + success = True + elif choice == 'all': + jf.authenticate(config['jellyfin']['username'], + config['jellyfin']['password']) + public = False + success = True + users = jf.getUsers(public=public) for index, user in enumerate(users): print(f'{index+1}) {user["Name"]}') success = False while not success: try: - policy = users[int(input(">: "))-1]['Policy'] + user_index = int(input(">: "))-1 + policy = users[user_index]['Policy'] success = True except (ValueError, IndexError): pass @@ -162,6 +191,28 @@ if args.get_policy: f.write(json.dumps(policy, indent=4)) print(f'Policy written to "{config["files"]["user_template"]}".') print('In future, this policy will be copied to all new users.') + print('Step 2: Homescreen Layout') + print(""" + You may want to customize the default layout of a new user's + home screen. These settings can be applied to an account through + the 'Home' section in a user's settings. """) + success = False + while not success: + choice = input("Grab the chosen user's homescreen layout? [y/n]: ") + if choice.lower() == 'y': + user_id = users[user_index]['Id'] + configuration = users[user_index]['Configuration'] + display_prefs = jf.getDisplayPreferences(user_id) + with open(config['files']['user_configuration'], 'w') as f: + f.write(json.dumps(configuration, indent=4)) + print(f'Configuration written to "{config["files"]["user_configuration"]}".') + with open(config['files']['user_displayprefs'], 'w') as f: + f.write(json.dumps(display_prefs, indent=4)) + print(f'Display Prefs written to "{config["files"]["user_displayprefs"]}".') + success = True + elif choice.lower() == 'n': + success = True + else: def signal_handler(sig, frame): print('Quitting...')