Skip to content

Commit efdd271

Browse files
authored
Merge pull request #145 from TroyWarez/5.3-develop
Split the settings database data into a new file and added a more detailed settings export file name, using the tab title.
2 parents 7e8e845 + 8fa87fe commit efdd271

2 files changed

Lines changed: 116 additions & 11 deletions

File tree

obplayer/data.py

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import re
2929
import traceback
3030
import subprocess
31+
import json
3132

3233

3334
class ObData(object):
@@ -96,6 +97,14 @@ def __init__(self):
9697
def open_db(self, filename):
9798
return apsw.Connection(filename)
9899

100+
def get_password_suffixes(self):
101+
# Update here to add more password suffixes
102+
return [
103+
"_password",
104+
"_access_key",
105+
"_access_key_id",
106+
]
107+
99108
def table_exists(self, table):
100109
for row in self.execute(
101110
"SELECT name FROM sqlite_master WHERE type IN ('table','view') AND name = ? UNION ALL SELECT name FROM sqlite_temp_master WHERE type IN ('table','view') AND name = ?",
@@ -158,6 +167,8 @@ def __init__(self):
158167
self.headless = False
159168
self.args = None
160169
self.version = open("VERSION").read().strip()
170+
self.secrets_file = self.datadir + "/.secrets.json"
171+
161172
srcpath = os.path.dirname(os.path.dirname(obplayer.__file__))
162173
branch = subprocess.Popen(
163174
'cd "{0}" && git branch'.format(srcpath), stdout=subprocess.PIPE, shell=True
@@ -183,10 +194,16 @@ def __init__(self):
183194
self.settings_cache = {}
184195
self.settings_type = {}
185196

197+
# Load secrets from JSON
198+
secrets = self.load_secrets_file()
199+
secrets_migrated = False
200+
186201
rows = self.query("SELECT name,value,type FROM 'settings'")
187202
for row in rows:
203+
name = row["name"]
188204
value = row["value"]
189205
datatype = row["type"]
206+
190207
if datatype == "int":
191208
value = int(value)
192209
elif datatype == "float":
@@ -195,8 +212,39 @@ def __init__(self):
195212
value = bool(int(value))
196213
else:
197214
value = str(value)
198-
self.settings_cache[row["name"]] = value
199-
self.settings_type[row["name"]] = datatype
215+
216+
# --- Migration Logic ---
217+
# If this is a password field, check if it is real data in DB.
218+
# If so, move to secrets and scrub DB.
219+
password_suffixes = self.get_password_suffixes()
220+
is_password_field = any(
221+
name.endswith(suffix) for suffix in password_suffixes
222+
)
223+
224+
if is_password_field:
225+
# If value is not the placeholder, we need to migrate it
226+
if value != "__SECRET__" and value != "":
227+
secrets[name] = value
228+
value = "__SECRET__" # Scrub local variable
229+
# Update DB to placeholder immediately
230+
self.execute(
231+
'UPDATE settings set value="__SECRET__" where name="'
232+
+ self.escape(name)
233+
+ '"'
234+
)
235+
secrets_migrated = True
236+
237+
# If the secret exists in the JSON file, override the DB value (which should be placeholder)
238+
if name in secrets:
239+
value = secrets[name]
240+
# -----------------------
241+
242+
self.settings_cache[name] = value
243+
self.settings_type[name] = datatype
244+
245+
# If we migrated old DB passwords to JSON, save the JSON file now
246+
if secrets_migrated:
247+
self.write_secrets_file(secrets)
200248

201249
# keep track of settings as they have been edited.
202250
# they don't take effect until restart, but we want to keep track of them for subsequent edits.
@@ -205,6 +253,25 @@ def __init__(self):
205253
if not self.setting("video_out_enable"):
206254
self.headless = True
207255

256+
def load_secrets_file(self):
257+
if os.path.exists(self.secrets_file):
258+
try:
259+
with open(self.secrets_file, "r") as f:
260+
return json.load(f)
261+
except (IOError, ValueError):
262+
# File missing or corrupt JSON
263+
return {}
264+
return {}
265+
266+
def write_secrets_file(self, secrets_data):
267+
try:
268+
# Set permissions so only owner can read/write (600)
269+
with open(self.secrets_file, "w") as f:
270+
json.dump(secrets_data, f, indent=4, sort_keys=True)
271+
os.chmod(self.secrets_file, 0o600)
272+
except IOError:
273+
print("Error writing to secrets file: " + self.secrets_file)
274+
208275
def validate_settings(self, settings):
209276
for setting_name, setting_value in settings.items():
210277
error = self.validate_setting(setting_name, setting_value, settings)
@@ -713,9 +780,23 @@ def add_setting(self, name, value, datatype=None):
713780
if len(check_setting):
714781
return
715782

783+
# If this is a default setting being added, and it's a password,
784+
# we should put the actual value in secrets.json and a placeholder in DB
785+
password_suffixes = self.get_password_suffixes()
786+
is_password_field = any(name.endswith(suffix) for suffix in password_suffixes)
787+
788+
db_value = value
789+
if is_password_field and value:
790+
secrets = self.load_secrets_file()
791+
# Only write to secrets if not already there (prevents overwriting existing user secrets with defaults)
792+
if name not in secrets:
793+
secrets[name] = value
794+
self.write_secrets_file(secrets)
795+
db_value = "__SECRET__"
796+
716797
data = {}
717798
data["name"] = name
718-
data["value"] = value
799+
data["value"] = db_value
719800

720801
if datatype != None:
721802
data["type"] = datatype
@@ -731,6 +812,11 @@ def setting(self, name, use_edit_cache=False):
731812

732813
# save our settings into the database. update settings_edit_cache to handle subsequent edits.
733814
def save_settings(self, settings):
815+
816+
secrets = self.load_secrets_file()
817+
secrets_updated = False
818+
password_suffixes = self.get_password_suffixes()
819+
734820
for name, value in settings.items():
735821
dataType = self.settings_type[name]
736822
if dataType == "int":
@@ -742,22 +828,36 @@ def save_settings(self, settings):
742828
else:
743829
self.settings_edit_cache[name] = str(value)
744830

831+
# Check if this is a secret
832+
is_password_field = any(
833+
name.endswith(suffix) for suffix in password_suffixes
834+
)
835+
836+
db_value = value
837+
if is_password_field:
838+
secrets[name] = str(value)
839+
secrets_updated = True
840+
db_value = "__SECRET__"
841+
745842
self.query(
746843
'UPDATE settings set value="'
747-
+ self.escape(str(value))
844+
+ self.escape(str(db_value))
748845
+ '" where name="'
749846
+ self.escape(name)
750847
+ '"'
751848
)
752849

850+
if secrets_updated:
851+
self.write_secrets_file(secrets)
852+
753853
def list_settings(self, hidepasswords=False):
754854
result = {}
755855
for name, value in self.settings_cache.items():
756-
if (
757-
not hidepasswords
758-
or not name.endswith("_password")
759-
and not name.endswith("_access_key")
760-
and not name.endswith("_access_key_id")
761-
):
856+
password_suffixes = self.get_password_suffixes()
857+
is_password_field = any(
858+
name.endswith(suffix) for suffix in password_suffixes
859+
)
860+
861+
if not hidepasswords or not is_password_field:
762862
result[name] = value
763863
return result

obplayer/httpadmin/httpadmin.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,12 @@ def req_export(self, request):
493493
)
494494

495495
res = httpserver.Response()
496-
res.add_header("Content-Disposition", "attachment; filename=obsettings.txt")
496+
res.add_header(
497+
"Content-Disposition",
498+
"attachment; filename="
499+
+ re.sub(r"[^\w\d_-]", "_", self.title)
500+
+ " dashboard_settings.txt",
501+
)
497502
res.send_content("text/plain", settings)
498503
return res
499504

0 commit comments

Comments
 (0)