mirror of
https://github.com/shadoll/moma.git
synced 2026-08-28 03:27:34 +00:00
feat: Refactor TMDB API key and access token handling; update settings screen for user input and remove hardcoded secrets
This commit is contained in:
@@ -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
@@ -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.
|
||||||
|
|||||||
@@ -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
@@ -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}")
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user