Skip to content

[API] Video Management #39071

Description

@FuaadZam

Functional Area: Video Management

Endpoints under this functional area:

Module API Version Resource / Endpoint HTTP Method Description
Video Download v1 /contentstore/v1/videos/{course_id}/download PUT Downloads multiple videos for a course as a single zip file. Accepts a body parameter files with an array of objects containing url and name for each video. Returns a zip attachment named {course_id}videos{random_id}.zip on success.
Video Usage v1 /contentstore/v1/videos/{course_id}/{edx_video_id}/usage GET Retrieves usage locations of a specific video within the course. Returns an array of strings indicating XBlocks or subsections where the video is embedded.

Activity

  1. added theissue type on Sep 2, 2026
  2. Abdul-Muqadim-Arbisoft commented on Oct 4, 2026

    @Abdul-Muqadim-Arbisoft
    Contributor

    Standardization PR open: #39186

    PR: #39186 — feat: standardize Video Management API into authoring v2
    State: open

    What 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's get_videos_for_course, which runs Video.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.py is 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_usage still reverses to …/download without a video id and to …/usage with one (VideoRoutesTest). Deprecated Org/Course/Run keys still reach v1.
    • Legacy tests untouched and green. The old-tests gate ran 108: 13 in rest_api/v1/views/tests/test_videos.py and 95 in views/tests/test_videos.py. The whole contentstore/rest_api/ suite passes (772). The tests gate passes the 276 new tests.
    • Schema: additions and deprecation flags only. The raw compare fails 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). compare ignores operationIds, tags and servers, so each of the 77 base operations was also compared whole with its re-prefixed head counterpart. The only difference is deprecated: true on the two superseded operations, and no head operationId is duplicated. components gains CourseVideoUsage, ErrorResponse, VideoArchiveFile, VideoArchiveRequest and VideoUsageLocation, and changes or drops none. test_no_authoring_operation_is_given_the_id_of_another checks 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 same Content-Type and Content-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 SAVEPOINT from 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}/usage and PUT /api/contentstore/v1/videos/{course_id}/download are superseded by the addresses above. They are marked deprecated: true in 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-1 Warning header is not emitted either, because the v1 route is frozen.

    Follow-ups

    • edly-io/openedx-platform-sdk regeneration adds v2_courses_video_usages_retrieve and v2_courses_video_archives_create (the v1 operations were never tagged). Three things for that PR. It needs content_type_overrides: {application/zip: application/octet-stream} so the archive call returns File; 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: ErrorResponse schema 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_info and send got_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_key converter should refuse version-only keys. Then HasFullCourseKey can 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/zip alone stops getting 406. Issue to be filed (text ready).
    • The shared video helpers, fixed once for both addresses: unquoted Content-Disposition filename, 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 VideoDownloadThrottle into v2 before rest_api/v1/views/videos.py is 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions