mirror of
https://github.com/hrfee/jellyfin-accounts.git
synced 2024-12-22 09:00:14 +00:00
Modularized JSON storage
user_template and other files are now accessed via JSONStorage, which has dictionary like attributes for each file which can be used like a dictionary, without the need to manually read and write the file. This was done so that other storage types (e.g a database) can be added in future.
This commit is contained in:
parent
39a27eb762
commit
f4f18d41ea
66
jellyfin_accounts/data_store.py
Normal file
66
jellyfin_accounts/data_store.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import json
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
class JSONFile(dict):
|
||||||
|
@staticmethod
|
||||||
|
def readJSON(path):
|
||||||
|
try:
|
||||||
|
with open(path, 'r') as f:
|
||||||
|
return json.load(f)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def writeJSON(path, data):
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
return f.write(json.dumps(data, indent=4, default=str))
|
||||||
|
|
||||||
|
def __init__(self, path, data=None):
|
||||||
|
self.path = path
|
||||||
|
if data is None:
|
||||||
|
super(JSONFile, self).__init__(self.readJSON(self.path))
|
||||||
|
else:
|
||||||
|
super(JSONFile, self).__init__(data)
|
||||||
|
self.writeJSON(self.path, data)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
super(JSONFile, self).__init__(self.readJSON(self.path))
|
||||||
|
return super(JSONFile, self).__getitem__(key)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
data = self.readJSON(self.path)
|
||||||
|
data[key] = value
|
||||||
|
self.writeJSON(self.path, data)
|
||||||
|
super(JSONFile, self).__init__(data)
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
data = self.readJSON(self.path)
|
||||||
|
super(JSONFile, self).__init__(data)
|
||||||
|
del data[key]
|
||||||
|
self.writeJSON(self.path, data)
|
||||||
|
super(JSONFile, self).__delitem__(key)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
super(JSONFile, self).__init__(self.readJSON(self.path))
|
||||||
|
return json.dumps(super(JSONFile, self))
|
||||||
|
|
||||||
|
|
||||||
|
class JSONStorage:
|
||||||
|
def __init__(self,
|
||||||
|
emails,
|
||||||
|
invites,
|
||||||
|
user_template,
|
||||||
|
user_displayprefs,
|
||||||
|
user_configuration):
|
||||||
|
self.emails = JSONFile(path=emails)
|
||||||
|
self.invites = JSONFile(path=invites)
|
||||||
|
self.user_template = JSONFile(path=user_template)
|
||||||
|
self.user_displayprefs = JSONFile(path=user_displayprefs)
|
||||||
|
self.user_configuration = JSONFile(path=user_configuration)
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
if hasattr(self, name):
|
||||||
|
path = self.__dict__[name].path
|
||||||
|
self.__dict__[name] = JSONFile(path=path, data=value)
|
||||||
|
else:
|
||||||
|
self.__dict__[name] = value
|
@ -4,7 +4,7 @@ from watchdog.observers import Observer
|
|||||||
from watchdog.events import FileSystemEventHandler
|
from watchdog.events import FileSystemEventHandler
|
||||||
from jellyfin_accounts.email import Mailgun, Smtp
|
from jellyfin_accounts.email import Mailgun, Smtp
|
||||||
from jellyfin_accounts.web_api import jf
|
from jellyfin_accounts.web_api import jf
|
||||||
from __main__ import config
|
from __main__ import config, data_store
|
||||||
from __main__ import email_log as log
|
from __main__ import email_log as log
|
||||||
|
|
||||||
|
|
||||||
@ -42,17 +42,18 @@ class Handler(FileSystemEventHandler):
|
|||||||
reset = json.load(f)
|
reset = json.load(f)
|
||||||
log.info(f'New password reset for {reset["UserName"]}')
|
log.info(f'New password reset for {reset["UserName"]}')
|
||||||
try:
|
try:
|
||||||
with open(config['files']['emails'], 'r') as f:
|
id = jf.getUsers(reset['UserName'], public=False)['Id']
|
||||||
emails = json.load(f)
|
address = data_store.emails[id]
|
||||||
id = jf.getUsers(reset['UserName'], public=False)['Id']
|
if address != '':
|
||||||
address = emails[id]
|
method = config['email']['method']
|
||||||
method = config['email']['method']
|
if method == 'mailgun':
|
||||||
if method == 'mailgun':
|
email = Mailgun(address)
|
||||||
email = Mailgun(address)
|
elif method == 'smtp':
|
||||||
elif method == 'smtp':
|
email = Smtp(address)
|
||||||
email = Smtp(address)
|
if email.construct_reset(reset):
|
||||||
if email.construct_reset(reset):
|
email.send()
|
||||||
email.send()
|
else:
|
||||||
|
raise IndexError
|
||||||
except (FileNotFoundError,
|
except (FileNotFoundError,
|
||||||
json.decoder.JSONDecodeError,
|
json.decoder.JSONDecodeError,
|
||||||
IndexError) as e:
|
IndexError) as e:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from flask import Flask, send_from_directory, render_template
|
from flask import Flask, send_from_directory, render_template
|
||||||
from __main__ import config, app, g, css
|
from __main__ import config, app, g, css, data_store
|
||||||
from __main__ import web_log as log
|
from __main__ import web_log as log
|
||||||
from jellyfin_accounts.web_api import checkInvite, validator
|
from jellyfin_accounts.web_api import checkInvite, validator
|
||||||
|
|
||||||
@ -43,16 +43,9 @@ def inviteProxy(path):
|
|||||||
if checkInvite(path):
|
if checkInvite(path):
|
||||||
log.info(f'Invite {path} used to request form')
|
log.info(f'Invite {path} used to request form')
|
||||||
try:
|
try:
|
||||||
with open(config['files']['invites'], 'r') as f:
|
email = data_store.invites[path]['email']
|
||||||
invites = json.load(f)
|
except KeyError:
|
||||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
email = ''
|
||||||
invites = {'invites': []}
|
|
||||||
for invite in invites['invites']:
|
|
||||||
if invite['code'] == path:
|
|
||||||
try:
|
|
||||||
email = invite['email']
|
|
||||||
except KeyError:
|
|
||||||
email = ""
|
|
||||||
return render_template('form.html',
|
return render_template('form.html',
|
||||||
css_href=css['href'],
|
css_href=css['href'],
|
||||||
css_integrity=css['integrity'],
|
css_integrity=css['integrity'],
|
||||||
|
@ -1,48 +1,40 @@
|
|||||||
from flask import request, jsonify
|
from flask import request, jsonify
|
||||||
from configparser import RawConfigParser
|
|
||||||
from jellyfin_accounts.jf_api import Jellyfin
|
from jellyfin_accounts.jf_api import Jellyfin
|
||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
import secrets
|
import secrets
|
||||||
import time
|
import time
|
||||||
from __main__ import config, config_path, app, g
|
from __main__ import config, config_path, app, g, data_store
|
||||||
from __main__ import web_log as log
|
from __main__ import web_log as log
|
||||||
from jellyfin_accounts.validate_password import PasswordValidator
|
from jellyfin_accounts.validate_password import PasswordValidator
|
||||||
|
|
||||||
def resp(success=True, code=500):
|
def resp(success=True, code=500):
|
||||||
if success:
|
if success:
|
||||||
r = jsonify({'success': True})
|
r = jsonify({'success': True})
|
||||||
r.status_code = 200
|
if code == 500:
|
||||||
|
r.status_code = 200
|
||||||
|
else:
|
||||||
|
r.status_code = code
|
||||||
else:
|
else:
|
||||||
r = jsonify({'success': False})
|
r = jsonify({'success': False})
|
||||||
r.status_code = code
|
r.status_code = code
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def checkInvite(code, delete=False):
|
def checkInvite(code, delete=False):
|
||||||
current_time = datetime.datetime.now()
|
current_time = datetime.datetime.now()
|
||||||
try:
|
invites = dict(data_store.invites)
|
||||||
with open(config['files']['invites'], 'r') as f:
|
match = False
|
||||||
invites = json.load(f)
|
for invite in invites:
|
||||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
expiry = datetime.datetime.strptime(invites[invite]['valid_till'],
|
||||||
invites = {'invites': []}
|
|
||||||
valid = False
|
|
||||||
for index, i in enumerate(invites['invites']):
|
|
||||||
expiry = datetime.datetime.strptime(i['valid_till'],
|
|
||||||
'%Y-%m-%dT%H:%M:%S.%f')
|
'%Y-%m-%dT%H:%M:%S.%f')
|
||||||
if current_time >= expiry:
|
if current_time >= expiry:
|
||||||
log.debug(('Housekeeping: Deleting old invite ' +
|
log.debug(f'Housekeeping: Deleting old invite {invite}')
|
||||||
invites['invites'][index]['code']))
|
del data_store.invites[invite]
|
||||||
del invites['invites'][index]
|
elif invite == code:
|
||||||
else:
|
match = True
|
||||||
if i['code'] == code:
|
if delete:
|
||||||
valid = True
|
del data_store.invites[code]
|
||||||
if delete:
|
return match
|
||||||
del invites['invites'][index]
|
|
||||||
with open(config['files']['invites'], 'w') as f:
|
|
||||||
f.write(json.dumps(invites, indent=4, default=str))
|
|
||||||
return valid
|
|
||||||
|
|
||||||
|
|
||||||
jf = Jellyfin(config['jellyfin']['server'],
|
jf = Jellyfin(config['jellyfin']['server'],
|
||||||
config['jellyfin']['client'],
|
config['jellyfin']['client'],
|
||||||
@ -52,26 +44,22 @@ jf = Jellyfin(config['jellyfin']['server'],
|
|||||||
|
|
||||||
from jellyfin_accounts.login import auth
|
from jellyfin_accounts.login import auth
|
||||||
|
|
||||||
attempts = 0
|
jf_address = config['jellyfin']['server']
|
||||||
success = False
|
success = False
|
||||||
while attempts != 3:
|
for i in range(3):
|
||||||
try:
|
try:
|
||||||
jf.authenticate(config['jellyfin']['username'],
|
jf.authenticate(config['jellyfin']['username'],
|
||||||
config['jellyfin']['password'])
|
config['jellyfin']['password'])
|
||||||
success = True
|
success = True
|
||||||
log.info(('Successfully authenticated with ' +
|
log.info(f'Successfully authenticated with {jf_address}')
|
||||||
config['jellyfin']['server']))
|
|
||||||
break
|
break
|
||||||
except Jellyfin.AuthenticationError:
|
except Jellyfin.AuthenticationError:
|
||||||
attempts += 1
|
log.error(f'Failed to authenticate with {jf_address}, Retrying...')
|
||||||
log.error(('Failed to authenticate with ' +
|
|
||||||
config['jellyfin']['server'] +
|
|
||||||
'. Retrying...'))
|
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
log.error('Could not authenticate after 3 tries.')
|
log.error('Could not authenticate after 3 tries.')
|
||||||
|
exit()
|
||||||
|
|
||||||
def switchToIds():
|
def switchToIds():
|
||||||
try:
|
try:
|
||||||
@ -112,7 +100,7 @@ else:
|
|||||||
validator = PasswordValidator(0, 0, 0, 0, 0)
|
validator = PasswordValidator(0, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/newUser', methods=['GET', 'POST'])
|
@app.route('/newUser', methods=['POST'])
|
||||||
def newUser():
|
def newUser():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
log.debug('Attempted newUser')
|
log.debug('Attempted newUser')
|
||||||
@ -125,12 +113,10 @@ def newUser():
|
|||||||
if valid:
|
if valid:
|
||||||
log.debug('User password valid')
|
log.debug('User password valid')
|
||||||
try:
|
try:
|
||||||
jf.authenticate(config['jellyfin']['username'],
|
user = jf.newUser(data['username'],
|
||||||
config['jellyfin']['password'])
|
data['password'])
|
||||||
user = jf.newUser(data['username'], data['password'])
|
|
||||||
except Jellyfin.UserExistsError:
|
except Jellyfin.UserExistsError:
|
||||||
error = 'User already exists with name '
|
error = f'User already exists named {data["username"]}'
|
||||||
error += data['username']
|
|
||||||
log.debug(error)
|
log.debug(error)
|
||||||
return jsonify({'error': error})
|
return jsonify({'error': error})
|
||||||
except:
|
except:
|
||||||
@ -138,36 +124,31 @@ def newUser():
|
|||||||
checkInvite(data['code'], delete=True)
|
checkInvite(data['code'], delete=True)
|
||||||
if user.status_code == 200:
|
if user.status_code == 200:
|
||||||
try:
|
try:
|
||||||
with open(config['files']['user_template'], 'r') as f:
|
policy = data_store.user_template
|
||||||
default_policy = json.load(f)
|
if policy != {}:
|
||||||
jf.setPolicy(user.json()['Id'], default_policy)
|
jf.setPolicy(user.json()['Id'], policy)
|
||||||
|
else:
|
||||||
|
log.debug('user policy was blank')
|
||||||
except:
|
except:
|
||||||
log.error('Failed to set new user policy. ' +
|
log.error('Failed to set new user policy')
|
||||||
'Ignore if you didn\'t create a template')
|
|
||||||
try:
|
try:
|
||||||
with open(config['files']['user_configuration'], 'r') as f:
|
configuration = data_store.user_configuration
|
||||||
default_configuration = json.load(f)
|
displayprefs = data_store.user_displayprefs
|
||||||
with open(config['files']['user_displayprefs'], 'r') as f:
|
if configuration != {} and displayprefs != {}:
|
||||||
default_displayprefs = json.load(f)
|
if jf.setConfiguration(user.json()['Id'],
|
||||||
if jf.setConfiguration(user.json()['Id'],
|
configuration):
|
||||||
default_configuration):
|
jf.setDisplayPreferences(user.json()['Id'],
|
||||||
jf.setDisplayPreferences(user.json()['Id'],
|
displayprefs)
|
||||||
default_displayprefs)
|
log.debug('Set homescreen layout.')
|
||||||
log.debug('Set homescreen layout.')
|
else:
|
||||||
|
log.debug('user configuration and/or ' +
|
||||||
|
'displayprefs were blank')
|
||||||
except:
|
except:
|
||||||
log.error('Failed to set new user homescreen kayout.' +
|
log.error('Failed to set new user homescreen layout')
|
||||||
'Ignore if you didn\'t create a template')
|
|
||||||
if config.getboolean('password_resets', 'enabled'):
|
if config.getboolean('password_resets', 'enabled'):
|
||||||
try:
|
data_store.emails[user.json()['Id']] = data['email']
|
||||||
with open(config['files']['emails'], 'r') as f:
|
|
||||||
emails = json.load(f)
|
|
||||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
|
||||||
emails = {}
|
|
||||||
emails[user.json()['Id']] = data['email']
|
|
||||||
with open(config['files']['emails'], 'w') as f:
|
|
||||||
f.write(json.dumps(emails, indent=4))
|
|
||||||
log.debug('Email address stored')
|
log.debug('Email address stored')
|
||||||
log.info('New User created.')
|
log.info('New user created')
|
||||||
else:
|
else:
|
||||||
log.error(f'New user creation failed: {user.status_code}')
|
log.error(f'New user creation failed: {user.status_code}')
|
||||||
return resp(False)
|
return resp(False)
|
||||||
@ -179,15 +160,16 @@ def newUser():
|
|||||||
return resp(False, code=401)
|
return resp(False, code=401)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/generateInvite', methods=['GET', 'POST'])
|
@app.route('/generateInvite', methods=['POST'])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def generateInvite():
|
def generateInvite():
|
||||||
current_time = datetime.datetime.now()
|
current_time = datetime.datetime.now()
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
delta = datetime.timedelta(hours=int(data['hours']),
|
delta = datetime.timedelta(hours=int(data['hours']),
|
||||||
minutes=int(data['minutes']))
|
minutes=int(data['minutes']))
|
||||||
invite = {'code': secrets.token_urlsafe(16)}
|
invite_code = secrets.token_urlsafe(16)
|
||||||
log.debug(f'Creating new invite: {invite["code"]}')
|
invite = {}
|
||||||
|
log.debug(f'Creating new invite: {invite_code}')
|
||||||
valid_till = current_time + delta
|
valid_till = current_time + delta
|
||||||
invite['valid_till'] = valid_till.strftime('%Y-%m-%dT%H:%M:%S.%f')
|
invite['valid_till'] = valid_till.strftime('%Y-%m-%dT%H:%M:%S.%f')
|
||||||
if 'email' in data and config.getboolean('invite_emails', 'enabled'):
|
if 'email' in data and config.getboolean('invite_emails', 'enabled'):
|
||||||
@ -202,19 +184,12 @@ def generateInvite():
|
|||||||
from jellyfin_accounts.email import Smtp
|
from jellyfin_accounts.email import Smtp
|
||||||
email = Smtp(address)
|
email = Smtp(address)
|
||||||
email.construct_invite({'expiry': valid_till,
|
email.construct_invite({'expiry': valid_till,
|
||||||
'code': invite['code']})
|
'code': invite_code})
|
||||||
response = email.send()
|
response = email.send()
|
||||||
if response is False or type(response) != bool:
|
if response is False or type(response) != bool:
|
||||||
invite['email'] = f'Failed to send to {address}'
|
invite['email'] = f'Failed to send to {address}'
|
||||||
try:
|
data_store.invites[invite_code] = invite
|
||||||
with open(config['files']['invites'], 'r') as f:
|
log.info(f'New invite created: {invite_code}')
|
||||||
invites = json.load(f)
|
|
||||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
|
||||||
invites = {'invites': []}
|
|
||||||
invites['invites'].append(invite)
|
|
||||||
with open(config['files']['invites'], 'w') as f:
|
|
||||||
f.write(json.dumps(invites, indent=4, default=str))
|
|
||||||
log.info(f'New invite created: {invite["code"]}')
|
|
||||||
return resp()
|
return resp()
|
||||||
|
|
||||||
|
|
||||||
@ -223,46 +198,30 @@ def generateInvite():
|
|||||||
def getInvites():
|
def getInvites():
|
||||||
log.debug('Invites requested')
|
log.debug('Invites requested')
|
||||||
current_time = datetime.datetime.now()
|
current_time = datetime.datetime.now()
|
||||||
try:
|
invites = dict(data_store.invites)
|
||||||
with open(config['files']['invites'], 'r') as f:
|
for code in invites:
|
||||||
invites = json.load(f)
|
checkInvite(code)
|
||||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
invites = dict(data_store.invites)
|
||||||
invites = {'invites': []}
|
|
||||||
response = {'invites': []}
|
response = {'invites': []}
|
||||||
for index, i in enumerate(invites['invites']):
|
for code in invites:
|
||||||
expiry = datetime.datetime.strptime(i['valid_till'],
|
expiry = datetime.datetime.strptime(invites[code]['valid_till'],
|
||||||
'%Y-%m-%dT%H:%M:%S.%f')
|
'%Y-%m-%dT%H:%M:%S.%f')
|
||||||
if current_time >= expiry:
|
valid_for = expiry - current_time
|
||||||
log.debug(('Housekeeping: Deleting old invite ' +
|
invite = {'code': code,
|
||||||
invites['invites'][index]['code']))
|
'hours': valid_for.seconds//3600,
|
||||||
del invites['invites'][index]
|
'minutes': (valid_for.seconds//60) % 60}
|
||||||
else:
|
if 'email' in invites[code]:
|
||||||
valid_for = expiry - current_time
|
invite['email'] = invites[code]['email']
|
||||||
invite = {'code': i['code'],
|
response['invites'].append(invite)
|
||||||
'hours': valid_for.seconds//3600,
|
|
||||||
'minutes': (valid_for.seconds//60) % 60}
|
|
||||||
if 'email' in i:
|
|
||||||
invite['email'] = i['email']
|
|
||||||
response['invites'].append(invite)
|
|
||||||
with open(config['files']['invites'], 'w') as f:
|
|
||||||
f.write(json.dumps(invites, indent=4, default=str))
|
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/deleteInvite', methods=['POST'])
|
@app.route('/deleteInvite', methods=['POST'])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def deleteInvite():
|
def deleteInvite():
|
||||||
code = request.get_json()['code']
|
code = request.get_json()['code']
|
||||||
try:
|
invites = dict(data_store.invites)
|
||||||
with open(config['files']['invites'], 'r') as f:
|
if code in invites:
|
||||||
invites = json.load(f)
|
del data_store.invites[code]
|
||||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
|
||||||
invites = {'invites': []}
|
|
||||||
for index, i in enumerate(invites['invites']):
|
|
||||||
if i['code'] == code:
|
|
||||||
del invites['invites'][index]
|
|
||||||
with open(config['files']['invites'], 'w') as f:
|
|
||||||
f.write(json.dumps(invites, indent=4, default=str))
|
|
||||||
log.info(f'Invite deleted: {code}')
|
log.info(f'Invite deleted: {code}')
|
||||||
return resp()
|
return resp()
|
||||||
|
|
||||||
@ -274,19 +233,13 @@ def get_token():
|
|||||||
return jsonify({'token': token.decode('ascii')})
|
return jsonify({'token': token.decode('ascii')})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/getUsers', methods=['GET', 'POST'])
|
@app.route('/getUsers', methods=['GET'])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def getUsers():
|
def getUsers():
|
||||||
log.debug('User and email list requested')
|
log.debug('User and email list requested')
|
||||||
try:
|
|
||||||
with open(config['files']['emails'], 'r') as f:
|
|
||||||
emails = json.load(f)
|
|
||||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
|
||||||
emails = {}
|
|
||||||
response = {'users': []}
|
response = {'users': []}
|
||||||
jf.authenticate(config['jellyfin']['username'],
|
|
||||||
config['jellyfin']['password'])
|
|
||||||
users = jf.getUsers(public=False)
|
users = jf.getUsers(public=False)
|
||||||
|
emails = data_store.emails
|
||||||
for user in users:
|
for user in users:
|
||||||
entry = {'name': user['Name']}
|
entry = {'name': user['Name']}
|
||||||
if user['Id'] in emails:
|
if user['Id'] in emails:
|
||||||
@ -294,29 +247,17 @@ def getUsers():
|
|||||||
response['users'].append(entry)
|
response['users'].append(entry)
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/modifyUsers', methods=['POST'])
|
@app.route('/modifyUsers', methods=['POST'])
|
||||||
@auth.login_required
|
@auth.login_required
|
||||||
def modifyUsers():
|
def modifyUsers():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
log.debug('User and email list modification requested')
|
log.debug('Email list modification requested')
|
||||||
try:
|
|
||||||
with open(config['files']['emails'], 'r') as f:
|
|
||||||
emails = json.load(f)
|
|
||||||
except (FileNotFoundError, json.decoder.JSONDecodeError):
|
|
||||||
emails = {}
|
|
||||||
jf.authenticate(config['jellyfin']['username'],
|
|
||||||
config['jellyfin']['password'])
|
|
||||||
for key in data:
|
for key in data:
|
||||||
uid = jf.getUsers(key, public=False)['Id']
|
uid = jf.getUsers(key, public=False)['Id']
|
||||||
|
data_store.emails[uid] = data[key]
|
||||||
log.debug(f'Email for user "{key}" modified')
|
log.debug(f'Email for user "{key}" modified')
|
||||||
emails[uid] = data[key]
|
return resp()
|
||||||
try:
|
|
||||||
with open(config['files']['emails'], 'w') as f:
|
|
||||||
f.write(json.dumps(emails, indent=4))
|
|
||||||
return resp()
|
|
||||||
except:
|
|
||||||
log.error('Could not store email')
|
|
||||||
return resp(success=False)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/setDefaults', methods=['POST'])
|
@app.route('/setDefaults', methods=['POST'])
|
||||||
@ -324,33 +265,27 @@ def modifyUsers():
|
|||||||
def setDefaults():
|
def setDefaults():
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
username = data['username']
|
username = data['username']
|
||||||
log.debug(f'storing default settings from user {username}')
|
log.debug(f'Storing default settings from user {username}')
|
||||||
jf.authenticate(config['jellyfin']['username'],
|
|
||||||
config['jellyfin']['password'])
|
|
||||||
try:
|
try:
|
||||||
user = jf.getUsers(username=username,
|
user = jf.getUsers(username=username,
|
||||||
public=False)
|
public=False)
|
||||||
except Jellyfin.UserNotFoundError:
|
except Jellyfin.UserNotFoundError:
|
||||||
log.error(f'couldn\'t find user {username}')
|
log.error(f'Storing defaults failed: Couldn\'t find user {username}')
|
||||||
return resp(success=False)
|
return resp(False)
|
||||||
uid = user['Id']
|
uid = user['Id']
|
||||||
policy = user['Policy']
|
policy = user['Policy']
|
||||||
try:
|
data_store.user_template = policy
|
||||||
with open(config['files']['user_template'], 'w') as f:
|
|
||||||
f.write(json.dumps(policy, indent=4))
|
|
||||||
except:
|
|
||||||
log.error('Could not store user template')
|
|
||||||
return resp(success=False)
|
|
||||||
if data['homescreen']:
|
if data['homescreen']:
|
||||||
configuration = user['Configuration']
|
configuration = user['Configuration']
|
||||||
try:
|
try:
|
||||||
display_prefs = jf.getDisplayPreferences(uid)
|
displayprefs = jf.getDisplayPreferences(uid)
|
||||||
with open(config['files']['user_configuration'], 'w') as f:
|
data_store.user_configuration = configuration
|
||||||
f.write(json.dumps(configuration, indent=4))
|
data_store.user_displayprefs = displayprefs
|
||||||
with open(config['files']['user_displayprefs'], 'w') as f:
|
|
||||||
f.write(json.dumps(display_prefs, indent=4))
|
|
||||||
except:
|
except:
|
||||||
log.error('Could not store homescreen layout')
|
log.error('Storing defaults failed: ' +
|
||||||
|
'couldn\'t store homescreen layout')
|
||||||
|
return resp(False)
|
||||||
return resp()
|
return resp()
|
||||||
|
|
||||||
import jellyfin_accounts.setup
|
import jellyfin_accounts.setup
|
||||||
|
|
||||||
|
38
jf-accounts
38
jf-accounts
@ -7,8 +7,10 @@ import logging
|
|||||||
import threading
|
import threading
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from flask import Flask, g
|
from flask import Flask, g
|
||||||
|
from jellyfin_accounts.data_store import JSONStorage
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="jellyfin-accounts")
|
parser = argparse.ArgumentParser(description="jellyfin-accounts")
|
||||||
|
|
||||||
@ -97,6 +99,25 @@ for key in ['user_configuration', 'user_displayprefs']:
|
|||||||
log.debug(f'Using default {key}')
|
log.debug(f'Using default {key}')
|
||||||
config['files'][key] = str(data_dir / (key + '.json'))
|
config['files'][key] = str(data_dir / (key + '.json'))
|
||||||
|
|
||||||
|
with open(config['files']['invites'], 'r') as f:
|
||||||
|
temp_invites = json.load(f)
|
||||||
|
if 'invites' in temp_invites:
|
||||||
|
new_invites = {}
|
||||||
|
log.info('Converting invites.json to new format, temporary.')
|
||||||
|
for el in temp_invites['invites']:
|
||||||
|
i = {'valid_till': el['valid_till']}
|
||||||
|
if 'email' in el:
|
||||||
|
i['email'] = el['email']
|
||||||
|
new_invites[el['code']] = i
|
||||||
|
with open(config['files']['invites'], 'w') as f:
|
||||||
|
f.write(json.dumps(new_invites, indent=4, default=str))
|
||||||
|
|
||||||
|
|
||||||
|
data_store = JSONStorage(config['files']['emails'],
|
||||||
|
config['files']['invites'],
|
||||||
|
config['files']['user_template'],
|
||||||
|
config['files']['user_displayprefs'],
|
||||||
|
config['files']['user_configuration'])
|
||||||
|
|
||||||
def default_css():
|
def default_css():
|
||||||
css = {}
|
css = {}
|
||||||
@ -148,10 +169,10 @@ if args.get_defaults:
|
|||||||
import json
|
import json
|
||||||
from jellyfin_accounts.jf_api import Jellyfin
|
from jellyfin_accounts.jf_api import Jellyfin
|
||||||
jf = Jellyfin(config['jellyfin']['server'],
|
jf = Jellyfin(config['jellyfin']['server'],
|
||||||
config['jellyfin']['client'],
|
config['jellyfin']['client'],
|
||||||
config['jellyfin']['version'],
|
config['jellyfin']['version'],
|
||||||
config['jellyfin']['device'],
|
config['jellyfin']['device'],
|
||||||
config['jellyfin']['device_id'])
|
config['jellyfin']['device_id'])
|
||||||
print("NOTE: This can now be done through the web ui.")
|
print("NOTE: This can now be done through the web ui.")
|
||||||
print("""
|
print("""
|
||||||
This tool lets you grab various settings from a user,
|
This tool lets you grab various settings from a user,
|
||||||
@ -187,8 +208,7 @@ if args.get_defaults:
|
|||||||
success = True
|
success = True
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
with open(config['files']['user_template'], 'w') as f:
|
data_store.user_template = policy
|
||||||
f.write(json.dumps(policy, indent=4))
|
|
||||||
print(f'Policy written to "{config["files"]["user_template"]}".')
|
print(f'Policy written to "{config["files"]["user_template"]}".')
|
||||||
print('In future, this policy will be copied to all new users.')
|
print('In future, this policy will be copied to all new users.')
|
||||||
print('Step 2: Homescreen Layout')
|
print('Step 2: Homescreen Layout')
|
||||||
@ -203,11 +223,9 @@ if args.get_defaults:
|
|||||||
user_id = users[user_index]['Id']
|
user_id = users[user_index]['Id']
|
||||||
configuration = users[user_index]['Configuration']
|
configuration = users[user_index]['Configuration']
|
||||||
display_prefs = jf.getDisplayPreferences(user_id)
|
display_prefs = jf.getDisplayPreferences(user_id)
|
||||||
with open(config['files']['user_configuration'], 'w') as f:
|
data_store.user_configuration = configuration
|
||||||
f.write(json.dumps(configuration, indent=4))
|
|
||||||
print(f'Configuration written to "{config["files"]["user_configuration"]}".')
|
print(f'Configuration written to "{config["files"]["user_configuration"]}".')
|
||||||
with open(config['files']['user_displayprefs'], 'w') as f:
|
data_store.user_displayprefs = display_prefs
|
||||||
f.write(json.dumps(display_prefs, indent=4))
|
|
||||||
print(f'Display Prefs written to "{config["files"]["user_displayprefs"]}".')
|
print(f'Display Prefs written to "{config["files"]["user_displayprefs"]}".')
|
||||||
success = True
|
success = True
|
||||||
elif choice.lower() == 'n':
|
elif choice.lower() == 'n':
|
||||||
|
Loading…
Reference in New Issue
Block a user