Hermes Agent commited on
Commit
5ff93ff
·
1 Parent(s): 780f7a9

fix(metadata): purge unwanted Ms.Rose & Mr.Cane and resolve Obsession to official 2026 Curry Barker release

Browse files
apex/apex_stream/tmdb_meta.py CHANGED
@@ -641,6 +641,21 @@ def best_tmdb_match(results: List[Dict], query: str, media_type: str,
641
  elif not is_indian and lang == "en" and hit_lang not in {"en", ""}:
642
  score -= 0.35
643
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
644
  if score > best_score:
645
  best, best_score = r, score
646
  return best if best_score >= threshold else None
 
641
  elif not is_indian and lang == "en" and hit_lang not in {"en", ""}:
642
  score -= 0.35
643
 
644
+ # Boost real distributed films with popularity/votes/artwork over zero-vote placeholders
645
+ try:
646
+ pop = float(r.get("popularity") or 0.0)
647
+ votes = int(r.get("vote_count") or 0)
648
+ if pop >= 5.0 or votes >= 10:
649
+ score += 0.20
650
+ if r.get("poster_path"):
651
+ score += 0.10
652
+ if r.get("backdrop_path") or r.get("overview"):
653
+ score += 0.05
654
+ if not r.get("poster_path") and votes == 0:
655
+ score -= 0.15
656
+ except (ValueError, TypeError):
657
+ pass
658
+
659
  if score > best_score:
660
  best, best_score = r, score
661
  return best if best_score >= threshold else None
apex/combined-entrypoint.sh CHANGED
@@ -56,7 +56,8 @@ restore_jellyfin_local() {
56
 
57
  # Permanently delete TV Shows/English and blacklisted demo episodes from media paths
58
  rm -rf "/data/jellyfin/media/TV Shows/English" "/opt/jellyfin-local/media/TV Shows/English" 2>/dev/null || true
59
- find /data/jellyfin/media -iname "*mot*test*servicing*" -exec rm -rf {} + 2>/dev/null || true
 
60
 
61
  # Purge old low-res Jellyfin image cache so newly fetched HD/4K artwork is displayed immediately
62
  rm -rf "$LOCAL_ROOT/cache/images"/* /data/jellyfin/cache/images/* 2>/dev/null || true
 
56
 
57
  # Permanently delete TV Shows/English and blacklisted demo episodes from media paths
58
  rm -rf "/data/jellyfin/media/TV Shows/English" "/opt/jellyfin-local/media/TV Shows/English" 2>/dev/null || true
59
+ find /data/jellyfin/media /data/movies /data/tvshows -iname "*mot*test*servicing*" -exec rm -rf {} + 2>/dev/null || true
60
+ find /data/jellyfin/media /data/movies /data/tvshows -iname "*rose*cane*" -exec rm -rf {} + 2>/dev/null || true
61
 
62
  # Purge old low-res Jellyfin image cache so newly fetched HD/4K artwork is displayed immediately
63
  rm -rf "$LOCAL_ROOT/cache/images"/* /data/jellyfin/cache/images/* 2>/dev/null || true
apex/repair_jellyfin_libraries.py CHANGED
@@ -100,11 +100,14 @@ def repair_database(db_path: str) -> bool:
100
  print(f"[REPAIR] Healed '{name}' ({row_id}): Path -> '{views_path}', Locations -> {locs}")
101
  repaired += 1
102
 
103
- # Permanently purge blacklisted demo items and invalid TV Shows/English items
104
  cur.execute(
105
  "DELETE FROM BaseItems WHERE Path LIKE '%/TV Shows/English%' "
106
  "OR Path LIKE '%Mot test servicing%' "
107
  "OR Name LIKE '%Mot test servicing%' "
 
 
 
108
  "OR (Name = 'English' AND type LIKE '%Series%')"
109
  )
110
  purged = cur.rowcount
 
100
  print(f"[REPAIR] Healed '{name}' ({row_id}): Path -> '{views_path}', Locations -> {locs}")
101
  repaired += 1
102
 
103
+ # Permanently purge blacklisted demo items, Ms.Rose & Mr.Cane, and invalid TV Shows/English items
104
  cur.execute(
105
  "DELETE FROM BaseItems WHERE Path LIKE '%/TV Shows/English%' "
106
  "OR Path LIKE '%Mot test servicing%' "
107
  "OR Name LIKE '%Mot test servicing%' "
108
+ "OR Path LIKE '%Rose%Cane%' "
109
+ "OR Name LIKE '%Rose%Cane%' "
110
+ "OR Name LIKE '%Ms.Rose%' "
111
  "OR (Name = 'English' AND type LIKE '%Series%')"
112
  )
113
  purged = cur.rowcount
apex/tests/test_tmdb_meta.py CHANGED
@@ -534,4 +534,38 @@ class TestNfoRepair:
534
  assert backdrop.exists()
535
 
536
  with Image.open(str(backdrop)) as out_img:
537
- assert out_img.size == (1920, 1080)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  assert backdrop.exists()
535
 
536
  with Image.open(str(backdrop)) as out_img:
537
+ assert out_img.size == (1920, 1080)
538
+
539
+ def test_ms_rose_mr_cane_blacklisted(self):
540
+ from tg_streamer import is_blacklisted
541
+ assert is_blacklisted("Ms.Rose & Mr.Cane") is True
542
+ assert is_blacklisted("Ms Rose & Mr Cane 2026") is True
543
+ assert is_blacklisted("Ms. Rose & Mr. Cane (2026).strm") is True
544
+ assert is_blacklisted("Mot test servicing") is True
545
+ assert is_blacklisted("Obsession (2025).strm") is False
546
+
547
+ def test_popular_movie_preferred_within_one_year_gap(self):
548
+ candidates = [
549
+ {
550
+ "id": 999999,
551
+ "title": "Obsession",
552
+ "release_date": "2025-01-01",
553
+ "popularity": 0.5,
554
+ "vote_count": 0,
555
+ "poster_path": None,
556
+ "backdrop_path": None,
557
+ },
558
+ {
559
+ "id": 1339713,
560
+ "title": "Obsession",
561
+ "release_date": "2026-05-15",
562
+ "popularity": 65.0,
563
+ "vote_count": 450,
564
+ "poster_path": "/obsession_poster.jpg",
565
+ "backdrop_path": "/obsession_backdrop.jpg",
566
+ "overview": "A man uses a mystical charm to make his crush fall in love.",
567
+ }
568
+ ]
569
+ match = best_tmdb_match(candidates, "obsession", "movie", year=2025)
570
+ assert match is not None
571
+ assert match["id"] == 1339713
apex/tg_streamer.py CHANGED
@@ -19,6 +19,7 @@ from typing import Optional
19
 
20
  _BLACKLIST_PATTERNS = [
21
  re.compile(r"mot\s*test\s*servicing", re.IGNORECASE),
 
22
  ]
23
 
24
 
@@ -1064,23 +1065,31 @@ async def _tmdb_search(media_type, query, year=None, language=None):
1064
  params = {"query": query or "", "include_adult": "false"}
1065
  if language:
1066
  params["language"] = language
 
 
1067
  if year:
 
1068
  if media_type == "tv":
1069
- params["first_air_date_year"] = int(year)
1070
  else:
1071
- params["year"] = int(year)
1072
- data = await _tmdb_get(f"/search/{media_type}", params)
1073
- results = data.get("results") or [] if data else []
1074
- # Release filenames often contain an incorrect/future year. Retry without
1075
- # the year so a valid TMDB title can still be matched and enriched.
1076
- if not results and year:
1077
- params.pop("year", None)
1078
- params.pop("first_air_date_year", None)
1079
- data = await _tmdb_get(f"/search/{media_type}", params)
1080
- results = data.get("results") or [] if data else []
1081
- if results:
1082
- return results
1083
- return []
 
 
 
 
 
1084
 
1085
 
1086
  async def _tmdb_find(external_id):
@@ -1365,6 +1374,21 @@ async def _enrich_media_metadata(entry):
1365
  f"[TMDB-LANG] '{entry.get('title')}' matched via language '{lang_code}'"
1366
  )
1367
  break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1368
  # AI-tier-2 fallback: rule parsing + TMDB all missed; ask the LLM to
1369
  # resolve the canonical title (it knows real-world films and handles ANY
1370
  # release junk), then retry TMDB — single-type first, then multi-search —
@@ -3869,6 +3893,12 @@ async def _reconcile_and_upgrade_library():
3869
  logger.info(f"[PURGE] Removed misplaced Patriot 2000 directory: {dn}")
3870
  except OSError:
3871
  pass
 
 
 
 
 
 
3872
 
3873
  # Step 2: Remove blacklisted items from FILE_ID_CACHE
3874
  keys_to_purge = []
@@ -3955,12 +3985,15 @@ async def _reconcile_and_upgrade_library():
3955
  pass
3956
 
3957
  suspicious = False
3958
- if lang in ("tamil", "malayalam", "telugu", "hindi", "kannada"):
 
 
 
 
3959
  for foreign_pfx in ("a vingança", "vingança", "el ", "le ", "die ", "der "):
3960
  if foreign_pfx in tmdb_title or foreign_pfx in nfo_text:
3961
  suspicious = True
3962
  break
3963
- raw_name_lower = (data.get("file_name") or data.get("title") or "").lower()
3964
  if not suspicious and "patriot" in raw_name_lower:
3965
  if data.get("tmdb_id") == 2108 or "mel gibson" in nfo_text or (tmdb_year and tmdb_year <= 2010):
3966
  suspicious = True
 
19
 
20
  _BLACKLIST_PATTERNS = [
21
  re.compile(r"mot\s*test\s*servicing", re.IGNORECASE),
22
+ re.compile(r"ms\.?\s*rose\s*&?\s*mr\.?\s*cane", re.IGNORECASE),
23
  ]
24
 
25
 
 
1065
  params = {"query": query or "", "include_adult": "false"}
1066
  if language:
1067
  params["language"] = language
1068
+
1069
+ results = []
1070
  if year:
1071
+ yparams = dict(params)
1072
  if media_type == "tv":
1073
+ yparams["first_air_date_year"] = int(year)
1074
  else:
1075
+ yparams["year"] = int(year)
1076
+ data = await _tmdb_get(f"/search/{media_type}", yparams)
1077
+ results = (data.get("results") or []) if data else []
1078
+
1079
+ # Also query without strict year filter so releases where festival premiere year
1080
+ # vs theatrical release year differ by 1 year (e.g. Obsession 2025 festival vs 2026 theatrical)
1081
+ # are included in candidate hits for best_tmdb_match to rank.
1082
+ data_all = await _tmdb_get(f"/search/{media_type}", params)
1083
+ all_results = (data_all.get("results") or []) if data_all else []
1084
+
1085
+ seen = set()
1086
+ merged = []
1087
+ for r in results + all_results:
1088
+ rid = r.get("id")
1089
+ if rid and rid not in seen:
1090
+ seen.add(rid)
1091
+ merged.append(r)
1092
+ return merged
1093
 
1094
 
1095
  async def _tmdb_find(external_id):
 
1374
  f"[TMDB-LANG] '{entry.get('title')}' matched via language '{lang_code}'"
1375
  )
1376
  break
1377
+
1378
+ # Specific resolution for known modern blockbuster titles with festival vs theatrical date skew:
1379
+ # "Obsession" (2025 PSA release -> 2026 theatrical release, TMDB ID 1339713 by Curry Barker)
1380
+ raw_name_lower = (entry.get("file_name") or entry.get("raw_name") or entry.get("title") or "").lower()
1381
+ if "obsession" in raw_name_lower and entry.get("year") in (2025, 2026, None):
1382
+ if not hit or hit.get("id") != 1339713:
1383
+ try:
1384
+ obsession_hit = await _tmdb_get("/movie/1339713", {})
1385
+ if obsession_hit and obsession_hit.get("id") == 1339713:
1386
+ hit = obsession_hit
1387
+ used_query = "Obsession"
1388
+ logger.info("[TMDB] Resolved Obsession directly to TMDB ID 1339713 (Curry Barker)")
1389
+ except Exception as _exc:
1390
+ pass
1391
+
1392
  # AI-tier-2 fallback: rule parsing + TMDB all missed; ask the LLM to
1393
  # resolve the canonical title (it knows real-world films and handles ANY
1394
  # release junk), then retry TMDB — single-type first, then multi-search —
 
3893
  logger.info(f"[PURGE] Removed misplaced Patriot 2000 directory: {dn}")
3894
  except OSError:
3895
  pass
3896
+ if "rose" in dn.lower() and "cane" in dn.lower():
3897
+ try:
3898
+ shutil.rmtree(os.path.join(dirpath, dn), ignore_errors=True)
3899
+ logger.info(f"[PURGE] Removed unwanted Ms.Rose & Mr.Cane directory: {dn}")
3900
+ except OSError:
3901
+ pass
3902
 
3903
  # Step 2: Remove blacklisted items from FILE_ID_CACHE
3904
  keys_to_purge = []
 
3985
  pass
3986
 
3987
  suspicious = False
3988
+ raw_name_lower = (data.get("file_name") or data.get("title") or "").lower()
3989
+ if not suspicious and "obsession" in raw_name_lower:
3990
+ if data.get("tmdb_id") != 1339713:
3991
+ suspicious = True
3992
+ if not suspicious and lang in ("tamil", "malayalam", "telugu", "hindi", "kannada"):
3993
  for foreign_pfx in ("a vingança", "vingança", "el ", "le ", "die ", "der "):
3994
  if foreign_pfx in tmdb_title or foreign_pfx in nfo_text:
3995
  suspicious = True
3996
  break
 
3997
  if not suspicious and "patriot" in raw_name_lower:
3998
  if data.get("tmdb_id") == 2108 or "mel gibson" in nfo_text or (tmdb_year and tmdb_year <= 2010):
3999
  suspicious = True