Skip to content

upload video to lfs#2138

Merged
jo-elimu merged 3 commits intomainfrom
1934-upload-to-lfs
Apr 16, 2025
Merged

upload video to lfs#2138
jo-elimu merged 3 commits intomainfrom
1934-upload-to-lfs

Conversation

@jo-elimu
Copy link
Copy Markdown
Member

Issue Number

Purpose

Technical Details

Testing Instructions

Screenshots


Format Checks

Note

Files in PRs are automatically checked for format violations with mvn spotless:check.

If this PR contains files with format violations, run mvn spotless:apply to fix them.

@jo-elimu jo-elimu self-assigned this Apr 16, 2025
@jo-elimu jo-elimu requested a review from a team as a code owner April 16, 2025 16:28
@jo-elimu jo-elimu requested review from AshishBagdane, alexander-kuruvilla and eymaal and removed request for a team April 16, 2025 16:28
@jo-elimu jo-elimu linked an issue Apr 16, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2025

Walkthrough

This set of changes migrates the storage and serving of video files from internal database storage to an external platform, specifically GitHub LFS. The Video entity now includes a method to generate a public URL for accessing video files hosted on GitHub. Controllers for video creation and editing are updated to upload video files to GitHub LFS and store related checksums. The CSV export and API logic are modified to use the new URL. The GitHubLfsHelper utility is extended to handle video uploads, and front-end JSPs and CSS are updated to support inline video playback and improved styling.

Changes

Files/Paths Change Summary
src/main/java/ai/elimu/entity/content/multimedia/Video.java Added getUrl() method to generate a GitHub raw content URL for videos; imported EnvironmentContextLoaderListener.
src/main/java/ai/elimu/util/GitHubLfsHelper.java Added support for uploading videos to GitHub LFS via new uploadVideoToLfs(); refactored upload logic into a shared private method; improved logging and path construction.
src/main/java/ai/elimu/web/content/multimedia/video/VideoCreateController.java
src/main/java/ai/elimu/web/content/multimedia/video/VideoEditController.java
Updated to calculate and set MD5 checksum for videos, upload video bytes to GitHub LFS, and store returned GitHub checksum; added relevant TODO comments.
src/main/java/ai/elimu/rest/v2/JpaToGsonConverter.java
src/main/java/ai/elimu/web/content/multimedia/video/VideoCsvExportController.java
Updated to use video.getUrl() for file URL in API and CSV export, replacing manual URL construction.
src/main/webapp/WEB-INF/jsp/content/multimedia/video/edit.jsp Embedded HTML5 video player for inline preview; displayed video metadata (MD5 checksum, file URL, GitHub checksum) in code blocks; removed old preview section.
src/main/webapp/WEB-INF/jsp/content/multimedia/video/list.jsp Added conditional CSS class to video card based on presence of GitHub checksum; updated class for play icon container.
src/main/webapp/static/css/styles.css Extended image styles to also apply to video elements; simplified .cid-false selector.
src/main/webapp/WEB-INF/jsp/content/multimedia/image/create.jsp
src/main/webapp/WEB-INF/jsp/content/multimedia/image/edit.jsp
Replaced Spring form file input with plain HTML file input for image upload fields.
pom-dependency-tree.txt Updated main project artifact version from 2.5.89-SNAPSHOT to 2.5.91-SNAPSHOT.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant VideoCreateController
    participant ChecksumHelper
    participant GitHubLfsHelper
    participant Video
    participant Database

    User->>VideoCreateController: Uploads video file
    VideoCreateController->>ChecksumHelper: Calculate MD5 checksum
    VideoCreateController->>Video: Set MD5 checksum
    VideoCreateController->>GitHubLfsHelper: Upload video to GitHub LFS
    GitHubLfsHelper->>GitHubLfsHelper: Upload file, return GitHub checksum
    VideoCreateController->>Video: Set GitHub checksum
    VideoCreateController->>Database: Store Video entity
Loading

Assessment against linked issues

Objective Addressed Explanation
Move storage of videos from database to external platform (#1934)

Possibly related PRs

Suggested reviewers

  • jpatel3
  • nya-elimu
  • tomaszsmy
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 16, 2025

Codecov Report

Attention: Patch coverage is 0% with 29 lines in your changes missing coverage. Please review.

Project coverage is 14.97%. Comparing base (33bfb84) to head (8b85cb6).
Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
src/main/java/ai/elimu/util/GitHubLfsHelper.java 0.00% 15 Missing ⚠️
.../content/multimedia/video/VideoEditController.java 0.00% 6 Missing ⚠️
...java/ai/elimu/entity/content/multimedia/Video.java 0.00% 3 Missing ⚠️
...ontent/multimedia/video/VideoCreateController.java 0.00% 3 Missing ⚠️
...main/java/ai/elimu/rest/v2/JpaToGsonConverter.java 0.00% 1 Missing ⚠️
...ent/multimedia/video/VideoCsvExportController.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #2138      +/-   ##
============================================
- Coverage     15.03%   14.97%   -0.06%     
  Complexity      387      387              
============================================
  Files           232      232              
  Lines          6065     6089      +24     
  Branches        702      703       +1     
============================================
  Hits            912      912              
- Misses         5103     5127      +24     
  Partials         50       50              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/main/java/ai/elimu/entity/content/multimedia/Video.java (1)

50-55: New getUrl() method for generating video URLs.

The method dynamically constructs URLs to videos stored in GitHub LFS, using the content language, MD5 checksum, and video format to form the complete path.

Consider extracting the base URL and path structure to constants or configuration properties to improve maintainability. This would make it easier to adapt if the repository structure or hosting location changes in the future:

+ private static final String BASE_URL = "https://raw.githubusercontent.com/elimu-ai/webapp-lfs/main";
+ private static final String VIDEOS_PATH = "/videos";

  public String getUrl() {
-   return "https://raw.githubusercontent.com/elimu-ai/webapp-lfs/main" +
+   return BASE_URL +
        "/lang-" + EnvironmentContextLoaderListener.PROPERTIES.getProperty("content.language") +
-       "/videos" +
+       VIDEOS_PATH +
        "/" + getChecksumMd5() + "." + getVideoFormat().toString().toLowerCase();
  }
src/main/java/ai/elimu/web/content/multimedia/video/VideoCreateController.java (1)

130-131: TODO comment should include more context.

The TODO comment references GitHub issue #1545 but doesn't provide any context about what needs to be done. Adding a brief description would make it clearer for other developers.

-// TODO: https://github.com/elimu-ai/webapp/issues/1545
+// TODO: Additional processing or optimization needed for videos - https://github.com/elimu-ai/webapp/issues/1545
src/main/java/ai/elimu/util/GitHubLfsHelper.java (1)

69-110: Good refactoring: Common upload functionality extracted to a private method.

The extraction of common upload functionality into a private method reduces code duplication and centralizes the GitHub API interaction logic. However, error handling could be improved.

Consider enhancing error handling with more specific error messages:

 if (!httpResponse.isSuccess()) {
-    log.warn("responseAsJson: " + responseAsJson);
+    log.error("Failed to upload file to GitHub LFS. Path: {}, Status: {}, Response: {}", 
+              path, httpResponse.getStatus(), responseAsJson);
     return null;
 } else {

Also consider adding retry logic for transient network issues:

-private static String uploadFileToLfs(String path, byte[] bytes) {
+private static String uploadFileToLfs(String path, byte[] bytes, int retryCount) {
     log.info("uploadFileToLfs");
     
     // ... existing code ...
     
     if (!httpResponse.isSuccess()) {
         log.warn("responseAsJson: " + responseAsJson);
+        if (retryCount > 0 && (httpResponse.getStatus() == 500 || httpResponse.getStatus() == 503)) {
+            log.info("Retrying upload due to server error. Attempts remaining: {}", retryCount - 1);
+            try {
+                Thread.sleep(1000); // Wait before retry
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+            return uploadFileToLfs(path, bytes, retryCount - 1);
+        }
         return null;
     } else {
         // ... existing code ...
     }
 }

+private static String uploadFileToLfs(String path, byte[] bytes) {
+    return uploadFileToLfs(path, bytes, 2); // Default to 2 retries
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0ec233 and 8b85cb6.

📒 Files selected for processing (12)
  • pom-dependency-tree.txt (1 hunks)
  • src/main/java/ai/elimu/entity/content/multimedia/Video.java (2 hunks)
  • src/main/java/ai/elimu/rest/v2/JpaToGsonConverter.java (1 hunks)
  • src/main/java/ai/elimu/util/GitHubLfsHelper.java (2 hunks)
  • src/main/java/ai/elimu/web/content/multimedia/video/VideoCreateController.java (3 hunks)
  • src/main/java/ai/elimu/web/content/multimedia/video/VideoCsvExportController.java (1 hunks)
  • src/main/java/ai/elimu/web/content/multimedia/video/VideoEditController.java (4 hunks)
  • src/main/webapp/WEB-INF/jsp/content/multimedia/image/create.jsp (1 hunks)
  • src/main/webapp/WEB-INF/jsp/content/multimedia/image/edit.jsp (1 hunks)
  • src/main/webapp/WEB-INF/jsp/content/multimedia/video/edit.jsp (2 hunks)
  • src/main/webapp/WEB-INF/jsp/content/multimedia/video/list.jsp (1 hunks)
  • src/main/webapp/static/css/styles.css (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/ai/elimu/util/GitHubLfsHelper.java (1)
src/main/java/ai/elimu/web/context/EnvironmentContextLoaderListener.java (1)
  • EnvironmentContextLoaderListener (32-167)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: build (macos-latest, 17)
  • GitHub Check: build (macos-latest, 21)
  • GitHub Check: build (windows-latest, 17)
  • GitHub Check: build (ubuntu-latest, 17)
  • GitHub Check: build (windows-latest, 21)
  • GitHub Check: build (ubuntu-latest, 21)
  • GitHub Check: test_rest_localhost
🔇 Additional comments (21)
pom-dependency-tree.txt (1)

1-1: Routine version bump looks good.

The update from 2.5.89-SNAPSHOT to 2.5.91-SNAPSHOT is appropriate for tracking new features and enhancements. No issues found.

src/main/webapp/WEB-INF/jsp/content/multimedia/image/edit.jsp (1)

103-103: File input changed from Spring form tag to standard HTML input.

The change from <form:input path="bytes" type="file" /> to standard HTML <input name="bytes" type="file" /> maintains compatibility while potentially avoiding Spring binding issues with multipart file uploads. This change aligns with similar modifications in the image creation form.

src/main/webapp/WEB-INF/jsp/content/multimedia/image/create.jsp (1)

84-84: File input changed from Spring form tag to standard HTML input.

This change matches the modification in the edit.jsp file, ensuring a consistent approach to file uploads across the application.

src/main/webapp/static/css/styles.css (3)

62-63: CSS styling extended to support video elements.

The CSS selectors now include both images and videos, which ensures consistent styling for all multimedia elements.


69-70: CSS styling for card panels extended to videos.

Consistent styling applied to videos within card panels, maintaining visual harmony with image elements.


74-74: Simplified CSS selector for cid-false class.

The selector was streamlined from .cid-false, main .card-panel img.cid-false to just .cid-false, which might broaden its application beyond images to include videos with this class.

src/main/java/ai/elimu/entity/content/multimedia/Video.java (1)

5-5: Added import for EnvironmentContextLoaderListener.

The import is required for accessing environment properties in the new getUrl() method.

src/main/java/ai/elimu/web/content/multimedia/video/VideoCsvExportController.java (1)

41-41: Good refactoring to use the new URL generation method.

Using video.getUrl() instead of manually constructing the URL is a good practice. This centralizes the URL generation logic in the Video entity, ensuring consistency across the application as videos are migrated to GitHub LFS.

src/main/webapp/WEB-INF/jsp/content/multimedia/video/list.jsp (2)

27-27: Appropriate conditional CSS class based on GitHub LFS migration status.

The CSS class cid-${video.checksumGitHub != null} provides visual differentiation between videos that have been migrated to GitHub LFS and those that haven't. This helps track the migration progress.


29-29: Good addition of the card-image class for consistent styling.

Adding the "card-image" class while maintaining the inline styles improves the styling structure. This will make it easier to apply consistent styling across video elements.

src/main/java/ai/elimu/rest/v2/JpaToGsonConverter.java (1)

356-356: Good refactoring to use the new URL generation method.

Using video.getUrl() ensures consistency with the changes in VideoCsvExportController.java. This centralizes the URL generation logic in the Video entity, which is a good practice when migrating to GitHub LFS.

src/main/java/ai/elimu/web/content/multimedia/video/VideoCreateController.java (2)

9-10: Appropriate imports for the new functionality.

Adding imports for ChecksumHelper and GitHubLfsHelper is necessary for the new checksum calculation and GitHub LFS upload functionality.


93-95: Good implementation of MD5 checksum calculation.

Setting the MD5 checksum using ChecksumHelper.calculateMD5(bytes) is important for the GitHub LFS integration. The TODO comment appropriately references the related GitHub issue for tracking.

src/main/java/ai/elimu/web/content/multimedia/video/VideoEditController.java (3)

17-18: Appropriate imports added for video LFS handling.

The addition of ChecksumHelper and GitHubLfsHelper imports aligns with the PR objective of supporting video uploads to GitHub LFS storage.


129-130: MD5 checksum calculation added for the uploaded video.

The code now calculates and stores an MD5 checksum for the video content, which is used for constructing filenames in the GitHub LFS storage. The TODO comment indicates there might be additional work planned around this functionality in issue #2137.


155-155:

Details

✅ Verification successful

Track the TODO comment for completion.

The same TODO referencing issue #1545 appears twice in this file (here and line 74). This indicates an important follow-up task that should be tracked for completion after this PR.


🏁 Script executed:

#!/bin/bash
# Check if issue #1545 is still open
gh issue view 1545 --repo elimu-ai/webapp --json state,title,body | jq .

Length of output: 151


ACTION: Ensure both TODO references for issue #1545 are tracked for timely resolution

  • The file src/main/java/ai/elimu/web/content/multimedia/video/VideoEditController.java contains the same TODO comment on lines 74 and 155.
  • Verification confirms that issue #1545 ("Add VideoContributionEvent") remains open.
  • Please track this issue and update or remove the TODO comments once the functionality is implemented.
src/main/webapp/WEB-INF/jsp/content/multimedia/video/edit.jsp (2)

7-11: Video preview and playback functionality added.

An embedded video player has been added to allow users to preview videos directly in the editor. This improves usability by providing immediate visual feedback without requiring users to navigate away.


359-370: Added technical metadata for video files.

The code displays important metadata about the video, including checksums and URL, which is useful for debugging and verification. This aligns with the PR objective of tracking video files in LFS.

A minor suggestion to improve user experience:

 <label>file_url</label>
-<code>${video.url}</code><br />
+<code><a href="${video.url}" target="_blank">${video.url}</a></code><br />
src/main/java/ai/elimu/util/GitHubLfsHelper.java (3)

23-24: Good refactoring: API base URL extracted as a constant.

Extracting the API base URL as a constant improves maintainability by centralizing this value and making it easier to update if needed.


32-45: Refactored image upload method to use common upload functionality.

The image upload method has been improved to use a more descriptive path construction and delegate to a common upload method. This reduces code duplication and improves maintainability.


47-67: New method to support video uploads to GitHub LFS.

This method properly handles the upload of video files to GitHub LFS, using the video's MD5 checksum in the filename for better identification and organization.

Note the key difference in naming convention: videos use MD5 checksum as the filename while images use ID and revision number. This difference should be documented for future maintainers.

Comment on lines +125 to 127
String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
video.setChecksumGitHub(checksumGitHub);
video.setTimeLastUpdate(Calendar.getInstance());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add validation for the GitHub LFS upload result.

The code uploads the video to GitHub LFS and sets the checksum, but there's no validation that the upload was successful. The checksumGitHub could potentially be null or empty if the upload fails.

-String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
-video.setChecksumGitHub(checksumGitHub);
+String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
+if (StringUtils.isNotBlank(checksumGitHub)) {
+  video.setChecksumGitHub(checksumGitHub);
+} else {
+  log.error("Failed to upload video to GitHub LFS: {}", video.getTitle());
+  result.rejectValue("bytes", "GitHubLfsUploadFailed");
+  return "content/multimedia/video/create";
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
video.setChecksumGitHub(checksumGitHub);
video.setTimeLastUpdate(Calendar.getInstance());
String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
if (StringUtils.isNotBlank(checksumGitHub)) {
video.setChecksumGitHub(checksumGitHub);
} else {
log.error("Failed to upload video to GitHub LFS: {}", video.getTitle());
result.rejectValue("bytes", "GitHubLfsUploadFailed");
return "content/multimedia/video/create";
}
video.setTimeLastUpdate(Calendar.getInstance());

Comment on lines +68 to +75
if (StringUtils.isBlank(video.getChecksumGitHub())) {
String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
video.setChecksumGitHub(checksumGitHub);
video.setRevisionNumber(video.getRevisionNumber() + 1);
videoDao.update(video);

// TODO: https://github.com/elimu-ai/webapp/issues/1545
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding error handling for GitHub LFS upload failures.

The code correctly checks for missing GitHub checksums and uploads files to LFS when needed. However, there's no error handling if the upload fails (when uploadVideoToLfs returns null).

Consider implementing error handling:

 if (StringUtils.isBlank(video.getChecksumGitHub())) {
   String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
+  if (checksumGitHub == null) {
+    log.error("Failed to upload video ID {} to GitHub LFS", video.getId());
+    // Consider adding a flash message or other user feedback
+  } else {
     video.setChecksumGitHub(checksumGitHub);
     video.setRevisionNumber(video.getRevisionNumber() + 1);
     videoDao.update(video);
+  }

   // TODO: https://github.com/elimu-ai/webapp/issues/1545
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (StringUtils.isBlank(video.getChecksumGitHub())) {
String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
video.setChecksumGitHub(checksumGitHub);
video.setRevisionNumber(video.getRevisionNumber() + 1);
videoDao.update(video);
// TODO: https://github.com/elimu-ai/webapp/issues/1545
}
if (StringUtils.isBlank(video.getChecksumGitHub())) {
String checksumGitHub = GitHubLfsHelper.uploadVideoToLfs(video, video.getBytes());
if (checksumGitHub == null) {
log.error("Failed to upload video ID {} to GitHub LFS", video.getId());
// Consider adding a flash message or other user feedback
} else {
video.setChecksumGitHub(checksumGitHub);
video.setRevisionNumber(video.getRevisionNumber() + 1);
videoDao.update(video);
}
// TODO: https://github.com/elimu-ai/webapp/issues/1545
}

@jo-elimu jo-elimu merged commit 01e5b3f into main Apr 16, 2025
9 of 10 checks passed
@jo-elimu jo-elimu deleted the 1934-upload-to-lfs branch April 16, 2025 16:34
@coderabbitai coderabbitai Bot mentioned this pull request Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move storage of videos from database to external platform

1 participant