Repository navigation
[API] Video Management #39071
Description
Activity
Abdul-Muqadim-Arbisoft commented
on Oct 4, 2026 ContributorMore actionsStandardization PR open: #39186
PR: #39186 — feat: standardize Video Management API into authoring v2
State: openWhat this does
Standardizes the Video Management API (#39071, umbrella #38137) onto two conforming addresses in the authoring API. The existing
/api/contentstore/v1/videos/…endpoints keep working exactly as they do today.New addresses
- GET
/api/authoring/v2/courses/{course_key}/video_usages/{edx_video_id}/ - POST
/api/authoring/v2/courses/{course_key}/video_archives/
ADRs applied
10 of 16 decisions applied on this pass; every one left open carries its reason.
- 0025 serializers
- 0026 permissions
- 0027 schema / docs
- 0028 viewsets
- 0029 errors
- 0030 GET idempotence
- 0034 authentication
- 0037 versioning
- 0038 URL structure
- OEP-69 conventions
- 0031 merged endpoints — nothing merged: a usage record and a generated archive are different resources, not an action-URL family on one resource
- 0032 pagination — n/a: the usage endpoint is a member retrieve whose list is bounded by one video's uses in one course, and the archive is binary
- 0033 filtering — n/a: neither endpoint takes a filter or sort parameter. The video id is a path identifier
- 0035 MFE config — n/a: no endpoint in this area serves front-end or site configuration
- 0036 nested JSON — n/a: the usage body is flat and thin (two strings per entry), and the archive is not JSON
- OEP-66 queryset scoping — out of scope as a mechanism, applied as a layering. Neither view has a
get_queryset(): data comes from the content store and edxval. The future ORM seam is edxval'sget_videos_for_course, which runsVideo.objects.filter(courses__course_id=…, courses__is_hidden=False)
The evidence for each tick is the compliance matrix in the PR body; nothing is ticked without a named test or gate line.
Backward compatibility
- No pre-existing version file is modified.
rest_api/v1/views/videos.pyis not in the diff. Versions gate PASS: 11 files added inside pre-existing version directories (see In-place edits), 0 modified, no allowlist. - Every v1 address still resolves to the same view. Urls gate PASS (2144 → 2150 routes). The shared URL name
video_usagestill reverses to…/downloadwithout a video id and to…/usagewith one (VideoRoutesTest). DeprecatedOrg/Course/Runkeys still reach v1. - Legacy tests untouched and green. The old-tests gate ran 108: 13 in
rest_api/v1/views/tests/test_videos.pyand 95 inviews/tests/test_videos.py. The wholecontentstore/rest_api/suite passes (772). The tests gate passes the 276 new tests. - Schema: additions and deprecation flags only. The raw
comparefails because removing the path trim renames all 56 trimmed base path keys. With the base paths re-prefixed the result is 0 BREAKING / 4 WARN / 4 INFO (Gate report).compareignores operationIds, tags and servers, so each of the 77 base operations was also compared whole with its re-prefixed head counterpart. The only difference isdeprecated: trueon the two superseded operations, and no head operationId is duplicated.componentsgainsCourseVideoUsage,ErrorResponse,VideoArchiveFile,VideoArchiveRequestandVideoUsageLocation, and changes or drops none.test_no_authoring_operation_is_given_the_id_of_anotherchecks operationIds before the generator renames collisions (seen red with a colliding route). - Parity (
INTENTIONAL_DIFFERENCES = []). Usage bodies are byte-identical at both addresses for these cases: one use, three uses, an unpublished unit, a parentless component, an unused id ({"usage_locations": []}), unset display names ("None - None / Video"), and a unit in no subsection. Archives are byte-identical, with the sameContent-TypeandContent-Disposition, for these cases: a named extension, an extension from the upstream content type, an unknown type, several files, and an empty list. A fetch that fails part way stops both archives at the same byte (test_a_fetch_failing_part_way_stops_the_archive). The harness was seen red when the usage result or the file list was altered. - Query counts (warm caches, legacy/new): usage 11/11 (staff), 13/13 (session), 11/11 (course staff), 8/7 (403), 8/9 (404), 5/6 (401); archive 11/11 (200), 11/12 (400), 8/7 (403), 8/9 (404), 5/6 (401). Every +1 is a
ROLLBACK TO SAVEPOINTfrom the standard error handling, not a read.
Gates
run_gates.sh --base origin/master --service cms --prefix /api/authoring/v2/ (6 --protect paths, no --allow), Studio container, final tree; no gate skipped versions PASS 11 added, 0 modified, 0 allowed hygiene PASS host 0 FAIL / 0 WARN (container: 4 WARN in Tutor-generated files outside the diff) schema FAIL, reconciled -> 0 BREAKING / 4 WARN / 4 INFO raw: 56 BREAKING = path keys renamed by removing SCHEMA_PATH_PREFIX_TRIM; after re-prefixing base paths: no base path missing, 2 operations added, 2 v1 operations newly deprecated; 4 WARN = existing "could not resolve authenticator" kind on the two new views urls PASS 2144 -> 2150 routes, 6 new checked old-tests PASS 108 passed (unedited) tests PASS 276 passed GATES FAILED: 1 (schema, reconciled; never reported as PASS)Deprecations
The two operations
GET /api/contentstore/v1/videos/{course_id}/{edx_video_id}/usageandPUT /api/contentstore/v1/videos/{course_id}/downloadare superseded by the addresses above. They are markeddeprecated: truein the Authoring API schema (/authoring-api/schema/, browsable at/authoring-api/ui/). The superseded views are unchanged: no response header and no deprecation notes in code.GET /api/contentstore/v1/videos/{course_id}(course videos) is a different resource and is not marked. The older/api-docs/document does not flag them (Departures, item 4). Readers of that document learn of the deprecation from #39128.Tracked on the FC-0118 umbrella DEPR ticket, #39128, which removes them one named release after the release this ships in. This area's entry, with its removal step: #39128 comment.
ADR 0037's docstring marker is deliberately omitted, following review on #39100 and #39102. If one is wanted later, it is the single OEP-21 line
Deprecated https://github.com/openedx/openedx-platform/issues/39128. The umbrella's step-1Warningheader is not emitted either, because the v1 route is frozen.Follow-ups
- edly-io/openedx-platform-sdk regeneration adds
v2_courses_video_usages_retrieveandv2_courses_video_archives_create(the v1 operations were never tagged). Three things for that PR. It needscontent_type_overrides: {application/zip: application/octet-stream}so the archive call returnsFile; this was checked on an earlier schema of this branch, not the final one. It must regenerate from a document with this PR's settings shape (feat: add GH workflow to generate openapi schema #39025). Its base URL is<CMS_BASE>. - edx-drf-extensions:
ErrorResponseschema description is the serializer's maintainer docstring. Issue to be filed (text ready). - edx-drf-extensions: nested validation errors and a stable
detail(normalize_validation_errors,flatten_detail). The tests here pin today's text. Issue to be filed (text ready). - edx-drf-extensions: log unhandled exceptions with
exc_infoand sendgot_request_exception. Issue to be filed (text ready). - edx-drf-extensions + platform: security schemes for
JwtAuthentication/DefaultSessionAuthentication. Once these exist, the 401 descriptions can be shortened. Issue to be filed (text ready, with the platform half). - edx-drf-extensions: the
course_keyconverter should refuse version-only keys. ThenHasFullCourseKeycan go. Issue to be filed (text ready). - edx-drf-extensions: catalog 405/406/415/malformed JSON instead of typing them
internal. Issue to be filed (text ready). - edx-drf-extensions: make
register_url_converters()idempotent. Django 5.2 warns on each URLconf reload and Django 6.0 raises. Issue to be filed (text ready). - edx-drf-extensions: a pass-through renderer for binary responses, so
Accept: application/zipalone stops getting 406. Issue to be filed (text ready). - The shared video helpers, fixed once for both addresses: unquoted
Content-Dispositionfilename, no upstream fetch timeout, truncated 200 on a failed fetch, and unsanitized or duplicate zip entry names. - A collection form
courses/{course_key}/video_usages/would let the Videos page fetch every video's usage in one call. It is not added here, to keep to the issue's table. - Move
VideoDownloadThrottleinto v2 beforerest_api/v1/views/videos.pyis removed, because the v2 archive view imports it from there. It is listed in the [DEPR]: Contentstore and enrollment API versions superseded by FC-0118 #39128 entry as a removal step.
Review on the PR, please — this issue stays open until it merges.
- GET
- added a commit that references this issue
on Oct 4, 2026
Functional Area: Video Management
Endpoints under this functional area:
/contentstore/v1/videos/{course_id}/download/contentstore/v1/videos/{course_id}/{edx_video_id}/usage