Finalize fixes
This commit is contained in:
1 parent
b3e3a97c2b
commit
1f216a8e8d
2 files changed
+120
-14
No files matched your search
@@ -113,21 +113,33 @@ def index():
|
||||
def report():
|
||||
week_start, week_end, _ = current_week() # This is the week range for display (e.g., Feb 15-21)
|
||||
total_episodes = int(os.environ.get("TOTAL_EPISODES", 1155))
|
||||
latest = get_latest_sunday() # This is the cutoff for searching (e.g., Feb 21 16:00 UTC)
|
||||
previous = get_previous_sunday()
|
||||
latest = get_latest_sunday() # e.g., 2026-02-21T16:00:00.000Z
|
||||
previous = get_previous_sunday() # e.g., 2026-02-14T16:00:00.000Z
|
||||
|
||||
jellyfin.clear_debug_logs()
|
||||
|
||||
# Convert latest_sunday string back to datetime for cutoff
|
||||
# Convert to datetime for cutoff
|
||||
latest_sunday_dt = datetime.fromisoformat(latest.replace("Z", "+00:00"))
|
||||
previous_sunday_dt = datetime.fromisoformat(previous.replace("Z", "+00:00"))
|
||||
user_id = os.environ["JELLYFIN_USER_ID"]
|
||||
|
||||
# Fetch latest episode (watched before latest Sunday)
|
||||
try:
|
||||
ep_info = jellyfin.find_last_one_piece_episode(latest_sunday_dt, user_id)
|
||||
except Exception as e:
|
||||
debug_output = jellyfin.get_debug_logs()
|
||||
return jsonify({"error": str(e), "debug": debug_output}), 502
|
||||
|
||||
# Fetch previous week episode (watched before previous Sunday)
|
||||
try:
|
||||
prev_ep_info = jellyfin.find_last_one_piece_before_previous_sunday(previous_sunday_dt, user_id)
|
||||
previous_episode = prev_ep_info["episode_number"]
|
||||
except Exception as e:
|
||||
_debug.add(f"✗ Could not find previous Sunday episode: {e}")
|
||||
_debug.add("✓ Falling back to DB seed or latest episode")
|
||||
prev = db.get_previous_episode(week_start.isoformat())
|
||||
previous_episode = prev["episode_number"] if prev else ep_info["episode_number"]
|
||||
|
||||
db.save_episode(
|
||||
episode_number=ep_info["episode_number"],
|
||||
week_start=week_start.isoformat(),
|
||||
@@ -137,8 +149,6 @@ def report():
|
||||
watched_at=ep_info.get("watched_at"),
|
||||
)
|
||||
|
||||
prev = db.get_previous_episode(week_start.isoformat())
|
||||
previous_episode = prev["episode_number"] if prev else ep_info["episode_number"]
|
||||
watched_this_week = ep_info["episode_number"] - previous_episode
|
||||
|
||||
date_range = format_date_range(week_start, week_end)
|
||||
|
||||
+105
-9
@@ -38,16 +38,22 @@ def clear_debug_logs():
|
||||
_debug.logs = []
|
||||
|
||||
|
||||
def get_activity_logs(min_date: datetime) -> list:
|
||||
"""Return activity log entries since min_date (UTC datetime)."""
|
||||
def get_activity_logs(min_date: datetime = None, limit: int = 200) -> list:
|
||||
"""Return activity log entries. Optionally filtered by min_date.
|
||||
|
||||
Args:
|
||||
min_date: Optional minimum date filter (UTC datetime)
|
||||
limit: Number of entries to fetch (default 200)
|
||||
"""
|
||||
c = _cfg()
|
||||
url = f"{c['host']}/System/ActivityLog/Entries"
|
||||
params = {
|
||||
"api_key": c["key"],
|
||||
"hasUserId": "true",
|
||||
"limit": "200",
|
||||
"minDate": min_date.strftime("%Y-%m-%dT%H:%M:%S.000000Z"),
|
||||
"limit": str(limit),
|
||||
}
|
||||
if min_date:
|
||||
params["minDate"] = min_date.strftime("%Y-%m-%dT%H:%M:%S.000000Z")
|
||||
|
||||
curl_cmd = f"curl --location '{url}?"
|
||||
curl_params = []
|
||||
@@ -57,7 +63,7 @@ def get_activity_logs(min_date: datetime) -> list:
|
||||
curl_cmd += "' \\\n --header 'accept: application/json'"
|
||||
|
||||
_debug.add(f"GET {url}")
|
||||
_debug.add(f" params: hasUserId=true, limit=200, minDate={params['minDate']}")
|
||||
_debug.add(f" params: hasUserId=true, limit={limit}" + (f", minDate={params['minDate']}" if min_date else ""))
|
||||
_debug.add("")
|
||||
_debug.add("Curl command:")
|
||||
_debug.add(curl_cmd)
|
||||
@@ -74,6 +80,7 @@ def get_activity_logs(min_date: datetime) -> list:
|
||||
raise
|
||||
|
||||
|
||||
|
||||
def get_playback_info(item_id: str) -> dict:
|
||||
"""Return the first MediaSource dict for the given item."""
|
||||
c = _cfg()
|
||||
@@ -103,13 +110,104 @@ def extract_episode_number(name: str) -> int:
|
||||
return int(match.group(1))
|
||||
|
||||
|
||||
def _find_last_one_piece_in_entries(entries: list, cutoff_utc: datetime, user_id: str) -> dict:
|
||||
"""Extract the last One Piece episode from a list of entries, watched before cutoff_utc.
|
||||
|
||||
Returns {episode_number, item_id, watched_at, media_name} or raises RuntimeError if not found.
|
||||
"""
|
||||
candidates = []
|
||||
for entry in entries:
|
||||
if entry.get("Type") != "VideoPlaybackStopped":
|
||||
continue
|
||||
if entry.get("UserId") != user_id:
|
||||
continue
|
||||
if "has finished playing One Piece" not in entry.get("Name", ""):
|
||||
continue
|
||||
|
||||
date_str = entry.get("Date", "")
|
||||
try:
|
||||
entry_dt = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
|
||||
if entry_dt < cutoff_utc:
|
||||
candidates.append((entry_dt, entry))
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if not candidates:
|
||||
raise RuntimeError("No matching One Piece episodes found")
|
||||
|
||||
# Sort by date descending (newest first)
|
||||
candidates.sort(key=lambda x: x[0], reverse=True)
|
||||
latest_dt, latest_entry = candidates[0]
|
||||
|
||||
name = latest_entry.get("Name", "")
|
||||
date_str = latest_entry.get("Date", "")
|
||||
item_id = latest_entry.get("ItemId")
|
||||
|
||||
if not item_id:
|
||||
raise RuntimeError("Entry has no ItemId")
|
||||
|
||||
source = get_playback_info(item_id)
|
||||
ep_num = extract_episode_number(source["Name"])
|
||||
|
||||
return {
|
||||
"episode_number": ep_num,
|
||||
"item_id": item_id,
|
||||
"watched_at": date_str,
|
||||
"media_name": source["Name"],
|
||||
"name": name,
|
||||
}
|
||||
|
||||
|
||||
def find_last_one_piece_before_previous_sunday(previous_sunday_utc: datetime, user_id: str) -> dict:
|
||||
"""Find the latest One Piece episode watched BEFORE the previous Sunday.
|
||||
|
||||
Uses exponential backoff: starts with limit=500, doubles until found or limit >= 32000.
|
||||
Returns: {episode_number, item_id, watched_at, media_name}
|
||||
"""
|
||||
_debug.add(f"Searching for episode watched before previous Sunday: {previous_sunday_utc.isoformat()}")
|
||||
_debug.add(f"Using exponential backoff: 500 → 1000 → 2000 → 4000 → 8000 → 16000 → 32000")
|
||||
_debug.add("")
|
||||
|
||||
limit = 500
|
||||
max_limit = 32000
|
||||
attempt = 0
|
||||
|
||||
while limit <= max_limit:
|
||||
attempt += 1
|
||||
_debug.add(f"Attempt {attempt}: Fetching {limit} activity log entries...")
|
||||
|
||||
try:
|
||||
entries = get_activity_logs(limit=limit)
|
||||
except Exception as e:
|
||||
_debug.add(f"✗ Failed to fetch: {e}")
|
||||
raise
|
||||
|
||||
_debug.add(f"✓ Got {len(entries)} entries, searching...")
|
||||
_debug.add("")
|
||||
|
||||
try:
|
||||
result = _find_last_one_piece_in_entries(entries, previous_sunday_utc, user_id)
|
||||
_debug.add(f"✓ Found: {result['name']}")
|
||||
_debug.add(f" Watched at: {result['watched_at']}")
|
||||
_debug.add(f" Extracted episode: {result['episode_number']}")
|
||||
_debug.add("")
|
||||
return result
|
||||
except RuntimeError as e:
|
||||
_debug.add(f"✗ {e} (trying next limit...)")
|
||||
_debug.add("")
|
||||
limit *= 2
|
||||
|
||||
_debug.add(f"✗ Exceeded max limit ({max_limit}) without finding episode")
|
||||
raise RuntimeError(f"Could not find previous Sunday episode after searching up to {max_limit} entries")
|
||||
|
||||
|
||||
def find_last_one_piece_episode(cutoff_utc: datetime, user_id: str) -> dict:
|
||||
"""Find the last One Piece episode watched BEFORE cutoff_utc by the given user.
|
||||
|
||||
Filters:
|
||||
1. Entry date must be strictly before cutoff_utc
|
||||
2. UserId must match
|
||||
3. Name must start with '{user} has finished playing One Piece'
|
||||
3. Name must contain 'has finished playing One Piece'
|
||||
4. Type must be VideoPlaybackStopped
|
||||
|
||||
Returns the most recent matching entry.
|
||||
@@ -123,7 +221,7 @@ def find_last_one_piece_episode(cutoff_utc: datetime, user_id: str) -> dict:
|
||||
_debug.add("")
|
||||
|
||||
try:
|
||||
entries = get_activity_logs(min_date)
|
||||
entries = get_activity_logs(limit=200, min_date=min_date)
|
||||
except Exception as e:
|
||||
_debug.add(f"✗ Failed to fetch activity logs: {e}")
|
||||
raise
|
||||
@@ -137,8 +235,6 @@ def find_last_one_piece_episode(cutoff_utc: datetime, user_id: str) -> dict:
|
||||
continue
|
||||
if entry.get("UserId") != user_id:
|
||||
continue
|
||||
if not entry.get("Name", "").startswith(f"{user_id.split('@')[0] if '@' in user_id else 'pat'} has finished playing One Piece"):
|
||||
# Try simple pattern: contains "has finished playing One Piece"
|
||||
if "has finished playing One Piece" not in entry.get("Name", ""):
|
||||
continue
|
||||
|
||||
|
||||
Reference in new issue
Block a user