Conversation
changed route for thumbnail posting Co-authored-by: Joscha Henningsen <44805696+joschahenningsen@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR implements functionality for uploading custom thumbnails for lectures. It adds a new field isCustomThumbnailEnabled to control whether custom thumbnails can be uploaded, along with UI controls for drag-and-drop thumbnail uploads and backend endpoints to handle the file uploads.
Changes:
- Added
isCustomThumbnailEnabledboolean field to lecture data model and API - Implemented drag-and-drop thumbnail upload UI with preview functionality
- Created backend endpoint and data access method for storing custom thumbnails
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| web/ts/edit-course.ts | Added thumbnail upload handlers and preview functionality |
| web/ts/data-store/admin-lecture-list.ts | Added method to upload thumbnail files and update lecture state |
| web/ts/api/admin-lecture-list.ts | Added API interface for custom thumbnail field and upload endpoint |
| web/template/partial/course/manage/lecture-management-card.gohtml | Added UI for custom thumbnail toggle and drag-and-drop upload area |
| model/stream.go | Added CustomThumbnailEnabled field to Stream model |
| model/file.go | Added FILETYPE_THUMB_CUSTOM constant and CourseName field to File model |
| dao/file.go | Added GetThumbnail method to retrieve thumbnails by type |
| api/stream.go | Implemented putCustomLiveThumbnail endpoint and updated getLiveThumbs to use custom thumbnails |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this.changeSet.commit({ discardKeys: this.videoFiles.map((v) => v.info.key) }); | ||
| this.uiEditMode = UIEditMode.none; | ||
| }, | ||
| previewThumbnail(e){ |
There was a problem hiding this comment.
The parameter 'e' is ambiguous. Consider renaming it to 'event' or 'dropEvent' to clearly indicate it's a drop event.
| const item = e.dataTransfer.items[0]; | ||
| const { kind } = item; | ||
| switch (kind) { | ||
| case "file": { | ||
| DataStore.adminLectureList.uploadThumbnail( | ||
| this.lectureData.courseId, | ||
| this.lectureData.lectureId, | ||
| item.getAsFile(), | ||
| ); | ||
| break; | ||
| } | ||
|
|
||
| } | ||
| this.previewThumbnail(e); | ||
| }}, |
There was a problem hiding this comment.
Missing indentation on line 398. The 'const item' declaration should be indented consistently with the if statement block.
| const item = e.dataTransfer.items[0]; | |
| const { kind } = item; | |
| switch (kind) { | |
| case "file": { | |
| DataStore.adminLectureList.uploadThumbnail( | |
| this.lectureData.courseId, | |
| this.lectureData.lectureId, | |
| item.getAsFile(), | |
| ); | |
| break; | |
| } | |
| } | |
| this.previewThumbnail(e); | |
| }}, | |
| const item = e.dataTransfer.items[0]; | |
| const { kind } = item; | |
| switch (kind) { | |
| case "file": { | |
| DataStore.adminLectureList.uploadThumbnail( | |
| this.lectureData.courseId, | |
| this.lectureData.lectureId, | |
| item.getAsFile(), | |
| ); | |
| break; | |
| } | |
| } | |
| this.previewThumbnail(e); | |
| }}, |
|
|
||
| const res = await AdminLectureList.uploadThumbnailFile(courseId, lectureId, file); | ||
| const newFile = new LectureFile({ | ||
| id: JSON.parse(res.responseText), | ||
| fileType: 2, | ||
| friendlyName: file.name, | ||
| }); | ||
|
|
||
| this.data[courseId] = (await this.getData(courseId)).map((s) => { | ||
| if (s.lectureId === lectureId) { | ||
| return { | ||
| ...s, | ||
| files: [...s.files, newFile], | ||
| }; | ||
| } | ||
| return s; | ||
| }); | ||
| await this.triggerUpdate(courseId); | ||
|
|
There was a problem hiding this comment.
Inconsistent indentation. The function body should start at the standard indentation level without the extra leading spaces.
| const res = await AdminLectureList.uploadThumbnailFile(courseId, lectureId, file); | |
| const newFile = new LectureFile({ | |
| id: JSON.parse(res.responseText), | |
| fileType: 2, | |
| friendlyName: file.name, | |
| }); | |
| this.data[courseId] = (await this.getData(courseId)).map((s) => { | |
| if (s.lectureId === lectureId) { | |
| return { | |
| ...s, | |
| files: [...s.files, newFile], | |
| }; | |
| } | |
| return s; | |
| }); | |
| await this.triggerUpdate(courseId); | |
| const res = await AdminLectureList.uploadThumbnailFile(courseId, lectureId, file); | |
| const newFile = new LectureFile({ | |
| id: JSON.parse(res.responseText), | |
| fileType: 2, | |
| friendlyName: file.name, | |
| }); | |
| this.data[courseId] = (await this.getData(courseId)).map((s) => { | |
| if (s.lectureId === lectureId) { | |
| return { | |
| ...s, | |
| files: [...s.files, newFile], | |
| }; | |
| } | |
| return s; | |
| }); | |
| await this.triggerUpdate(courseId); |
| uploadThumbnailFile: async ( | ||
| courseId: number, | ||
| lectureId: number, | ||
| file: File, | ||
| listener: PostFormDataListener = {}, | ||
| ) => { | ||
| return await uploadFile(`/api/stream/${lectureId}/thumbs/`, file, listener); | ||
|
|
||
| }, |
There was a problem hiding this comment.
The courseId parameter is unused in this function. Either remove it from the parameter list or use it in the API endpoint URL if it's required for the backend route.
| if err != nil { | ||
|
|
||
| path := pathprovider.LiveThumbnail(streamId) | ||
| c.File(path) |
There was a problem hiding this comment.
Control flow issue: when err is not nil, the function serves the default thumbnail but then continues to line 191 and attempts to serve file.Path, which may be invalid. Add a return statement after c.File(path) on line 189.
| c.File(path) | |
| c.File(path) | |
| return |
| StreamID: streamID, | ||
| Path: path, | ||
| Filename: file.Filename, | ||
| Type: model.FILETYPE_THUMB_LG_CAM_PRES, |
There was a problem hiding this comment.
Incorrect file type used for custom thumbnail. This should be model.FILETYPE_THUMB_CUSTOM instead of model.FILETYPE_THUMB_LG_CAM_PRES to properly identify custom uploaded thumbnails.
| Type: model.FILETYPE_THUMB_LG_CAM_PRES, | |
| Type: model.FILETYPE_THUMB_CUSTOM, |
| {{/* </label>*/}} | ||
| </section> | ||
| {{/* <article x-data="{ id: $id('text-input') }"*/}} | ||
| {{/* class="w-full" >*/}} | ||
|
|
||
| {{/* </article>*/}} | ||
|
|
||
| <!--input type="file" id="thumbnailUpload" class="hidden" accept="image/*" @change="onAttachmentFileDrop"--> | ||
|
|
||
|
|
||
|
|
There was a problem hiding this comment.
Remove commented-out code. Lines 407, 409-412, and 414 contain unused commented code that should be deleted to improve code clarity.
| {{/* </label>*/}} | |
| </section> | |
| {{/* <article x-data="{ id: $id('text-input') }"*/}} | |
| {{/* class="w-full" >*/}} | |
| {{/* </article>*/}} | |
| <!--input type="file" id="thumbnailUpload" class="hidden" accept="image/*" @change="onAttachmentFileDrop"--> | |
| </section> |
|
Supersedes #1479 |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Motivation and Context
Continuing
Description
Steps for Testing
Prerequisites:
Screenshots