EchoReader / database.py
Kostya-Lolinger's picture
Upload 9 files
ca8847b verified
Raw History Blame Contribute Delete
6.85 kB
import json
import os
import shutil
import bcrypt
from datetime import datetime
DATA_DIR = "user_data"
def _ensure_dirs():
os.makedirs(DATA_DIR, exist_ok=True)
def _user_dir(username: str) -> str:
path = os.path.join(DATA_DIR, username)
os.makedirs(path, exist_ok=True)
return path
def _voices_dir(username: str) -> str:
path = os.path.join(_user_dir(username), "voices")
os.makedirs(path, exist_ok=True)
return path
def _files_dir(username: str) -> str:
path = os.path.join(_user_dir(username), "files")
os.makedirs(path, exist_ok=True)
return path
def _users_file() -> str:
_ensure_dirs()
return os.path.join(DATA_DIR, "users.json")
def _load_users() -> dict:
path = _users_file()
if os.path.exists(path):
with open(path, "r") as f:
return json.load(f)
return {}
def _save_users(users: dict):
with open(_users_file(), "w") as f:
json.dump(users, f, indent=2)
def register_user(username: str, password: str) -> tuple[bool, str]:
if not username or not password:
return False, "Username and password required"
if len(username) < 3:
return False, "Username must be at least 3 characters"
if len(password) < 6:
return False, "Password must be at least 6 characters"
users = _load_users()
if username.lower() in users:
return False, "user_exists"
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
users[username.lower()] = {
"password_hash": hashed,
"created": datetime.now().isoformat(),
"settings": {"app_language": "en", "tts_language": "en", "speed": 1.0},
}
_save_users(users)
_user_dir(username.lower())
_voices_dir(username.lower())
_files_dir(username.lower())
return True, "register_success"
def verify_user(username: str, password: str) -> bool:
users = _load_users()
user = users.get(username.lower())
if not user:
return False
return bcrypt.checkpw(password.encode(), user["password_hash"].encode())
def delete_user(username: str, password: str) -> bool:
if not verify_user(username, password):
return False
users = _load_users()
username = username.lower()
if username in users:
del users[username]
_save_users(users)
user_path = os.path.join(DATA_DIR, username)
if os.path.exists(user_path):
shutil.rmtree(user_path)
return True
def get_user_settings(username: str) -> dict:
users = _load_users()
user = users.get(username.lower(), {})
return user.get("settings", {"app_language": "en", "tts_language": "en", "speed": 1.0})
def update_user_settings(username: str, settings: dict):
users = _load_users()
username = username.lower()
if username in users:
users[username]["settings"] = settings
_save_users(users)
def save_user_file(username: str, filename: str, content: str) -> str:
files_dir = _files_dir(username.lower())
meta_path = os.path.join(files_dir, "files_meta.json")
if os.path.exists(meta_path):
with open(meta_path, "r") as f:
meta = json.load(f)
else:
meta = {}
safe_name = "".join(c for c in filename if c.isalnum() or c in "._- ").strip()
file_id = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{safe_name}"
content_path = os.path.join(files_dir, f"{file_id}.txt")
with open(content_path, "w", encoding="utf-8") as f:
f.write(content)
meta[file_id] = {
"original_name": filename,
"saved_at": datetime.now().isoformat(),
"char_count": len(content),
}
with open(meta_path, "w") as f:
json.dump(meta, f, indent=2)
return file_id
def get_user_files(username: str) -> dict:
files_dir = _files_dir(username.lower())
meta_path = os.path.join(files_dir, "files_meta.json")
if os.path.exists(meta_path):
with open(meta_path, "r") as f:
return json.load(f)
return {}
def load_file_content(username: str, file_id: str) -> str:
files_dir = _files_dir(username.lower())
content_path = os.path.join(files_dir, f"{file_id}.txt")
if os.path.exists(content_path):
with open(content_path, "r", encoding="utf-8") as f:
return f.read()
return ""
def delete_user_file(username: str, file_id: str) -> bool:
files_dir = _files_dir(username.lower())
meta_path = os.path.join(files_dir, "files_meta.json")
content_path = os.path.join(files_dir, f"{file_id}.txt")
if os.path.exists(meta_path):
with open(meta_path, "r") as f:
meta = json.load(f)
if file_id in meta:
del meta[file_id]
with open(meta_path, "w") as f:
json.dump(meta, f, indent=2)
if os.path.exists(content_path):
os.remove(content_path)
return True
return False
def save_cloned_voice(username: str, voice_name: str, audio_path: str) -> str:
voices_dir = _voices_dir(username.lower())
meta_path = os.path.join(voices_dir, "voices_meta.json")
if os.path.exists(meta_path):
with open(meta_path, "r") as f:
meta = json.load(f)
else:
meta = {}
voice_id = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{voice_name}"
ext = os.path.splitext(audio_path)[1]
voice_file = os.path.join(voices_dir, f"{voice_id}{ext}")
shutil.copy2(audio_path, voice_file)
meta[voice_id] = {
"name": voice_name,
"file": f"{voice_id}{ext}",
"created": datetime.now().isoformat(),
}
with open(meta_path, "w") as f:
json.dump(meta, f, indent=2)
return voice_id
def get_user_voices(username: str) -> dict:
voices_dir = _voices_dir(username.lower())
meta_path = os.path.join(voices_dir, "voices_meta.json")
if os.path.exists(meta_path):
with open(meta_path, "r") as f:
return json.load(f)
return {}
def get_voice_path(username: str, voice_id: str) -> str:
voices = get_user_voices(username.lower())
if voice_id in voices:
voices_dir = _voices_dir(username.lower())
return os.path.join(voices_dir, voices[voice_id]["file"])
return ""
def delete_user_voice(username: str, voice_id: str) -> bool:
voices_dir = _voices_dir(username.lower())
meta_path = os.path.join(voices_dir, "voices_meta.json")
if os.path.exists(meta_path):
with open(meta_path, "r") as f:
meta = json.load(f)
if voice_id in meta:
voice_file = os.path.join(voices_dir, meta[voice_id]["file"])
if os.path.exists(voice_file):
os.remove(voice_file)
del meta[voice_id]
with open(meta_path, "w") as f:
json.dump(meta, f, indent=2)
return True
return False