-
Notifications
You must be signed in to change notification settings - Fork 23
Description
This feature was originally proposed by @holaontiveros and @rodmgwgu, in a workshop session during the Axim/WGU dev summit in Feb '26, though I'm reframing it somewhat here. All of this is subject to Product approval, of course.
Use Cases
Western Governors University uses a Digital Asset Management System for its various media files. This serves to provide a centralized, searchable repository of such assets, as well as providing a way to remove assets if it becomes necessary for copyright reasons. In their current deployment of Open edX platform, WGU stores nothing in course Files and Uploads, and uses links for all assets.
The goals for this would be:
- Create a system that allows for centralized Media asset management in Open edX Platform.
- Allow for pluggable integration with external digital asset management systems, where libraries contain local references and metadata, but the actual asset is stored and served via that external system.
- Use this foundation to re-platform course Files and Uploads on top of openedx-core, and help to unblock the migration of course data.
Non-goal: We are not trying to grow Open edX Platform to be a first-in-class Digital Asset Management System. We basically want our platform to be good enough for the 80% use case that includes centralized management for copyright reasons, as well as being able to serve assets at scale.
The current state of media files in openedx-core
We currently support the storage of media assets with the media applet in openedx_content. However, the media app is a very low-level system, and only really gives us blob storage. The only place where media assets can be leveraged by end-users is through the components applet, which has models that map a particular component-local path name with a corresponding media file, e.g. static/diagram.webp → Media:
openedx-core/src/openedx_content/applets/components/models.py
Lines 226 to 250 in 848f663
| class ComponentVersionMedia(models.Model): | |
| """ | |
| Determines the Content for a given ComponentVersion. | |
| An ComponentVersion may be associated with multiple pieces of binary data. | |
| For instance, a Video ComponentVersion might be associated with multiple | |
| transcripts in different languages. | |
| When Content is associated with an ComponentVersion, it has some local | |
| key that is unique within the the context of that ComponentVersion. This | |
| allows the ComponentVersion to do things like store an image file and | |
| reference it by a "path" key. | |
| Content is immutable and sharable across multiple ComponentVersions. | |
| """ | |
| component_version = models.ForeignKey(ComponentVersion, on_delete=models.CASCADE) | |
| media = models.ForeignKey(Media, on_delete=models.RESTRICT) | |
| # "key" is a reserved word for MySQL, so we're temporarily using the column | |
| # name of "_key" to avoid breaking downstream tooling. A possible | |
| # alternative name for this would be "path", since it's most often used as | |
| # an internal file path. However, we might also want to put special | |
| # identifiers that don't map as cleanly to file paths at some point. | |
| key = key_field(db_column="_key") |
(Note: the comments say "Content" here, when they should say "Media"--I missed this in the refactoring.)
High Level Proposal
- Create a new
assetsapplet inopenedx_content. - Create a new publishable model called
Asset. - Design it such that Assets can be backed either by local file storage via the
mediaapp, or act as local references to external digital asset management systems. - Use Assets as the basis for course Files and Uploads.
- Use Assets as an alternative method of storage for Components that want to store media. An Asset would become a publishing dependency of the Components in which it is used, giving us a similar version vs. side-effect semantics as we currently have with container parents and children.
Open Questions
- Would we migrate current component/media associations in libraries to use assets, or allow for both modes?
- What sort of access control is practical on the LMS side?
- How will namespacing work, e.g. importing multiple courses into the same library without causing collisions?