Add Dockerfile, compose file and Gitea Actions workflow
build / check (push) Has been cancelled
build / image (push) Has been cancelled

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-14 15:29:44 -04:00
1 parent 8341c25846
commit b0480c8e56
5 files changed
+104 -12

No files matched your search

+19 -12
View File
@@ -29,11 +29,12 @@ from http.cookies import SimpleCookie
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
HERE = os.path.dirname(os.path.abspath(__file__))
F_CONFIG = os.path.join(HERE, "config.json") # OpenAudible login (chmod 600)
F_USERS = os.path.join(HERE, "users.json") # name -> salted scrypt hash
F_SESSIONS = os.path.join(HERE, "sessions.json") # token hash -> {user, exp}
F_PROGRESS = os.path.join(HERE, "progress_users.json")
F_LEGACY = os.path.join(HERE, "progress.json") # single-user file from the first version
DATA = os.environ.get("DATA_DIR", HERE) # where accounts/progress live (a volume in Docker)
F_CONFIG = os.path.join(DATA, "config.json") # OpenAudible login (chmod 600)
F_USERS = os.path.join(DATA, "users.json") # name -> salted scrypt hash
F_SESSIONS = os.path.join(DATA, "sessions.json") # token hash -> {user, exp}
F_PROGRESS = os.path.join(DATA, "progress_users.json")
F_LEGACY = os.path.join(DATA, "progress.json") # single-user file from the first version
COOKIE = "oa_session"
SESSION_DAYS = 90
@@ -79,6 +80,12 @@ def check_pw(password, rec):
return hmac.compare_digest(h, rec["hash"])
def oa_login():
"""OpenAudible credentials: env vars win over config.json (handy for containers)."""
cfg = read(F_CONFIG, {})
return os.environ.get("OPENAUDIBLE_USER") or cfg.get("user"), os.environ.get("OPENAUDIBLE_PASSWORD") or cfg.get("password", "")
def sha(token):
return hashlib.sha256(token.encode()).hexdigest()
@@ -272,10 +279,10 @@ class Handler(BaseHTTPRequestHandler):
self.end_headers()
def proxy(self):
cfg = read(F_CONFIG, {})
oa_user, oa_pass = oa_login()
req = urllib.request.Request(self.upstream + self.path)
if cfg.get("user"):
cred = base64.b64encode(f"{cfg['user']}:{cfg.get('password', '')}".encode()).decode()
if oa_user:
cred = base64.b64encode(f"{oa_user}:{oa_pass}".encode()).decode()
req.add_header("Authorization", "Basic " + cred)
for h in PASS_HEADERS:
if self.headers.get(h):
@@ -306,14 +313,14 @@ class Handler(BaseHTTPRequestHandler):
def serve(argv):
ap = argparse.ArgumentParser()
ap.add_argument("--upstream", default="http://172.16.16.106:8000")
ap.add_argument("--port", type=int, default=8080)
ap.add_argument("--upstream", default=os.environ.get("OPENAUDIBLE_URL", "http://172.16.16.106:8000"))
ap.add_argument("--port", type=int, default=int(os.environ.get("PORT", 8080)))
ap.add_argument("--host", default="0.0.0.0")
a = ap.parse_args(argv)
if not read(F_USERS, {}):
sys.exit("No accounts yet. Run: python3 server.py adduser YOURNAME")
if not read(F_CONFIG, {}).get("user"):
print("Note: no OpenAudible login saved. Run `python3 server.py setup` first.")
if not oa_login()[0]:
print("Note: no OpenAudible login set. Run `python3 server.py setup` or set OPENAUDIBLE_USER/OPENAUDIBLE_PASSWORD.")
Handler.upstream = a.upstream.rstrip("/")
print(f"Player: http://localhost:{a.port} (OpenAudible at {Handler.upstream})")
ThreadingHTTPServer((a.host, a.port), Handler).serve_forever()