Fix wrapped-course false positive in picker; add build version display

Sort Unsorted's destination picker still listed a course whose real
video content sat two levels deep inside its own "display name"
folder (a common release-bundle extraction shape), alongside empty
leftover folders from the extraction that neither of the two earlier
exclusion checks could safely rule out - an empty folder is
indistinguishable from a legitimate freshly-created category. Detect
this case by comparing token similarity between a folder's own name
and its single course child's name: high overlap means they're naming
the same thing (a redundant wrapper), not a deliberate category
holding one course.

Also bake the build's git commit + build time into the Docker image
(git rev-parse at build time, .git deleted again immediately after) so
a push's effect on the running container is actually visible - shown
on the Settings page and in /health - instead of having to guess
whether a webhook rebuild picked up the latest commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 13:52:32 -04:00
co-authored by Claude Sonnet 5
parent 7fbe63e05c
commit 8d1ce72372
5 changed files with 111 additions and 16 deletions
+17 -6
View File
@@ -44,6 +44,14 @@ triggers a full image **rebuild** from the Dockerfile (confirmed, not just a
container restart), so Dockerfile changes (e.g. adding `ffmpeg`) take effect
on the very next push without any manual step.
**Confirming a deploy landed:** the Dockerfile bakes the exact commit and
build time into the image as `/app/BUILD_VERSION` (`git rev-parse` at build
time, `.git` itself is deleted again right after so it doesn't ship in the
image) - shown at the bottom of the Settings page and in `/health`'s JSON
response, so after a push you can check the running container actually
picked it up instead of guessing. Reads as `dev` outside Docker (no
`BUILD_VERSION` file to read).
---
## Local development
@@ -109,12 +117,15 @@ silently stay blank instead of erroring.
the same picker Manage Library's Move action uses. The picker excludes
actual course/item folders, not just organizational ones: a leaf folder
holding a single non-video/audio file (an ebook, an audiobook in a
format this app doesn't play, etc.), and a folder whose whole subtree
has no video/audio anywhere in it at all (a course's bundled source
code, a Python virtualenv, project assets, ...) even though it has
plenty of subfolders - both cases Library browsing's own course
detection wouldn't catch either, since there's no video/audio file to
key off. Nothing on disk moves until you review and hit Apply. Matching
format this app doesn't play, etc.); a folder whose whole subtree has no
video/audio anywhere in it at all (a course's bundled source code, a
Python virtualenv, project assets, ...) even though it has plenty of
subfolders; and a release-bundle folder that wraps a single real course
one level down under its own "course display name" folder, alongside
unrelated (possibly empty) junk siblings at the same level - all three
cases Library browsing's own course detection wouldn't catch either,
since it only checks one level of nesting for video/audio. Nothing on
disk moves until you review and hit Apply. Matching
ignores common noise (e-learning
platform names, release/distribution-group tags, dates) via a stopword
list, and beyond that treats a match against a folder's own deliberate