feat: Refactor TMDB API key and access token handling; update settings screen for user input and remove hardcoded secrets

This commit is contained in:
sha
2026-04-12 11:57:14 +03:00
parent 1b50d15eea
commit cff973dd94
5 changed files with 40 additions and 10 deletions
+2
View File
@@ -8,6 +8,8 @@ wheels/
# Virtual environments # Virtual environments
.venv .venv
venv/ venv/
# Secrets
src/secrets.py
# Test-generated files # Test-generated files
src/test/datasets/sample_mediafiles/ src/test/datasets/sample_mediafiles/
.pytest_cache/ .pytest_cache/
+5 -1
View File
@@ -11,7 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- **GitHub Actions CI** (`ci.yml`): runs tests + mypy on Python 3.11 and 3.12 for every push to `main` and all PRs - **GitHub Actions CI** (`ci.yml`): runs tests + mypy on Python 3.11 and 3.12 for every push to `main` and all PRs
- **GitHub Actions Release** (`release.yml`): triggered by `v*.*.*` tags — runs tests, builds wheel + tarball, publishes packages as GitHub Release assets - **GitHub Actions Release** (`release.yml`): triggered by `v*.*.*` tags — runs tests, builds wheel + tarball, publishes packages as GitHub Release assets (`moma-X.Y.Z.whl`, `moma-latest.whl`, etc.)
- **TMDB credentials in Settings**: `tmdb_api_key` and `tmdb_access_token` are now stored in `~/.config/moma/config.json` and editable via the Settings screen (`p`)
### Changed
- Removed hardcoded TMDB API keys from `src/secrets.py`; `secrets.py` is now gitignored
### Future Plans ### Future Plans
See [docs/REFACTORING_PROGRESS.md](docs/REFACTORING_PROGRESS.md) and [docs/ToDo.md](docs/ToDo.md) for upcoming features and improvements. See [docs/REFACTORING_PROGRESS.md](docs/REFACTORING_PROGRESS.md) and [docs/ToDo.md](docs/ToDo.md) for upcoming features and improvements.
+11 -3
View File
@@ -6,7 +6,6 @@ import requests
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Dict, Optional, Tuple, Any from typing import Dict, Optional, Tuple, Any
from ..secrets import TMDB_API_KEY, TMDB_ACCESS_TOKEN
from ..cache import Cache from ..cache import Cache
from ..settings import Settings from ..settings import Settings
@@ -38,14 +37,23 @@ class TMDBExtractor:
base_url = "https://api.themoviedb.org/3" base_url = "https://api.themoviedb.org/3"
url = f"{base_url}{endpoint}" url = f"{base_url}{endpoint}"
api_key = self.settings.get("tmdb_api_key", "")
access_token = self.settings.get("tmdb_access_token", "")
if not api_key and not access_token:
logging.warning("TMDB API key and access token are not configured")
return None
headers = { headers = {
"Authorization": f"Bearer {TMDB_ACCESS_TOKEN}",
"accept": "application/json" "accept": "application/json"
} }
if access_token:
headers["Authorization"] = f"Bearer {access_token}"
if params is None: if params is None:
params = {} params = {}
params['api_key'] = TMDB_API_KEY if api_key:
params['api_key'] = api_key
try: try:
response = requests.get(url, headers=headers, params=params, timeout=10) response = requests.get(url, headers=headers, params=params, timeout=10)
+9 -4
View File
@@ -16,6 +16,8 @@ class Settings:
"cache_ttl_extractors": 21600, # 6 hours in seconds "cache_ttl_extractors": 21600, # 6 hours in seconds
"cache_ttl_tmdb": 21600, # 6 hours in seconds "cache_ttl_tmdb": 21600, # 6 hours in seconds
"cache_ttl_posters": 2592000, # 30 days in seconds "cache_ttl_posters": 2592000, # 30 days in seconds
"tmdb_api_key": "", # TMDB API key
"tmdb_access_token": "", # TMDB Bearer access token
} }
_instance: Optional['Settings'] = None _instance: Optional['Settings'] = None
@@ -80,12 +82,15 @@ class Settings:
def set(self, key: str, value: Any) -> None: def set(self, key: str, value: Any) -> None:
"""Set a setting value and save.""" """Set a setting value and save."""
if key in self.DEFAULTS: if key in self.DEFAULTS:
# Basic type checking # Basic type checking (both empty string and non-empty string are valid for str defaults)
if isinstance(value, type(self.DEFAULTS[key])): default = self.DEFAULTS[key]
if isinstance(default, bool):
if not isinstance(value, bool):
raise ValueError(f"Invalid type for setting {key}")
elif not isinstance(value, type(default)):
raise ValueError(f"Invalid type for setting {key}")
self._settings[key] = value self._settings[key] = value
self.save() self.save()
else:
raise ValueError(f"Invalid type for setting {key}")
else: else:
raise KeyError(f"Unknown setting: {key}") raise KeyError(f"Unknown setting: {key}")
+11
View File
@@ -78,6 +78,12 @@ Configure application settings.
yield Static("Cache TTL - Posters (days):", classes="label") yield Static("Cache TTL - Posters (days):", classes="label")
yield Input(value=str(settings.get("cache_ttl_posters") // 86400), id="ttl_posters", classes="input_field") yield Input(value=str(settings.get("cache_ttl_posters") // 86400), id="ttl_posters", classes="input_field")
yield Static("TMDB API Key:", classes="label")
yield Input(value=settings.get("tmdb_api_key", ""), id="tmdb_api_key", password=False, classes="input_field")
yield Static("TMDB Access Token (Bearer):", classes="label")
yield Input(value=settings.get("tmdb_access_token", ""), id="tmdb_access_token", password=True, classes="input_field")
with Horizontal(id="buttons"): with Horizontal(id="buttons"):
yield Button("Save", id="save") yield Button("Save", id="save")
yield Button("Cancel", id="cancel") yield Button("Cancel", id="cancel")
@@ -146,6 +152,11 @@ Configure application settings.
self.app.settings.set("cache_ttl_tmdb", ttl_tmdb) # type: ignore self.app.settings.set("cache_ttl_tmdb", ttl_tmdb) # type: ignore
self.app.settings.set("cache_ttl_posters", ttl_posters) # type: ignore self.app.settings.set("cache_ttl_posters", ttl_posters) # type: ignore
tmdb_api_key = self.query_one("#tmdb_api_key", Input).value.strip()
tmdb_access_token = self.query_one("#tmdb_access_token", Input).value.strip()
self.app.settings.set("tmdb_api_key", tmdb_api_key) # type: ignore
self.app.settings.set("tmdb_access_token", tmdb_access_token) # type: ignore
self.app.notify("Settings saved!", severity="information", timeout=2) # type: ignore self.app.notify("Settings saved!", severity="information", timeout=2) # type: ignore
except ValueError: except ValueError:
self.app.notify("Invalid TTL values. Please enter numbers only.", severity="error", timeout=3) # type: ignore self.app.notify("Invalid TTL values. Please enter numbers only.", severity="error", timeout=3) # type: ignore