Skip to content

Commit 8733abd

Browse files
committed
🏷️ v1.8.3, Fixes #5, FIXED DOWNLOADS,FAVORITES.
1 parent e2d467c commit 8733abd

10 files changed

Lines changed: 799 additions & 179 deletions

File tree

src/api.py

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import json
12
import re
23
import threading
4+
from pathlib import Path
35
from typing import Dict, List, Optional
46

57
import requests
68
from .models import AnimeResult, Episode
9+
from .storage import atomic_write_json
710

811
# Default credentials - can be overridden with environment variables
912
# This is for analytics and also api credentials fetching.
@@ -19,8 +22,59 @@ def _get_endpoint_config() -> tuple[str, str]:
1922

2023

2124
class APICache:
25+
CACHE_FILENAME = "api_credentials.json"
26+
27+
def __init__(self):
28+
home_dir = Path.home()
29+
db_dir = home_dir / ".ani-cli-arabic" / "database"
30+
db_dir.mkdir(parents=True, exist_ok=True)
31+
self.cache_file = db_dir / self.CACHE_FILENAME
32+
33+
@staticmethod
34+
def _default_keys() -> dict:
35+
return {
36+
'ANI_CLI_AR_API_BASE': '',
37+
'ANI_CLI_AR_TOKEN': '',
38+
'THUMBNAILS_BASE_URL': '',
39+
'TRAILERS_BASE_URL': ''
40+
}
41+
42+
@staticmethod
43+
def _normalize_keys(data: dict) -> dict:
44+
defaults = APICache._default_keys()
45+
if not isinstance(data, dict):
46+
return defaults
47+
return {key: str(data.get(key, defaults[key]) or '') for key in defaults}
48+
49+
def _load_cached_keys(self) -> Optional[dict]:
50+
if not self.cache_file.exists():
51+
return None
52+
53+
try:
54+
with open(self.cache_file, 'r', encoding='utf-8') as cache_handle:
55+
cached = json.load(cache_handle)
56+
57+
normalized = self._normalize_keys(cached)
58+
if normalized['ANI_CLI_AR_API_BASE'] and normalized['ANI_CLI_AR_TOKEN']:
59+
return normalized
60+
except (json.JSONDecodeError, OSError, IOError, ValueError, TypeError):
61+
return None
62+
63+
return None
64+
65+
def _save_cached_keys(self, keys: dict) -> None:
66+
normalized = self._normalize_keys(keys)
67+
if not normalized['ANI_CLI_AR_API_BASE'] or not normalized['ANI_CLI_AR_TOKEN']:
68+
return
69+
70+
try:
71+
atomic_write_json(self.cache_file, normalized, indent=2, ensure_ascii=False)
72+
except OSError:
73+
pass
74+
2275
def _fetch_from_remote(self) -> dict:
2376
endpoint_url, auth_secret = _get_endpoint_config()
77+
cached = self._load_cached_keys()
2478

2579
try:
2680
response = requests.get(
@@ -32,22 +86,17 @@ def _fetch_from_remote(self) -> dict:
3286
timeout=10
3387
)
3488

35-
if response.status_code == 200:
36-
return response.json()
37-
else:
38-
return {
39-
'ANI_CLI_AR_API_BASE': '',
40-
'ANI_CLI_AR_TOKEN': '',
41-
'THUMBNAILS_BASE_URL': '',
42-
'TRAILERS_BASE_URL': ''
43-
}
44-
except Exception:
45-
return {
46-
'ANI_CLI_AR_API_BASE': '',
47-
'ANI_CLI_AR_TOKEN': '',
48-
'THUMBNAILS_BASE_URL': '',
49-
'TRAILERS_BASE_URL': ''
50-
}
89+
response.raise_for_status()
90+
remote_keys = self._normalize_keys(response.json())
91+
if remote_keys['ANI_CLI_AR_API_BASE'] and remote_keys['ANI_CLI_AR_TOKEN']:
92+
self._save_cached_keys(remote_keys)
93+
return remote_keys
94+
except (requests.RequestException, ValueError, TypeError):
95+
pass
96+
97+
if cached:
98+
return cached
99+
return self._default_keys()
51100

52101
def get_keys(self) -> dict:
53102
return self._fetch_from_remote()
@@ -78,15 +127,15 @@ def _ensure_creds():
78127

79128
def get_api_base():
80129
_ensure_creds()
81-
return _creds['ANI_CLI_AR_API_BASE']
130+
return _creds.get('ANI_CLI_AR_API_BASE', '')
82131

83132
def get_api_token():
84133
_ensure_creds()
85-
return _creds['ANI_CLI_AR_TOKEN']
134+
return _creds.get('ANI_CLI_AR_TOKEN', '')
86135

87136
def get_thumbnails_base():
88137
_ensure_creds()
89-
return _creds['THUMBNAILS_BASE_URL']
138+
return _creds.get('THUMBNAILS_BASE_URL', '')
90139

91140
def get_trailers_base():
92141
_ensure_creds()

0 commit comments

Comments
 (0)