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:
+16
-20
@@ -526,25 +526,26 @@ def find_course_thumbnail(course_path: str) -> Optional[str]:
|
||||
|
||||
|
||||
_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:
|
||||
"""
|
||||
Heuristic for 'this folder is a course, stop recursing into it':
|
||||
it has media files directly, or at least two of its immediate
|
||||
subfolders do (covers the common Section 1/, Section 2/... layout).
|
||||
Heuristic for 'this folder is a course, stop recursing into it': it has
|
||||
media files directly, or its media-holding immediate subfolders all
|
||||
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
|
||||
a publisher/grouping folder that holds a single course - e.g.
|
||||
Pluralsight/Docker Deep Dive/lesson.mp4 - for the course itself.
|
||||
|
||||
The one exception: a directory with exactly one media-holding subfolder
|
||||
whose name reads as a section label ("Section 1", "Module 2", ...)
|
||||
rather than a course title. That's still a course with just one
|
||||
section, not a publisher folder - Pluralsight/Docker Deep Dive doesn't
|
||||
get named "Section 1", so this doesn't reopen the ambiguity above.
|
||||
Requiring *every* media-holding subfolder to match, not just one, is
|
||||
what keeps a publisher/category folder holding several unrelated
|
||||
courses - e.g. Claude/Pluralsight.X.../*.mp4 next to
|
||||
Claude/Linkedin.Learning.Y.../*.mp4 - from being mistaken for a single
|
||||
course just because more than one of its subfolders happens to store
|
||||
episodes directly rather than nested under their own chapter folder.
|
||||
Real course titles ("Pluralsight.Docker.Deep.Dive...") don't read as
|
||||
chapter labels, so this doesn't reopen that ambiguity.
|
||||
"""
|
||||
if _has_direct_media(directory):
|
||||
return True
|
||||
@@ -556,14 +557,9 @@ def _looks_like_course(directory: Path) -> bool:
|
||||
except (PermissionError, OSError):
|
||||
return False
|
||||
children_with_media = [child for child in children if _has_direct_media(child)]
|
||||
if len(children_with_media) >= 2:
|
||||
return True
|
||||
if (
|
||||
len(children_with_media) == 1
|
||||
and _SECTION_NAME_RE.match(children_with_media[0].name.strip())
|
||||
):
|
||||
return True
|
||||
if not children_with_media:
|
||||
return False
|
||||
return all(_SECTION_NAME_RE.match(child.name.strip()) for child in children_with_media)
|
||||
|
||||
|
||||
HIDDEN_PATHS_FILE = os.path.join(DATA_DIR, 'hidden_paths.json')
|
||||
|
||||
Reference in New Issue
Block a user