Fix course-detection heuristic swallowing category folders as one course
_looks_like_course() treated any folder with 2+ media-holding subfolders as a single multi-section course, with no check that those subfolders actually looked like sections. A category folder holding many unrelated courses (e.g. Claude/, General/) could accidentally satisfy that if several of its courses happened to keep episodes directly at their own top level rather than nested under a chapter folder, collapsing the whole category into one bogus course. Now every media-holding subfolder must read as a chapter/section label (Section 1, Module 2, or a numbered chapter like 01-Introduction) for the parent to count as one course - verified against the real library mount that ChatGPT/Claude/Copilot/General all correctly show as folders again, while individually-structured courses still detect correctly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+17
-21
@@ -526,25 +526,26 @@ def find_course_thumbnail(course_path: str) -> Optional[str]:
|
|||||||
|
|
||||||
|
|
||||||
_SECTION_NAME_RE = re.compile(
|
_SECTION_NAME_RE = re.compile(
|
||||||
r'^(section|module|chapter|part|unit|lesson)\b', re.IGNORECASE
|
r'^(section|module|chapter|part|unit|lesson)\b|^\d+[\s_.\-]', re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _looks_like_course(directory: Path) -> bool:
|
def _looks_like_course(directory: Path) -> bool:
|
||||||
"""
|
"""
|
||||||
Heuristic for 'this folder is a course, stop recursing into it':
|
Heuristic for 'this folder is a course, stop recursing into it': it has
|
||||||
it has media files directly, or at least two of its immediate
|
media files directly, or its media-holding immediate subfolders all
|
||||||
subfolders do (covers the common Section 1/, Section 2/... layout).
|
read as chapter/section labels ("Section 1", "Module 2", "01 -
|
||||||
|
Introduction", ...) rather than course titles - covers the common
|
||||||
|
Section 1/, Section 2/... (or numbered-chapter) layout.
|
||||||
|
|
||||||
Requiring 2+ matching subfolders (rather than just 1) avoids mistaking
|
Requiring *every* media-holding subfolder to match, not just one, is
|
||||||
a publisher/grouping folder that holds a single course - e.g.
|
what keeps a publisher/category folder holding several unrelated
|
||||||
Pluralsight/Docker Deep Dive/lesson.mp4 - for the course itself.
|
courses - e.g. Claude/Pluralsight.X.../*.mp4 next to
|
||||||
|
Claude/Linkedin.Learning.Y.../*.mp4 - from being mistaken for a single
|
||||||
The one exception: a directory with exactly one media-holding subfolder
|
course just because more than one of its subfolders happens to store
|
||||||
whose name reads as a section label ("Section 1", "Module 2", ...)
|
episodes directly rather than nested under their own chapter folder.
|
||||||
rather than a course title. That's still a course with just one
|
Real course titles ("Pluralsight.Docker.Deep.Dive...") don't read as
|
||||||
section, not a publisher folder - Pluralsight/Docker Deep Dive doesn't
|
chapter labels, so this doesn't reopen that ambiguity.
|
||||||
get named "Section 1", so this doesn't reopen the ambiguity above.
|
|
||||||
"""
|
"""
|
||||||
if _has_direct_media(directory):
|
if _has_direct_media(directory):
|
||||||
return True
|
return True
|
||||||
@@ -556,14 +557,9 @@ def _looks_like_course(directory: Path) -> bool:
|
|||||||
except (PermissionError, OSError):
|
except (PermissionError, OSError):
|
||||||
return False
|
return False
|
||||||
children_with_media = [child for child in children if _has_direct_media(child)]
|
children_with_media = [child for child in children if _has_direct_media(child)]
|
||||||
if len(children_with_media) >= 2:
|
if not children_with_media:
|
||||||
return True
|
return False
|
||||||
if (
|
return all(_SECTION_NAME_RE.match(child.name.strip()) for child in children_with_media)
|
||||||
len(children_with_media) == 1
|
|
||||||
and _SECTION_NAME_RE.match(children_with_media[0].name.strip())
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
HIDDEN_PATHS_FILE = os.path.join(DATA_DIR, 'hidden_paths.json')
|
HIDDEN_PATHS_FILE = os.path.join(DATA_DIR, 'hidden_paths.json')
|
||||||
|
|||||||
Reference in New Issue
Block a user