mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-11 07:46:07 -07:00
Add config import to settings page
This commit is contained in:
parent
06665fdd06
commit
3043956dec
4 changed files with 213 additions and 10 deletions
148
data/interfaces/default/config_import.html
Normal file
148
data/interfaces/default/config_import.html
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true"><i class="fa fa-remove"></i></button>
|
||||||
|
<h4 class="modal-title">${title}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="modal-text">
|
||||||
|
<form id="import_config_form" enctype="multipart/form-data" method="post" name="import_config_form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="import_config_file">Option 1: Upload a Config File</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="import_config_file" class="input-group-btn">
|
||||||
|
<span class="btn btn-form">Upload</span>
|
||||||
|
<input type="file" style="display: none;" id="import_config_file" name="import_config_file" required>
|
||||||
|
</label>
|
||||||
|
<input id="import_config_file_name" type="text" class="form-control" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Upload the Tautulli config you wish to import.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="import_config_path">Option 2: Browse for a Config File</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-form" type="button" id="import_config_path_browse">Browse</button>
|
||||||
|
</span>
|
||||||
|
<input type="text" class="form-control" id="import_config_path" name="import_config_path" value="" required disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Browse for the Tautulli config you wish to import.</p>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="import_backup_config" id="import_backup_config" value="1" checked> Backup Current Config
|
||||||
|
</label>
|
||||||
|
<p class="help-block">Automatically create a backup of the current config before importing.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Import Notes</label>
|
||||||
|
<p class="help-block">The following settings will <em>not</em> be imported:</p>
|
||||||
|
<ul class="help-block" style="padding-inline-start: 15px;">
|
||||||
|
<li>Git Path, Log / Backup / Cache Directory</li>
|
||||||
|
<li>Custom Newsletter Templates Folder, Newsletter Output Directory</li>
|
||||||
|
<li>HTTP Host / Port / Root / Username / Password</li>
|
||||||
|
<li>Enable HTTPS, HTTPS Certificate / Certificate Chain / Key</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div>
|
||||||
|
<span id="status-message" style="padding-right: 25px;"></span>
|
||||||
|
<input type="button" id="import_config" class="btn btn-bright" value="Import">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$('#import_config_path_browse').click(function () {
|
||||||
|
$('#browse-path-type').text('Config File');
|
||||||
|
$('#browse-path-modal').modal('show');
|
||||||
|
browsePath(null, null, '.ini');
|
||||||
|
});
|
||||||
|
$('#select-browse-file').click(function () {
|
||||||
|
$('#browse-path-modal').modal('hide');
|
||||||
|
$("#import_config_path").val($('#browse-path').val());
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#import_config_file').change(function() {
|
||||||
|
if ($(this)[0].files[0]) {
|
||||||
|
$('#import_config_file_name').val($(this)[0].files[0].name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#import_config").click(function() {
|
||||||
|
$(this).prop('disabled', true);
|
||||||
|
|
||||||
|
var config_file = $('#import_config_file')[0].files[0];
|
||||||
|
var config_path = $("#import_config_path").val();
|
||||||
|
var backup = $("#import_backup_config").is(':checked');
|
||||||
|
|
||||||
|
var content_type;
|
||||||
|
var process_data;
|
||||||
|
var data;
|
||||||
|
|
||||||
|
if (config_file) {
|
||||||
|
content_type = false;
|
||||||
|
process_data = false;
|
||||||
|
data = new FormData();
|
||||||
|
data.append('config_file', config_file);
|
||||||
|
data.append('backup', backup);
|
||||||
|
} else {
|
||||||
|
content_type = 'application/x-www-form-urlencoded; charset=UTF-8';
|
||||||
|
process_data = true;
|
||||||
|
data = {
|
||||||
|
config_path: config_path,
|
||||||
|
backup: backup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_file) {
|
||||||
|
$("#status-message").html('<i class="fa fa-fw fa-spin fa-refresh"></i> Uploading config file...');
|
||||||
|
} else {
|
||||||
|
$("#status-message").html('<i class="fa fa-fw fa-spin fa-refresh"></i>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: 'import_config',
|
||||||
|
type: 'POST',
|
||||||
|
data: data,
|
||||||
|
cache: false,
|
||||||
|
async: true,
|
||||||
|
contentType: content_type,
|
||||||
|
processData: process_data,
|
||||||
|
success: function(data) {
|
||||||
|
var msg;
|
||||||
|
if (data.result === 'success') {
|
||||||
|
msg = "<i class='fa fa-check'></i> " + data.message;
|
||||||
|
window.location.href = 'restart_import_config';
|
||||||
|
} else {
|
||||||
|
msg = "<i class='fa fa-exclamation-triangle'></i> " + data.message;
|
||||||
|
}
|
||||||
|
$("#status-message").html(msg);
|
||||||
|
$("#import_config_file").val(null);
|
||||||
|
$("#import_config_file_name").val('');
|
||||||
|
$("#import_config_path").val('');
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
var msg = "<i class='fa fa-exclamation-triangle'></i> Error (" + xhr.status + "): ";
|
||||||
|
if (xhr.status === 413) {
|
||||||
|
msg += "file is too large to upload"
|
||||||
|
} else {
|
||||||
|
msg += 'try again'
|
||||||
|
}
|
||||||
|
$("#status-message").html(msg);
|
||||||
|
},
|
||||||
|
complete: function(xhr) {
|
||||||
|
$("#import_config").prop('disabled', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -1302,14 +1302,32 @@
|
||||||
<div role="tabpanel" class="tab-pane" id="tabs-import_backups">
|
<div role="tabpanel" class="tab-pane" id="tabs-import_backups">
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
<h3>Database Import</h3>
|
<h3>Import</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="help-block">Click a button below to import an existing database from the selected app.</p>
|
<div class="form-group">
|
||||||
<div class="btn-group">
|
<label for="database_import">Database Import</label>
|
||||||
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="tautulli">Tautulli</button>
|
<p class="help-block">Click a button below to import an existing database from the selected app.</p>
|
||||||
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexwatch">PlexWatch</button>
|
<div class="row">
|
||||||
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexivity">Plexivity</button>
|
<div class="col-md-9">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="tautulli">Tautulli</button>
|
||||||
|
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexwatch">PlexWatch</button>
|
||||||
|
<button class="btn btn-form toggle-app-import-modal" type="button" data-target="#app-import-modal" data-toggle="modal" data-app="plexivity">Plexivity</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="config_import">Config Import</label>
|
||||||
|
<p class="help-block">Click the button below to import a previous Tautulli config.</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-9">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-form toggle-config-import-modal" type="button" data-target="#config-import-modal" data-toggle="modal">Tautulli</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="padded-header">
|
<div class="padded-header">
|
||||||
|
@ -1500,6 +1518,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="app-import-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="app-import-modal"></div>
|
<div id="app-import-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="app-import-modal"></div>
|
||||||
|
<div id="config-import-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="config-import-modal"></div>
|
||||||
<div id="add-notifier-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="add-notifier-modal">
|
<div id="add-notifier-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="add-notifier-modal">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
@ -2513,6 +2532,18 @@ $(document).ready(function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Load config import modal
|
||||||
|
$(".toggle-config-import-modal").click(function() {
|
||||||
|
$.ajax({
|
||||||
|
url: 'import_config_tool',
|
||||||
|
cache: false,
|
||||||
|
async: true,
|
||||||
|
complete: function(xhr, status) {
|
||||||
|
$("#config-import-modal").html(xhr.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
pms_version = false;
|
pms_version = false;
|
||||||
pms_logs_debug = false;
|
pms_logs_debug = false;
|
||||||
pms_logs = false;
|
pms_logs = false;
|
||||||
|
|
|
@ -22,6 +22,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
from configobj import ConfigObj
|
from configobj import ConfigObj
|
||||||
|
|
||||||
|
@ -202,6 +203,7 @@ _DO_NOT_IMPORT_KEYS_DOCKER = [
|
||||||
]
|
]
|
||||||
|
|
||||||
IS_IMPORTING = False
|
IS_IMPORTING = False
|
||||||
|
IMPORT_THREAD = None
|
||||||
|
|
||||||
|
|
||||||
def set_is_importing(value):
|
def set_is_importing(value):
|
||||||
|
@ -209,6 +211,15 @@ def set_is_importing(value):
|
||||||
IS_IMPORTING = value
|
IS_IMPORTING = value
|
||||||
|
|
||||||
|
|
||||||
|
def set_import_thread(config=None, backup=False):
|
||||||
|
global IMPORT_THREAD
|
||||||
|
if config:
|
||||||
|
IMPORT_THREAD = threading.Thread(target=import_tautulli_config,
|
||||||
|
kwargs={'config': config, 'backup': backup})
|
||||||
|
else:
|
||||||
|
IMPORT_THREAD = None
|
||||||
|
|
||||||
|
|
||||||
def import_tautulli_config(config=None, backup=False):
|
def import_tautulli_config(config=None, backup=False):
|
||||||
if backup:
|
if backup:
|
||||||
# Make a backup of the current config first
|
# Make a backup of the current config first
|
||||||
|
@ -235,6 +246,7 @@ def import_tautulli_config(config=None, backup=False):
|
||||||
plexpy.CONFIG.write()
|
plexpy.CONFIG.write()
|
||||||
|
|
||||||
logger.info("Tautulli Config :: Tautulli config import complete.")
|
logger.info("Tautulli Config :: Tautulli config import complete.")
|
||||||
|
set_import_thread(None)
|
||||||
set_is_importing(False)
|
set_is_importing(False)
|
||||||
|
|
||||||
# Restart to apply changes
|
# Restart to apply changes
|
||||||
|
|
|
@ -3876,9 +3876,8 @@ class WebInterface(object):
|
||||||
if not config_path:
|
if not config_path:
|
||||||
return {'result': 'error', 'message': 'No config specified for import'}
|
return {'result': 'error', 'message': 'No config specified for import'}
|
||||||
|
|
||||||
threading.Thread(target=config.import_tautulli_config,
|
config.set_import_thread(config=config_path, backup=helpers.bool_true(backup))
|
||||||
kwargs={'config': config_path,
|
|
||||||
'backup': helpers.bool_true(backup)}).start()
|
|
||||||
return {'result': 'success',
|
return {'result': 'success',
|
||||||
'message': 'Config import has started. Check the logs to monitor any problems. '
|
'message': 'Config import has started. Check the logs to monitor any problems. '
|
||||||
'Tautulli will restart automatically.'}
|
'Tautulli will restart automatically.'}
|
||||||
|
@ -3896,6 +3895,11 @@ class WebInterface(object):
|
||||||
logger.warn("No app specified for import.")
|
logger.warn("No app specified for import.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
@requireAuth(member_of("admin"))
|
||||||
|
def import_config_tool(self, **kwargs):
|
||||||
|
return serve_template(templatename="config_import.html", title="Import Tautulli Config")
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@cherrypy.tools.json_out()
|
@cherrypy.tools.json_out()
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
|
@ -4158,7 +4162,8 @@ class WebInterface(object):
|
||||||
def do_state_change(self, signal, title, timer, **kwargs):
|
def do_state_change(self, signal, title, timer, **kwargs):
|
||||||
message = title
|
message = title
|
||||||
quote = self.random_arnold_quotes()
|
quote = self.random_arnold_quotes()
|
||||||
plexpy.SIGNAL = signal
|
if signal:
|
||||||
|
plexpy.SIGNAL = signal
|
||||||
|
|
||||||
if plexpy.CONFIG.HTTP_ROOT.strip('/'):
|
if plexpy.CONFIG.HTTP_ROOT.strip('/'):
|
||||||
new_http_root = '/' + plexpy.CONFIG.HTTP_ROOT.strip('/') + '/'
|
new_http_root = '/' + plexpy.CONFIG.HTTP_ROOT.strip('/') + '/'
|
||||||
|
@ -4207,6 +4212,13 @@ class WebInterface(object):
|
||||||
def reset_git_install(self, **kwargs):
|
def reset_git_install(self, **kwargs):
|
||||||
return self.do_state_change('reset', 'Resetting to {}'.format(common.RELEASE), 120)
|
return self.do_state_change('reset', 'Resetting to {}'.format(common.RELEASE), 120)
|
||||||
|
|
||||||
|
@cherrypy.expose
|
||||||
|
@requireAuth(member_of("admin"))
|
||||||
|
def restart_import_config(self, **kwargs):
|
||||||
|
if config.IMPORT_THREAD:
|
||||||
|
config.IMPORT_THREAD.start()
|
||||||
|
return self.do_state_change(None, 'Importing a Config', 15)
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
@requireAuth(member_of("admin"))
|
@requireAuth(member_of("admin"))
|
||||||
def get_changelog(self, latest_only=False, since_prev_release=False, update_shown=False, **kwargs):
|
def get_changelog(self, latest_only=False, since_prev_release=False, update_shown=False, **kwargs):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue